PageSourceSearch

https://pyct.com.tw/app/themes/pyct/javascripts/vivus.js

js pyct.com.tw collected 2026-10-02 22:05:09 UTC 29,405 bytes, 1,014 lines download raw bytes

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.