vendor: 13,564 bytes, lines 1-383
1/** 2 * vivus - JavaScript library to make drawing animation on SVG 3 * @version v0.4.2 4 * @link https://github.com/maxwellito/vivus 5 * @license MIT 6 */ 7 8'use strict'; 9 10(function () { 11 12 'use strict'; 13 14 /** 15 * Pathformer 16 * Beta version 17 * 18 * Take any SVG version 1.1 and transform 19 * child elements to 'path' elements 20 * 21 * This code is purely forked from 22 * https://github.com/Waest/SVGPathConverter 23 */ 24 25 /** 26 * Class constructor 27 * 28 * @param {DOM|String} element Dom element of the SVG or id of it 29 */ 30 function Pathformer(element) { 31 // Test params 32 if (typeof element === 'undefined') { 33 throw new Error('Pathformer [constructor]: "element" parameter is required'); 34 } 35 36 // Set the element 37 if (element.constructor === String) { 38 element = document.getElementById(element); 39 if (!element) { 40 throw new Error('Pathformer [constructor]: "element" parameter is not related to an existing ID'); 41 } 42 } 43 if (element instanceof window.SVGElement || 44 element instanceof window.SVGGElement || 45 /^svg$/i.test(element.nodeName)) { 46 this.el = element; 47 } else { 48 throw new Error('Pathformer [constructor]: "element" parameter must be a string or a SVGelement'); 49 } 50 51 // Start 52 this.scan(element); 53 } 54 55 /** 56 * List of tags which can be transformed 57 * to path elements 58 * 59 * @type {Array} 60 */ 61 Pathformer.prototype.TYPES = ['line', 'ellipse', 'circle', 'polygon', 'polyline', 'rect']; 62 63 /** 64 * List of attribute names which contain 65 * data. This array list them to check if 66 * they contain bad values, like percentage. 67 * 68 * @type {Array} 69 */ 70 Pathformer.prototype.ATTR_WATCH = ['cx', 'cy', 'points', 'r', 'rx', 'ry', 'x', 'x1', 'x2', 'y', 'y1', 'y2']; 71 72 /** 73 * Finds the elements compatible for transform 74 * and apply the liked method 75 * 76 * @param {object} options Object from the constructor 77 */ 78 Pathformer.prototype.scan = function (svg) { 79 var fn, element, pathData, pathDom, 80 elements = svg.querySelectorAll(this.TYPES.join(',')); 81 82 for (var i = 0; i < elements.length; i++) { 83 element = elements[i]; 84 fn = this[element.tagName.toLowerCase() + 'ToPath']; 85 pathData = fn(this.parseAttr(element.attributes)); 86 pathDom = this.pathMaker(element, pathData); 87 element.parentNode.replaceChild(pathDom, element); 88 } 89 }; 90 91 92 /** 93 * Read `line` element to extract and transform 94 * data, to make it ready for a `path` object. 95 * 96 * @param {DOMelement} element Line element to transform 97 * @return {object} Data for a `path` element 98 */ 99 Pathformer.prototype.lineToPath = function (element) { 100 var newElement = {}, 101 x1 = element.x1 || 0, 102 y1 = element.y1 || 0, 103 x2 = element.x2 || 0, 104 y2 = element.y2 || 0; 105 106 newElement.d = 'M' + x1 + ',' + y1 + 'L' + x2 + ',' + y2; 107 return newElement; 108 }; 109 110 /** 111 * Read `rect` element to extract and transform 112 * data, to make it ready for a `path` object. 113 * The radius-border is not taken in charge yet. 114 * (your help is more than welcomed) 115 * 116 * @param {DOMelement} element Rect element to transform 117 * @return {object} Data for a `path` element 118 */ 119 Pathformer.prototype.rectToPath = function (element) { 120 var newElement = {}, 121 x = parseFloat(element.x) || 0, 122 y = parseFloat(element.y) || 0, 123 width = parseFloat(element.width) || 0, 124 height = parseFloat(element.height) || 0; 125 126 if (element.rx || element.ry) { 127 var rx = parseInt(element.rx, 10) || -1, 128 ry = parseInt(element.ry, 10) || -1; 129 rx = Math.min(Math.max(rx < 0 ? ry : rx, 0), width/2); 130 ry = Math.min(Math.max(ry < 0 ? rx : ry, 0), height/2); 131 132 newElement.d = 'M ' + (x + rx) + ',' + y + ' ' + 133 'L ' + (x + width - rx) + ',' + y + ' ' + 134 'A ' + rx + ',' + ry + ',0,0,1,' + (x + width) + ',' + (y + ry) + ' ' + 135 'L ' + (x + width) + ',' + (y + height - ry) + ' ' + 136 'A ' + rx + ',' + ry + ',0,0,1,' + (x + width - rx) + ',' + (y + height) + ' ' + 137 'L ' + (x + rx) + ',' + (y + height) + ' ' + 138 'A ' + rx + ',' + ry + ',0,0,1,' + x + ',' + (y + height - ry) + ' ' + 139 'L ' + x + ',' + (y + ry) + ' ' + 140 'A ' + rx + ',' + ry + ',0,0,1,' + (x + rx) + ',' + y; 141 } 142 else { 143 newElement.d = 'M' + x + ' ' + y + ' ' + 144 'L' + (x + width) + ' ' + y + ' ' + 145 'L' + (x + width) + ' ' + (y + height) + ' ' + 146 'L' + x + ' ' + (y + height) + ' Z'; 147 } 148 return newElement; 149 }; 150 151 /** 152 * Read `polyline` element to extract and transform 153 * data, to make it ready for a `path` object. 154 * 155 * @param {DOMelement} element Polyline element to transform 156 * @return {object} Data for a `path` element 157 */ 158 Pathformer.prototype.polylineToPath = function (element) { 159 var newElement = {}, 160 points = element.points.trim().split(' '), 161 i, path; 162 163 // Reformatting if points are defined without commas 164 if (element.points.indexOf(',') === -1) { 165 var formattedPoints = []; 166 for (i = 0; i < points.length; i+=2) { 167 formattedPoints.push(points[i] + ',' + points[i+1]); 168 } 169 points = formattedPoints; 170 } 171 172 // Generate the path.d value 173 path = 'M' + points[0]; 174 for(i = 1; i < points.length; i++) { 175 if (points[i].indexOf(',') !== -1) { 176 path += 'L' + points[i]; 177 } 178 } 179 newElement.d = path; 180 return newElement; 181 }; 182 183 /** 184 * Read `polygon` element to extract and transform 185 * data, to make it ready for a `path` object. 186 * This method rely on polylineToPath, because the 187 * logic is similar. The path created is just closed, 188 * so it needs an 'Z' at the end. 189 * 190 * @param {DOMelement} element Polygon element to transform 191 * @return {object} Data for a `path` element 192 */ 193 Pathformer.prototype.polygonToPath = function (element) { 194 var newElement = Pathformer.prototype.polylineToPath(element); 195 196 newElement.d += 'Z'; 197 return newElement; 198 }; 199 200 /** 201 * Read `ellipse` element to extract and transform 202 * data, to make it ready for a `path` object. 203 * 204 * @param {DOMelement} element ellipse element to transform 205 * @return {object} Data for a `path` element 206 */ 207 Pathformer.prototype.ellipseToPath = function (element) { 208 var newElement = {}, 209 rx = parseFloat(element.rx) || 0, 210 ry = parseFloat(element.ry) || 0, 211 cx = parseFloat(element.cx) || 0, 212 cy = parseFloat(element.cy) || 0, 213 startX = cx - rx, 214 startY = cy, 215 endX = parseFloat(cx) + parseFloat(rx), 216 endY = cy; 217 218 newElement.d = 'M' + startX + ',' + startY + 219 'A' + rx + ',' + ry + ' 0,1,1 ' + endX + ',' + endY + 220 'A' + rx + ',' + ry + ' 0,1,1 ' + startX + ',' + endY; 221 return newElement; 222 }; 223 224 /** 225 * Read `circle` element to extract and transform 226 * data, to make it ready for a `path` object. 227 * 228 * @param {DOMelement} element Circle element to transform 229 * @return {object} Data for a `path` element 230 */ 231 Pathformer.prototype.circleToPath = function (element) { 232 var newElement = {}, 233 r = parseFloat(element.r) || 0, 234 cx = parseFloat(element.cx) || 0, 235 cy = parseFloat(element.cy) || 0, 236 startX = cx - r, 237 startY = cy, 238 endX = parseFloat(cx) + parseFloat(r), 239 endY = cy; 240 241 newElement.d = 'M' + startX + ',' + startY + 242 'A' + r + ',' + r + ' 0,1,1 ' + endX + ',' + endY + 243 'A' + r + ',' + r + ' 0,1,1 ' + startX + ',' + endY; 244 return newElement; 245 }; 246 247 /** 248 * Create `path` elements form original element 249 * and prepared objects 250 * 251 * @param {DOMelement} element Original element to transform 252 * @param {object} pathData Path data (from `toPath` methods) 253 * @return {DOMelement} Path element 254 */ 255 Pathformer.prototype.pathMaker = function (element, pathData) { 256 var i, attr, pathTag = document.createElementNS('http://www.w3.org/2000/svg','path'); 257 for(i = 0; i < element.attributes.length; i++) { 258 attr = element.attributes[i]; 259 if (this.ATTR_WATCH.indexOf(attr.name) === -1) { 260 pathTag.setAttribute(attr.name, attr.value); 261 } 262 } 263 for(i in pathData) { 264 pathTag.setAttribute(i, pathData[i]); 265 } 266 return pathTag; 267 }; 268 269 /** 270 * Parse attributes of a DOM element to 271 * get an object of attribute => value 272 * 273 * @param {NamedNodeMap} attributes Attributes object from DOM element to parse 274 * @return {object} Object of attributes 275 */ 276 Pathformer.prototype.parseAttr = function (element) { 277 var attr, output = {}; 278 for (var i = 0; i < element.length; i++) { 279 attr = element[i]; 280 // Check if no data attribute contains '%', or the transformation is impossible 281 if (this.ATTR_WATCH.indexOf(attr.name) !== -1 && attr.value.indexOf('%') !== -1) { 282 throw new Error('Pathformer [parseAttr]: a SVG shape got values in percentage. This cannot be transformed into \'path\' tags. Please use \'viewBox\'.'); 283 } 284 output[attr.name] = attr.value; 285 } 286 return output; 287 }; 288 289 290 var setupEnv, requestAnimFrame, cancelAnimFrame, parsePositiveInt; 291 292 /** 293 * Vivus 294 * Beta version 295 * 296 * Take any SVG and make the animation 297 * to give give the impression of live drawing 298 * 299 * This in more than just inspired from codrops 300 * At that point, it's a pure fork. 301 */ 302 303 /** 304 * Class constructor 305 * option structure 306 * type: 'delayed'|'sync'|'oneByOne'|'script' (to know if the items must be drawn synchronously or not, default: delayed) 307 * duration: <int> (in frames) 308 * start: 'inViewport'|'manual'|'autostart' (start automatically the animation, default: inViewport) 309 * delay: <int> (delay between the drawing of first and last path) 310 * dashGap <integer> whitespace extra margin between dashes 311 * pathTimingFunction <function> timing animation function for each path element of the SVG 312 * animTimingFunction <function> timing animation function for the complete SVG 313 * forceRender <boolean> force the browser to re-render all updated path items 314 * selfDestroy <boolean> removes all extra styling on the SVG, and leaves it as original 315 * 316 * The attribute 'type' is by default on 'delayed'. 317 * - 'delayed' 318 * all paths are draw at the same time but with a 319 * little delay between them before start 320 * - 'sync' 321 * all path are start and finish at the same time 322 * - 'oneByOne' 323 * only one path is draw at the time 324 * the end of the first one will trigger the draw 325 * of the next one 326 * 327 * All these values can be overwritten individually 328 * for each path item in the SVG 329 * The value of frames will always take the advantage of 330 * the duration value. 331 * If you fail somewhere, an error will be thrown. 332 * Good luck. 333 * 334 * @constructor 335 * @this {Vivus} 336 * @param {DOM|String} element Dom element of the SVG or id of it 337 * @param {Object} options Options about the animation 338 * @param {Function} callback Callback for the end of the animation 339 */ 340 function Vivus (element, options, callback) { 341 342 setupEnv(); 343 344 // Setup 345 this.isReady = false; 346 this.setElement(element, options); 347 this.setOptions(options); 348 this.setCallback(callback); 349 350 if (this.isReady) { 351 this.init(); 352 } 353 } 354 355 /** 356 * Timing functions 357 ************************************** 358 * 359 * Default functions to help developers. 360 * It always take a number as parameter (between 0 to 1) then 361 * return a number (between 0 and 1) 362 */ 363 Vivus.LINEAR = function (x) {return x;}; 364 Vivus.EASE = function (x) {return -Math.cos(x * Math.PI) / 2 + 0.5;}; 365 Vivus.EASE_OUT = function (x) {return 1 - Math.pow(1-x, 3);}; 366 Vivus.EASE_IN = function (x) {return Math.pow(x, 3);}; 367 Vivus.EASE_OUT_BOUNCE = function (x) { 368 var base = -Math.cos(x * (0.5 * Math.PI)) + 1, 369 rate = Math.pow(base,1.5), 370 rateR = Math.pow(1 - x, 2), 371 progress = -Math.abs(Math.cos(rate * (2.5 * Math.PI) )) + 1; 372 return (1- rateR) + (progress * rateR); 373 }; 374 375 376 /** 377 * Setters 378 ************************************** 379 */ 380 381 /** 382 * Check and set the element in the instance 383 * The method will not return anything, but will throw an
384 * error if the parameter is invalid 385 * 386 * @param {DOM|String} element SVG Dom element or id of it 387 */ 388 Vivus.prototype.setElement = function (element, options) { 389 // Basic check 390 if (typeof element === 'undefined') { 391 throw new Error('Vivus [constructor]: "element" parameter is required'); 392 } 393 394 // Set the element 395 if (element.constructor === String) { 396 element = document.getElementById(element); 397 if (!element) { 398 throw new Error('Vivus [constructor]: "element" parameter is not related to an existing ID'); 399 } 400 } 401 this.parentEl = element; 402 403 // Create the object element if the property `file` exists in the options object 404 if (options && options.file) { 405 var objElm = document.createElement('object'); 406 objElm.setAttribute('type', 'image/svg+xml'); 407 objElm.setAttribute('data', options.file); 408 objElm.setAttribute('built-by-vivus', 'true'); 409 element.appendChild(objElm); 410 element = objElm; 411 } 412 413 switch (element.constructor) { 414 case window.SVGSVGElement: 415 case window.SVGElement: 416 case window.SVGGElement: 417 this.el = element; 418 this.isReady = true; 419 break; 420 421 case window.HTMLObjectElement: 422 // If we have to wait for it 423 var onLoad, self; 424 425 self = this; 426 onLoad = function (e) { 427 if (self.isReady) { 428 return; 429 } 430 self.el = element.contentDocument && element.contentDocument.querySelector('svg'); 431 if (!self.el && e) { 432 throw new Error('Vivus [constructor]: object loaded does not contain any SVG'); 433 } 434 else if (self.el) { 435 if (element.getAttribute('built-by-vivus')) { 436 self.parentEl.insertBefore(self.el, element); 437 self.parentEl.removeChild(element); 438 self.el.setAttribute('width', '100%'); 439 self.el.setAttribute('height', '100%'); 440 } 441 self.isReady = true; 442 self.init(); 443 return true; 444 } 445 }; 446 447 if (!onLoad()) { 448 element.addEventListener('load', onLoad); 449 } 450 break; 451 452 default: 453 throw new Error('Vivus [constructor]: "element" parameter is not valid (or miss the "file" attribute)'); 454 } 455 }; 456 457 /** 458 * Set up user option to the instance 459 * The method will not return anything, but will throw an 460 * error if the parameter is invalid 461 * 462 * @param {object} options Object from the constructor 463 */ 464 Vivus.prototype.setOptions = function (options) { 465 var allowedTypes = ['delayed', 'sync', 'async', 'nsync', 'oneByOne', 'scenario', 'scenario-sync']; 466 var allowedStarts = ['inViewport', 'manual', 'autostart']; 467 468 // Basic check 469 if (options !== undefined && options.constructor !== Object) { 470 throw new Error('Vivus [constructor]: "options" parameter must be an object'); 471 } 472 else { 473 options = options || {}; 474 } 475 476 // Set the animation type 477 if (options.type && allowedTypes.indexOf(options.type) === -1) { 478 throw new Error('Vivus [constructor]: ' + options.type + ' is not an existing animation `type`'); 479 } 480 else { 481 this.type = options.type || allowedTypes[0]; 482 } 483 484 // Set the start type 485 if (options.start && allowedStarts.indexOf(options.start) === -1) { 486 throw new Error('Vivus [constructor]: ' + options.start + ' is not an existing `start` option'); 487 } 488 else { 489 this.start = options.start || allowedStarts[0]; 490 } 491 492 this.isIE = (window.navigator.userAgent.indexOf('MSIE') !== -1 || window.navigator.userAgent.indexOf('Trident/') !== -1 || window.navigator.userAgent.indexOf('Edge/') !== -1 ); 493 this.duration = parsePositiveInt(options.duration, 120); 494 this.delay = parsePositiveInt(options.delay, null); 495 this.dashGap = parsePositiveInt(options.dashGap, 1); 496 this.forceRender = options.hasOwnProperty('forceRender') ? !!options.forceRender : this.isIE; 497 this.reverseStack = !!options.reverseStack;
498 this.selfDestroy = !!options.selfDestroy; 499 this.onReady = options.onReady; 500 this.map = []; 501 this.frameLength = this.currentFrame = this.delayUnit = this.speed = this.handle = null; 502 503 this.ignoreInvisible = options.hasOwnProperty('ignoreInvisible') ? !!options.ignoreInvisible : false; 504 505 this.animTimingFunction = options.animTimingFunction || Vivus.LINEAR; 506 this.pathTimingFunction = options.pathTimingFunction || Vivus.LINEAR; 507 508 if (this.delay >= this.duration) { 509 throw new Error('Vivus [constructor]: delay must be shorter than duration'); 510 } 511 }; 512 513 /** 514 * Set up callback to the instance 515 * The method will not return enything, but will throw an 516 * error if the parameter is invalid 517 * 518 * @param {Function} callback Callback for the animation end 519 */ 520 Vivus.prototype.setCallback = function (callback) { 521 // Basic check 522 if (!!callback && callback.constructor !== Function) { 523 throw new Error('Vivus [constructor]: "callback" parameter must be a function'); 524 } 525 this.callback = callback || function () {}; 526 }; 527 528 529 /** 530 * Core 531 ************************************** 532 */ 533 534 /** 535 * Map the svg, path by path. 536 * The method return nothing, it just fill the 537 * `map` array. Each item in this array represent 538 * a path element from the SVG, with informations for 539 * the animation. 540 * 541 * ``` 542 * [ 543 * { 544 * el: <DOMobj> the path element 545 * length: <number> length of the path line 546 * startAt: <number> time start of the path animation (in frames) 547 * duration: <number> path animation duration (in frames) 548 * }, 549 * ... 550 * ] 551 * ``` 552 * 553 */ 554 Vivus.prototype.mapping = function () { 555 var i, paths, path, pAttrs, pathObj, totalLength, lengthMeter, timePoint; 556 timePoint = totalLength = lengthMeter = 0; 557 paths = this.el.querySelectorAll('path'); 558 559 for (i = 0; i < paths.length; i++) { 560 path = paths[i]; 561 if (this.isInvisible(path)) { 562 continue; 563 } 564 pathObj = { 565 el: path, 566 length: Math.ceil(path.getTotalLength()) 567 }; 568 // Test if the path length is correct 569 if (isNaN(pathObj.length)) { 570 if (window.console && console.warn) { 571 console.warn('Vivus [mapping]: cannot retrieve a path element length', path); 572 } 573 continue; 574 } 575 this.map.push(pathObj); 576 path.style.strokeDasharray = pathObj.length + ' ' + (pathObj.length + this.dashGap * 2); 577 path.style.strokeDashoffset = pathObj.length + this.dashGap; 578 pathObj.length += this.dashGap; 579 totalLength += pathObj.length; 580 581 this.renderPath(i); 582 } 583 584 totalLength = totalLength === 0 ? 1 : totalLength; 585 this.delay = this.delay === null ? this.duration / 3 : this.delay; 586 this.delayUnit = this.delay / (paths.length > 1 ? paths.length - 1 : 1); 587 588 // Reverse stack if asked 589 if (this.reverseStack) { 590 this.map.reverse(); 591 } 592 593 for (i = 0; i < this.map.length; i++) { 594 pathObj = this.map[i]; 595 596 switch (this.type) { 597 case 'delayed': 598 pathObj.startAt = this.delayUnit * i; 599 pathObj.duration = this.duration - this.delay; 600 break; 601 602 case 'oneByOne': 603 pathObj.startAt = lengthMeter / totalLength * this.duration; 604 pathObj.duration = pathObj.length / totalLength * this.duration; 605 break; 606 607 case 'sync': 608 case 'async': 609 case 'nsync': 610 pathObj.startAt = 0; 611 pathObj.duration = this.duration; 612 break; 613 614 case 'scenario-sync': 615 path = pathObj.el; 616 pAttrs = this.parseAttr(path); 617 pathObj.startAt = timePoint + (parsePositiveInt(pAttrs['data-delay'], this.delayUnit) || 0); 618 pathObj.duration = parsePositiveInt(pAttrs['data-duration'], this.duration); 619 timePoint = pAttrs['data-async'] !== undefined ? pathObj.startAt : pathObj.startAt + pathObj.duration; 620 this.frameLength = Math.max(this.frameLength, (pathObj.startAt + pathObj.duration)); 621 break; 622 623 case 'scenario': 624 path = pathObj.el; 625 pAttrs = this.parseAttr(path); 626 pathObj.startAt = parsePositiveInt(pAttrs['data-start'], this.delayUnit) || 0; 627 pathObj.duration = parsePositiveInt(pAttrs['data-duration'], this.duration); 628 this.frameLength = Math.max(this.frameLength, (pathObj.startAt + pathObj.duration)); 629 break; 630 } 631 lengthMeter += pathObj.length; 632 this.frameLength = this.frameLength || this.duration; 633 } 634 }; 635 636 /** 637 * Interval method to draw the SVG from current 638 * position of the animation. It update the value of 639 * `currentFrame` and re-trace the SVG. 640 * 641 * It use this.handle to store the requestAnimationFrame 642 * and clear it one the animation is stopped. So this 643 * attribute can be used to know if the animation is 644 * playing. 645 * 646 * Once the animation at the end, this method will 647 * trigger the Vivus callback. 648 * 649 */ 650 Vivus.prototype.drawer = function () { 651 var self = this; 652 this.currentFrame += this.speed; 653 654 if (this.currentFrame <= 0) { 655 this.stop(); 656 this.reset(); 657 } else if (this.currentFrame >= this.frameLength) { 658 this.stop(); 659 this.currentFrame = this.frameLength; 660 this.trace();
vendor: 6,282 bytes, lines 661-853
661 if (this.selfDestroy) { 662 this.destroy(); 663 } 664 } else { 665 this.trace(); 666 this.handle = requestAnimFrame(function () { 667 self.drawer(); 668 }); 669 return; 670 } 671 672 this.callback(this); 673 if (this.instanceCallback) { 674 this.instanceCallback(this); 675 this.instanceCallback = null; 676 } 677 }; 678 679 /** 680 * Draw the SVG at the current instant from the 681 * `currentFrame` value. Here is where most of the magic is. 682 * The trick is to use the `strokeDashoffset` style property. 683 * 684 * For optimisation reasons, a new property called `progress` 685 * is added in each item of `map`. This one contain the current 686 * progress of the path element. Only if the new value is different 687 * the new value will be applied to the DOM element. This 688 * method save a lot of resources to re-render the SVG. And could 689 * be improved if the animation couldn't be played forward. 690 * 691 */ 692 Vivus.prototype.trace = function () { 693 var i, progress, path, currentFrame; 694 currentFrame = this.animTimingFunction(this.currentFrame / this.frameLength) * this.frameLength; 695 for (i = 0; i < this.map.length; i++) { 696 path = this.map[i]; 697 progress = (currentFrame - path.startAt) / path.duration; 698 progress = this.pathTimingFunction(Math.max(0, Math.min(1, progress))); 699 if (path.progress !== progress) { 700 path.progress = progress; 701 path.el.style.strokeDashoffset = Math.floor(path.length * (1 - progress)); 702 this.renderPath(i); 703 } 704 } 705 }; 706 707 /** 708 * Method forcing the browser to re-render a path element 709 * from it's index in the map. Depending on the `forceRender` 710 * value. 711 * The trick is to replace the path element by it's clone. 712 * This practice is not recommended because it's asking more 713 * ressources, too much DOM manupulation.. 714 * but it's the only way to let the magic happen on IE. 715 * By default, this fallback is only applied on IE. 716 * 717 * @param {Number} index Path index 718 */ 719 Vivus.prototype.renderPath = function (index) { 720 if (this.forceRender && this.map && this.map[index]) { 721 var pathObj = this.map[index], 722 newPath = pathObj.el.cloneNode(true); 723 pathObj.el.parentNode.replaceChild(newPath, pathObj.el); 724 pathObj.el = newPath; 725 } 726 }; 727 728 /** 729 * When the SVG object is loaded and ready, 730 * this method will continue the initialisation. 731 * 732 * This this mainly due to the case of passing an 733 * object tag in the constructor. It will wait 734 * the end of the loading to initialise. 735 * 736 */ 737 Vivus.prototype.init = function () { 738 // Set object variables 739 this.frameLength = 0; 740 this.currentFrame = 0; 741 this.map = []; 742 743 // Start 744 new Pathformer(this.el); 745 this.mapping(); 746 this.starter(); 747 748 if (this.onReady) { 749 this.onReady(this); 750 } 751 }; 752 753 /** 754 * Trigger to start of the animation. 755 * Depending on the `start` value, a different script 756 * will be applied. 757 * 758 * If the `start` value is not valid, an error will be thrown. 759 * Even if technically, this is impossible. 760 * 761 */ 762 Vivus.prototype.starter = function () { 763 switch (this.start) { 764 case 'manual': 765 return; 766 767 case 'autostart': 768 this.play(); 769 break; 770 771 case 'inViewport': 772 var self = this, 773 listener = function () { 774 if (self.isInViewport(self.parentEl, 1)) { 775 self.play(); 776 window.removeEventListener('scroll', listener); 777 } 778 }; 779 window.addEventListener('scroll', listener); 780 listener(); 781 break; 782 } 783 }; 784 785 786 /** 787 * Controls 788 ************************************** 789 */ 790 791 /** 792 * Get the current status of the animation between 793 * three different states: 'start', 'progress', 'end'. 794 * @return {string} Instance status 795 */ 796 Vivus.prototype.getStatus = function () { 797 return this.currentFrame === 0 ? 'start' : this.currentFrame === this.frameLength ? 'end' : 'progress'; 798 }; 799 800 /** 801 * Reset the instance to the initial state : undraw 802 * Be careful, it just reset the animation, if you're 803 * playing the animation, this won't stop it. But just 804 * make it start from start. 805 * 806 */ 807 Vivus.prototype.reset = function () { 808 return this.setFrameProgress(0); 809 }; 810 811 /** 812 * Set the instance to the final state : drawn 813 * Be careful, it just set the animation, if you're 814 * playing the animation on rewind, this won't stop it. 815 * But just make it start from the end. 816 * 817 */ 818 Vivus.prototype.finish = function () { 819 return this.setFrameProgress(1); 820 }; 821 822 /** 823 * Set the level of progress of the drawing. 824 * 825 * @param {number} progress Level of progress to set 826 */ 827 Vivus.prototype.setFrameProgress = function (progress) { 828 progress = Math.min(1, Math.max(0, progress)); 829 this.currentFrame = Math.round(this.frameLength * progress); 830 this.trace(); 831 return this; 832 }; 833 834 /** 835 * Play the animation at the desired speed. 836 * Speed must be a valid number (no zero). 837 * By default, the speed value is 1. 838 * But a negative value is accepted to go forward. 839 * 840 * And works with float too. 841 * But don't forget we are in JavaScript, se be nice 842 * with him and give him a 1/2^x value. 843 * 844 * @param {number} speed Animation speed [optional] 845 */ 846 Vivus.prototype.play = function (speed, callback) { 847 this.instanceCallback = null; 848 849 if (speed && typeof speed === 'function') { 850 this.instanceCallback = speed; // first parameter is actually the callback function 851 speed = null; 852 } 853 else if (speed && typeof speed !== 'number') {
854 throw new Error('Vivus [play]: invalid speed'); 855 } 856 // if the first parameter wasn't the callback, check if the seconds was 857 if (callback && typeof(callback) === 'function' && !this.instanceCallback) { 858 this.instanceCallback = callback; 859 } 860 861 862 this.speed = speed || 1; 863 if (!this.handle) { 864 this.drawer(); 865 } 866 return this; 867 }; 868 869 /** 870 * Stop the current animation, if on progress. 871 * Should not trigger any error. 872 * 873 */ 874 Vivus.prototype.stop = function () { 875 if (this.handle) { 876 cancelAnimFrame(this.handle); 877 this.handle = null; 878 } 879 return this; 880 }; 881 882 /** 883 * Destroy the instance. 884 * Remove all bad styling attributes on all 885 * path tags 886 * 887 */ 888 Vivus.prototype.destroy = function () { 889 this.stop(); 890 var i, path; 891 for (i = 0; i < this.map.length; i++) { 892 path = this.map[i]; 893 path.el.style.strokeDashoffset = null; 894 path.el.style.strokeDasharray = null; 895 this.renderPath(i); 896 } 897 }; 898 899 900 /** 901 * Utils methods 902 * include methods from Codrops 903 ************************************** 904 */ 905 906 /** 907 * Method to best guess if a path should added into 908 * the animation or not. 909 * 910 * 1. Use the `data-vivus-ignore` attribute if set 911 * 2. Check if the instance must ignore invisible paths 912 * 3. Check if the path is visible 913 * 914 * For now the visibility checking is unstable. 915 * It will be used for a beta phase. 916 * 917 * Other improvments are planned. Like detecting 918 * is the path got a stroke or a valid opacity. 919 */ 920 Vivus.prototype.isInvisible = function (el) { 921 var rect, 922 ignoreAttr = el.getAttribute('data-ignore'); 923 924 if (ignoreAttr !== null) { 925 return ignoreAttr !== 'false'; 926 } 927 928 if (this.ignoreInvisible) { 929 rect = el.getBoundingClientRect(); 930 return !rect.width && !rect.height; 931 } 932 else { 933 return false; 934 } 935 }; 936 937 /** 938 * Parse attributes of a DOM element to 939 * get an object of {attributeName => attributeValue} 940 * 941 * @param {object} element DOM element to parse 942 * @return {object} Object of attributes 943 */ 944 Vivus.prototype.parseAttr = function (element) { 945 var attr, output = {}; 946 if (element && element.attributes) { 947 for (var i = 0; i < element.attributes.length; i++) { 948 attr = element.attributes[i]; 949 output[attr.name] = attr.value; 950 } 951 } 952 return output; 953 }; 954 955 /** 956 * Reply if an element is in the page viewport 957 * 958 * @param {object} el Element to observe 959 * @param {number} h Percentage of height 960 * @return {boolean} 961 */ 962 Vivus.prototype.isInViewport = function (el, h) { 963 var scrolled = this.scrollY(), 964 viewed = scrolled + this.getViewportH(), 965 elBCR = el.getBoundingClientRect(), 966 elHeight = elBCR.height, 967 elTop = scrolled + elBCR.top, 968 elBottom = elTop + elHeight; 969 970 // if 0, the element is considered in the viewport as soon as it enters. 971 // if 1, the element is considered in the viewport only when it's fully inside 972 // value in percentage (1 >= h >= 0) 973 h = h || 0; 974 975 return (elTop + elHeight * h) <= viewed && (elBottom) >= scrolled; 976 }; 977 978 979 /** 980 * Get the viewport height in pixels 981 * 982 * @return {integer} Viewport height 983 */ 984 Vivus.prototype.getViewportH = function () { 985 var client = this.docElem.clientHeight, 986 inner = window.innerHeight; 987 988 if (client < inner) { 989 return inner; 990 } 991 else { 992 return client; 993 } 994 }; 995 996 /** 997 * Get the page Y offset 998 * 999 * @return {integer} Page Y offset 1000 */ 1001 Vivus.prototype.scrollY = function () { 1002 return window.pageYOffset || this.docElem.scrollTop; 1003 }; 1004 1005 setupEnv = function () { 1006 1007 if (Vivus.prototype.docElem) { 1008 return; 1009 } 1010 1011 /** 1012 * Alias for document element 1013 * 1014 * @type {DOMelement} 1015 */ 1016 Vivus.prototype.docElem = window.document.documentElement; 1017 1018 /** 1019 * Alias for `requestAnimationFrame` or 1020 * `setTimeout` function for deprecated browsers. 1021 * 1022 */ 1023 requestAnimFrame = (function () { 1024 return ( 1025 window.requestAnimationFrame || 1026 window.webkitRequestAnimationFrame || 1027 window.mozRequestAnimationFrame || 1028 window.oRequestAnimationFrame || 1029 window.msRequestAnimationFrame || 1030 function(/* function */ callback){ 1031 return window.setTimeout(callback, 1000 / 60); 1032 } 1033 ); 1034 })(); 1035 1036 /** 1037 * Alias for `cancelAnimationFrame` or 1038 * `cancelTimeout` function for deprecated browsers. 1039 * 1040 */ 1041 cancelAnimFrame = (function () { 1042 return ( 1043 window.cancelAnimationFrame || 1044 window.webkitCancelAnimationFrame || 1045 window.mozCancelAnimationFrame || 1046 window.oCancelAnimationFrame || 1047 window.msCancelAnimationFrame || 1048 function(id){ 1049 return window.clearTimeout(id); 1050 } 1051 ); 1052 })(); 1053 }; 1054 1055 /** 1056 * Parse string to integer. 1057 * If the number is not positive or null 1058 * the method will return the default value 1059 * or 0 if undefined 1060 * 1061 * @param {string} value String to parse 1062 * @param {*}
1062 defaultValue Value to return if the result parsed is invalid 1063 * @return {number} 1064 * 1065 */ 1066 parsePositiveInt = function (value, defaultValue) { 1067 var output = parseInt(value, 10); 1068 return (output >= 0) ? output : defaultValue; 1069 }; 1070 1071 1072 if (typeof define === 'function' && define.amd) { 1073 // AMD. Register as an anonymous module. 1074 define([], function() { 1075 return Vivus; 1076 }); 1077 } else if (typeof exports === 'object') { 1078 // Node. Does not work with strict CommonJS, but 1079 // only CommonJS-like environments that support module.exports, 1080 // like Node. 1081 module.exports = Vivus; 1082 } else { 1083 // Browser globals 1084 window.Vivus = Vivus; 1085 } 1086 1087}());
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.