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