PageSourceSearch

https://www.sanyau.edu.cn/_upload/tpl/00/18/24/template24/js/vivus.js

js sanyau.edu.cn collected 2026-09-25 04:03:20 UTC 32,792 bytes, 1,058 lines download raw bytes

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.