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