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];
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 val
415id (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 for (i = 0; i < paths.length; i++) { 519 path = paths[i]; 520 if (this.isInvisible(path)) { 521 continue; 522 } 523 pathObj = { 524 el: path, 525 length: Math.ceil(path.getTotalLength()) 526 }; 527 // Test if the path length is correct 528 if (isNaN(pathObj.length)) { 529 if (window.console && console.warn) { 530 console.warn('Vivus [mapping]: cannot retrieve a path element length', path); 531 } 532 continue; 533 } 534 this.map.push(pathObj); 535 path.style.strokeDasharray = pathObj.length + ' ' + (pathObj.length + this.dashGap * 2); 536 path.style.strokeDashoffset = pathObj.length + this.dashGap; 537 pathObj.length += this.dashGap; 538 totalLength += pathObj.length; 539 540 this.renderPath(i); 541 } 542 543 totalLength = totalLength === 0 ? 1 : totalLength; 544 this.delay = this.delay === null ? this.duration / 3 : this.delay; 545 this.delayUnit = this.delay / (paths.length > 1 ? paths.length - 1 : 1); 546 547 for (i = 0; i < this.map.length; i++) { 548 pathObj = this.map[i]; 549 550 switch (this.type) { 551 case 'delayed': 552 pathObj.startAt = this.delayUnit * i; 553 pathObj.duration = this.duration - this.delay; 554 break; 555 556 case 'oneByOne': 557 pathObj.startAt = lengthMeter / totalLength * this.duration; 558 pathObj.duration = pathObj.length / totalLength * this.duration; 559 break; 560 561 case 'async': 562 pathObj.startAt = 0; 563 pathObj.duration = this.duration; 564 break; 565 566 case 'scenario-sync': 567 path = pathObj.el; 568 pAttrs = this.parseAttr(path); 569 pathObj.startAt = timePoint + (parsePositiveInt(pAttrs['data-delay'], this.delayUnit) || 0); 570 pathObj.duration = parsePositiveInt(pAttrs['data-duration'], this.duration); 571 timePoint = pAttrs['data-async'] !== undefined ? pathObj.startAt : pathObj.startAt + pathObj.duration; 572 this.frameLength = Math.max(this.frameLength, (pathObj.startAt + pathObj.duration)); 573 break; 574 575 case 'scenario': 576 path = pathObj.el; 577 pAttrs = this.parseAttr(path); 578 pathObj.startAt = parsePositiveInt(pAttrs['data-start'], this.delayUnit) || 0; 579 pathObj.duration = parsePositiveInt(pAttrs['data-duration'], this.duration); 580 this.frameLength = Math.max(this.frameLength, (pathObj.startAt + pathObj.duration)); 581 break; 582 } 583 lengthMeter += pathObj.length; 584 this.frameLength = this.frameLength || this.duration; 585 } 586}; 587 588/** 589 * Interval method to draw the SVG from current 590 * position of the animation. It update the value of 591 * `currentFrame` and re-trace the SVG. 592 * 593 * It use this.handle to store the requestAnimationFrame 594 * and clear it one the animation is stopped. So this 595 * attribute can be used to know if the animation is 596 * playing. 597 * 598 * Once the animation at the end, this method will 599 * trigger the Vivus callback. 600 * 601 */ 602Vivus.prototype.drawer = function () { 603 var self = this; 604 this.currentFrame += this.speed; 605 606 if (this.currentFrame <= 0) { 607 this.stop(); 608 this.reset(); 609 this.callback(this); 610 } else if (this.currentFrame >= this.frameLength) { 611 this.stop(); 612 this.currentFrame = this.frameLength; 613 this.trace();
vendor: 4,659 bytes, lines 614-786
614 if (this.selfDestroy) { 615 this.destroy(); 616 } 617 this.callback(this); 618 } else { 619 this.trace(); 620 this.handle = requestAnimFrame(function () { 621 self.drawer(); 622 }); 623 } 624}; 625 626/** 627 * Draw the SVG at the current instant from the 628 * `currentFrame` value. Here is where most of the magic is. 629 * The trick is to use the `strokeDashoffset` style property. 630 * 631 * For optimisation reasons, a new property called `progress` 632 * is added in each item of `map`. This one contain the current 633 * progress of the path element. Only if the new value is different 634 * the new value will be applied to the DOM element. This 635 * method save a lot of resources to re-render the SVG. And could 636 * be improved if the animation couldn't be played forward. 637 * 638 */ 639Vivus.prototype.trace = function () { 640 var i, progress, path, currentFrame; 641 currentFrame = this.animTimingFunction(this.currentFrame / this.frameLength) * this.frameLength; 642 for (i = 0; i < this.map.length; i++) { 643 path = this.map[i]; 644 progress = (currentFrame - path.startAt) / path.duration; 645 progress = this.pathTimingFunction(Math.max(0, Math.min(1, progress))); 646 if (path.progress !== progress) { 647 path.progress = progress; 648 path.el.style.strokeDashoffset = Math.floor(path.length * (1 - progress)); 649 this.renderPath(i); 650 } 651 } 652}; 653 654/** 655 * Method forcing the browser to re-render a path element 656 * from it's index in the map. Depending on the `forceRender` 657 * value. 658 * The trick is to replace the path element by it's clone. 659 * This practice is not recommended because it's asking more 660 * ressources, too much DOM manupulation.. 661 * but it's the only way to let the magic happen on IE. 662 * By default, this fallback is only applied on IE. 663 * 664 * @param {Number} index Path index 665 */ 666Vivus.prototype.renderPath = function (index) { 667 if (this.forceRender && this.map && this.map[index]) { 668 var pathObj = this.map[index], 669 newPath = pathObj.el.cloneNode(true); 670 pathObj.el.parentNode.replaceChild(newPath, pathObj.el); 671 pathObj.el = newPath; 672 } 673}; 674 675/** 676 * When the SVG object is loaded and ready, 677 * this method will continue the initialisation. 678 * 679 * This this mainly due to the case of passing an 680 * object tag in the constructor. It will wait 681 * the end of the loading to initialise. 682 * 683 */ 684Vivus.prototype.init = function () { 685 // Set object variables 686 this.frameLength = 0; 687 this.currentFrame = 0; 688 this.map = []; 689 690 // Start 691 new Pathformer(this.el); 692 this.mapping(); 693 this.starter(); 694 695 if (this.onReady) { 696 this.onReady(this); 697 } 698}; 699 700/** 701 * Trigger to start of the animation. 702 * Depending on the `start` value, a different script 703 * will be applied. 704 * 705 * If the `start` value is not valid, an error will be thrown. 706 * Even if technically, this is impossible. 707 * 708 */ 709Vivus.prototype.starter = function () { 710 switch (this.start) { 711 case 'manual': 712 return; 713 714 case 'autostart': 715 this.play(); 716 break; 717 718 case 'inViewport': 719 var self = this, 720 listener = function () { 721 if (self.isInViewport(self.parentEl, 1.5)) { 722 self.play(); 723 window.removeEventListener('scroll', listener); 724 } 725 }; 726 window.addEventListener('scroll', listener); 727 listener(); 728 break; 729 } 730}; 731 732 733/** 734 * Controls 735 ************************************** 736 */ 737 738/** 739 * Get the current status of the animation between 740 * three different states: 'start', 'progress', 'end'. 741 * @return {string} Instance status 742 */ 743Vivus.prototype.getStatus = function () { 744 return this.currentFrame === 0 ? 'start' : this.currentFrame === this.frameLength ? 'end' : 'progress'; 745}; 746 747/** 748 * Reset the instance to the initial state : undraw 749 * Be careful, it just reset the animation, if you're 750 * playing the animation, this won't stop it. But just 751 * make it start from start. 752 * 753 */ 754Vivus.prototype.reset = function () { 755 return this.setFrameProgress(0); 756}; 757 758/** 759 * Set the instance to the final state : drawn 760 * Be careful, it just set the animation, if you're 761 * playing the animation on rewind, this won't stop it. 762 * But just make it start from the end. 763 * 764 */ 765Vivus.prototype.finish = function () { 766 return this.setFrameProgress(1); 767}; 768 769/** 770 * Set the level of progress of the drawing. 771 * 772 * @param {number} progress Level of progress to set 773 */ 774Vivus.prototype.setFrameProgress = function (progress) { 775 progress = Math.min(1, Math.max(0, progress)); 776 this.currentFrame = Math.round(this.frameLength * progress); 777 this.trace(); 778 return this; 779}; 780 781/** 782 * Play the animation at the desired speed. 783 * Speed must be a valid number (no zero). 784 * By default, the speed value is 1. 785 * But a negative value is accepted to go forward. 786 *
787 * And works with float too. 788 * But don't forget we are in JavaScript, se be nice 789 * with him and give him a 1/2^x value. 790 * 791 * @param {number} speed Animation speed [optional] 792 */ 793Vivus.prototype.play = function (speed) { 794 if (speed && typeof speed !== 'number') { 795 throw new Error('Vivus [play]: invalid speed'); 796 } 797 this.speed = speed || 1; 798 if (!this.handle) { 799 this.drawer(); 800 } 801 return this; 802}; 803 804/** 805 * Stop the current animation, if on progress. 806 * Should not trigger any error. 807 * 808 */ 809Vivus.prototype.stop = function () { 810 if (this.handle) { 811 cancelAnimFrame(this.handle); 812 this.handle = null; 813 } 814 return this; 815}; 816 817/** 818 * Destroy the instance. 819 * Remove all bad styling attributes on all 820 * path tags 821 * 822 */ 823Vivus.prototype.destroy = function () { 824 this.stop(); 825 var i, path; 826 for (i = 0; i < this.map.length; i++) { 827 path = this.map[i]; 828 path.el.style.strokeDashoffset = null; 829 path.el.style.strokeDasharray = null; 830 this.renderPath(i); 831 } 832}; 833 834 835/** 836 * Utils methods 837 * include methods from Codrops 838 ************************************** 839 */ 840 841/** 842 * Method to best guess if a path should added into 843 * the animation or not. 844 * 845 * 1. Use the `data-vivus-ignore` attribute if set 846 * 2. Check if the instance must ignore invisible paths 847 * 3. Check if the path is visible 848 * 849 * For now the visibility checking is unstable. 850 * It will be used for a beta phase. 851 * 852 * Other improvments are planned. Like detecting 853 * is the path got a stroke or a valid opacity. 854 */ 855Vivus.prototype.isInvisible = function (el) { 856 var rect, 857 ignoreAttr = el.getAttribute('data-ignore'); 858 859 if (ignoreAttr !== null) { 860 return ignoreAttr !== 'false'; 861 } 862 863 if (this.ignoreInvisible) { 864 rect = el.getBoundingClientRect(); 865 return !rect.width && !rect.height; 866 } 867 else { 868 return false; 869 } 870}; 871 872/** 873 * Parse attributes of a DOM element to 874 * get an object of {attributeName => attributeValue} 875 * 876 * @param {object} element DOM element to parse 877 * @return {object} Object of attributes 878 */ 879Vivus.prototype.parseAttr = function (element) { 880 var attr, output = {}; 881 if (element && element.attributes) { 882 for (var i = 0; i < element.attributes.length; i++) { 883 attr = element.attributes[i]; 884 output[attr.name] = attr.value; 885 } 886 } 887 return output; 888}; 889 890/** 891 * Reply if an element is in the page viewport 892 * 893 * @param {object} el Element to observe 894 * @param {number} h Percentage of height 895 * @return {boolean} 896 */ 897Vivus.prototype.isInViewport = function (el, h) { 898 var scrolled = this.scrollY(), 899 viewed = scrolled + this.getViewportH(), 900 elBCR = el.getBoundingClientRect(), 901 elHeight = elBCR.height, 902 elTop = scrolled + elBCR.top, 903 elBottom = elTop + elHeight; 904 905 // if 0, the element is considered in the viewport as soon as it enters. 906 // if 1, the element is considered in the viewport only when it's fully inside 907 // value in percentage (1 >= h >= 0) 908 h = h || 0; 909 910 return (elTop + elHeight * h) <= viewed && (elBottom) >= scrolled; 911}; 912 913/** 914 * Alias for document element 915 * 916 * @type {DOMelement} 917 */ 918Vivus.prototype.docElem = window.document.documentElement; 919 920/** 921 * Get the viewport height in pixels 922 * 923 * @return {integer} Viewport height 924 */ 925Vivus.prototype.getViewportH = function () { 926 var client = this.docElem.clientHeight, 927 inner = window.innerHeight; 928 929 if (client < inner) { 930 return inner; 931 } 932 else { 933 return client; 934 } 935}; 936 937/** 938 * Get the page Y offset 939 * 940 * @return {integer} Page Y offset 941 */ 942Vivus.prototype.scrollY = function () { 943 return window.pageYOffset || this.docElem.scrollTop; 944}; 945 946/** 947 * Alias for `requestAnimationFrame` or 948 * `setTimeout` function for deprecated browsers. 949 * 950 */ 951requestAnimFrame = (function () { 952 return ( 953 window.requestAnimationFrame || 954 window.webkitRequestAnimationFrame || 955 window.mozRequestAnimationFrame || 956 window.oRequestAnimationFrame || 957 window.msRequestAnimationFrame || 958 function(/* function */ callback){ 959 return window.setTimeout(callback, 1000 / 60); 960 } 961 ); 962})(); 963 964/** 965 * Alias for `cancelAnimationFrame` or 966 * `cancelTimeout` function for deprecated browsers. 967 * 968 */ 969cancelAnimFrame = (function () { 970 return ( 971 window.cancelAnimationFrame || 972 window.webkitCancelAnimationFrame || 973 window.mozCancelAnimationFrame || 974 window.oCancelAnimationFrame || 975 window.msCancelAnimationFrame || 976 function(id){ 977 return window.clearTimeout(id); 978 } 979 ); 980})(); 981 982/** 983 * Parse string to integer. 984 * If the number is not positive or null 985 * the method will return the default value 986 * or 0 if undefined 987 * 988 * @param {string} value String to parse 989 * @param {*}
989 defaultValue Value to return if the result parsed is invalid 990 * @return {number} 991 * 992 */ 993parsePositiveInt = function (value, defaultValue) { 994 var output = parseInt(value, 10); 995 return (output >= 0) ? output : defaultValue; 996}; 997 998 999 if (typeof define === 'function' && define.amd) { 1000 // AMD. Register as an anonymous module. 1001 define([], function() { 1002 return Vivus; 1003 }); 1004 } else if (typeof exports === 'object') { 1005 // Node. Does not work with strict CommonJS, but 1006 // only CommonJS-like environments that support module.exports, 1007 // like Node. 1008 module.exports = Vivus; 1009 } else { 1010 // Browser globals 1011 window.Vivus = Vivus; 1012 } 1013 1014}(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.