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