<!DOCTYPE html>
<html lang="en" >
<head>
<meta charset="UTF-8">
<title>A Pen by scott smith</title>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<svg height="300" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px"
viewBox="0 0 404.7 354" enable-background="new 0 0 404.7 354" id="hi-there" onclick="hi.reset().play();">
<!-- HI -->
<path data-duration="10" stroke-width="4" d="M324.6,61.2c16.6,0,29.5-12.9,29.5-29.5c0-16.6-12.9-29.5-29.5-29.5c-16.6,0-29.5,12.9-29.5,29.5C295.1,48.4,308,61.2,324.6,61.2z"/>
<path data-duration="130" stroke-width="4" d="M366.2,204.2c-9.8,0-15-5.6-15-15.1V77.2h-85v28h19.5c9.8,0,8.5,2.1,8.5,11.6v72.4c0,9.5,0.5,15.1-9.3,15.1H277h-20.7c-8.5,0-14.2-4.1-14.2-12.9V52.4c0-8.5,5.7-12.3,14.2-12.3h18.8v-28h-127v28h18.1c8.5,0,9.9,2.1,9.9,8.9v56.1h-75V53.4c0-11.5,8.6-13.3,17-13.3h11v-28H2.2v28h26c8.5,0,12,2.1,12,7.9v142.2c0,8.5-3.6,13.9-12,13.9h-21v33h122v-33h-11c-8.5,0-17-4.1-17-12.2v-57.8h75v58.4c0,9.1-1.4,11.6-9.9,11.6h-18.1v33h122.9h5.9h102.2v-33H366.2z"/>
<path data-async data-delay="20" stroke-width="4" d="M358.8,82.8c11.1-4.2,18.8-14.7,18.8-27.5c0-8.5-3.4-16-8.9-21.3"/>
<path data-async stroke-width="4" d="M124.2,105.7V77c0-11.5,9.1-13.8,17.5-13.8h10.5V44.7"/>
<polyline data-async stroke-width="4" points="147.9,40.2 171.2,63.2 175.7,63.2"/>
<line data-async stroke-width="4" x1="295.1" y1="32.1" x2="275.2" y2="12.2"/>
<path data-async stroke-width="4" d="M266.2,204.7V75.9c0-8.5,5.2-12.8,13.7-12.8h18.3V44.7"/>
<polyline data-async stroke-width="4" points="265.9,105.2 289.2,129.2 293.7,129.2"/>
<polyline data-async stroke-width="4" points="374.2,204.7 374.2,94.2 358.8,82.8 351.2,77.2"/>
<polyline data-async stroke-width="4" points="148.2,237.2 171.2,261.2 294.6,261.2 300.5,261.2 402.2,261.2 402.2,228.2 379.2,204.2"/>
<polyline data-async stroke-width="4" points="124.2,204.7 124.2,157.2 175.7,157.2"/>
<line data-async stroke-width="4" x1="147.7" y1="228.2" x2="129.2" y2="204.2"/>
<polyline data-async stroke-width="4" points="7.2,237.3 30.2,261.2 152.2,261.2 152.2,241.7"/>
<polyline data-async stroke-width="4" points="1.9,40.2 26,63.2 39.7,63.2"/>
<line data-async stroke-width="4" x1="129.2" y1="12.2" x2="148.2" y2="33.2"/>
<line data-async stroke-width="4" x1="303.9" y1="53" x2="328.1" y2="77.2"/>
<line stroke-width="4" x1="345.1" y1="10.5" x2="368.7" y2="34"/>
<!-- there -->
<path data-delay="30" data-duration="60" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" d="M76.8,337.3c0,0,1.9,12.2,13.1,12.2c22.1,0,23.8-1.8,59-66.4c-19.7,35.7-36.4,66.2-19.3,66.2c15.2,0,22.9-14.2,28.3-23.7c3.3-0.5,24-3.2,35-25.5c4-8.1,4.1-17.8-8.1-15.2c-5.6,1.2-13.1,14.8-15.7,19.2c-7.6,12.7-22.4,45.2-22.4,45.2s10.3-22.4,21.5-22.4c15.5,0-9.4,22.4,4.7,22.4c4.9,0,11.7-11.4,16.6-20.9c7.5,4.7,19.7,1.7,24.5-8.1c10.1-20.4-14.4-12.8-24.5,8.1c-5.5,11.3-2.2,21.1,11.2,21.1c16.4,0,26.1-28.3,30.5-37.5c9.9,2.5,14,2.5,22.7-1.1c-3.5,5.1-24,38.1-8.3,38.1c6.7,0,11.7-11.4,16.6-20.9c7.5,4.7,19.7,1.7,24.5-8.1c10.1-20.4-14.4-12.8-24.5,8.1c-5.5,11.3-2.2,21.1,11.2,21.1c16.4,0,20.6-4,24.7-10.5"/>
<path stroke-width="4" stroke-linecap="round" stroke-linejoin="round" d="M157.3,300.8c3.8-2.3-29,0.8-35.6,3.2"/>
</svg>
<script src='http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js'></script>
<script src="js/index.js"></script>
</body>
</html>
/*Downloaded from https://www.codeseek.co/philabama/a-pen-by-scott-smith-RWRLpo */
/**
* vivus - JavaScript library to make drawing animation on SVG
* @version v0.2.3
* @link https://github.com/maxwellito/vivus
* @license MIT
*/
'use strict';
(function (window, document) {
'use strict';
/**
* Pathformer
* Beta version
*
* Take any SVG version 1.1 and transform
* child elements to 'path' elements
*
* This code is purely forked from
* https://github.com/Waest/SVGPathConverter
*/
/**
* Class constructor
*
* @param {DOM|String} element Dom element of the SVG or id of it
*/
function Pathformer(element) {
// Test params
if (typeof element === 'undefined') {
throw new Error('Pathformer [constructor]: "element" parameter is required');
}
// Set the element
if (element.constructor === String) {
element = document.getElementById(element);
if (!element) {
throw new Error('Pathformer [constructor]: "element" parameter is not related to an existing ID');
}
}
if (element.constructor instanceof window.SVGElement || /^svg$/i.test(element.nodeName)) {
this.el = element;
} else {
throw new Error('Pathformer [constructor]: "element" parameter must be a string or a SVGelement');
}
// Start
this.scan(element);
}
/**
* List of tags which can be transformed
* to path elements
*
* @type {Array}
*/
Pathformer.prototype.TYPES = ['line', 'ellipse', 'circle', 'polygon', 'polyline', 'rect'];
/**
* List of attribute names which contain
* data. This array list them to check if
* they contain bad values, like percentage.
*
* @type {Array}
*/
Pathformer.prototype.ATTR_WATCH = ['cx', 'cy', 'points', 'r', 'rx', 'ry', 'x', 'x1', 'x2', 'y', 'y1', 'y2'];
/**
* Finds the elements compatible for transform
* and apply the liked method
*
* @param {object} options Object from the constructor
*/
Pathformer.prototype.scan = function (svg) {
var fn, element, pathData, pathDom,
elements = svg.querySelectorAll(this.TYPES.join(','));
for (var i = 0; i < elements.length; i++) {
element = elements[i];
fn = this[element.tagName.toLowerCase() + 'ToPath'];
pathData = fn(this.parseAttr(element.attributes));
pathDom = this.pathMaker(element, pathData);
element.parentNode.replaceChild(pathDom, element);
}
};
/**
* Read `line` element to extract and transform
* data, to make it ready for a `path` object.
*
* @param {DOMelement} element Line element to transform
* @return {object} Data for a `path` element
*/
Pathformer.prototype.lineToPath = function (element) {
var newElement = {};
newElement.d = 'M' + element.x1 + ',' + element.y1 + 'L' + element.x2 + ',' + element.y2;
return newElement;
};
/**
* Read `rect` element to extract and transform
* data, to make it ready for a `path` object.
* The radius-border is not taken in charge yet.
* (your help is more than welcomed)
*
* @param {DOMelement} element Rect element to transform
* @return {object} Data for a `path` element
*/
Pathformer.prototype.rectToPath = function (element) {
var newElement = {},
x = parseFloat(element.x) || 0,
y = parseFloat(element.y) || 0,
width = parseFloat(element.width) || 0,
height = parseFloat(element.height) || 0;
newElement.d = 'M' + x + ' ' + y + ' ';
newElement.d += 'L' + (x + width) + ' ' + y + ' ';
newElement.d += 'L' + (x + width) + ' ' + (y + height) + ' ';
newElement.d += 'L' + x + ' ' + (y + height) + ' Z';
return newElement;
};
/**
* Read `polyline` element to extract and transform
* data, to make it ready for a `path` object.
*
* @param {DOMelement} element Polyline element to transform
* @return {object} Data for a `path` element
*/
Pathformer.prototype.polylineToPath = function (element) {
var i, path;
var newElement = {};
var points = element.points.trim().split(' ');
// Reformatting if points are defined without commas
if (element.points.indexOf(',') === -1) {
var formattedPoints = [];
for (i = 0; i < points.length; i+=2) {
formattedPoints.push(points[i] + ',' + points[i+1]);
}
points = formattedPoints;
}
// Generate the path.d value
path = 'M' + points[0];
for(i = 1; i < points.length; i++) {
if (points[i].indexOf(',') !== -1) {
path += 'L' + points[i];
}
}
newElement.d = path;
return newElement;
};
/**
* Read `polygon` element to extract and transform
* data, to make it ready for a `path` object.
* This method rely on polylineToPath, because the
* logic is similar. The path created is just closed,
* so it needs an 'Z' at the end.
*
* @param {DOMelement} element Polygon element to transform
* @return {object} Data for a `path` element
*/
Pathformer.prototype.polygonToPath = function (element) {
var newElement = Pathformer.prototype.polylineToPath(element);
newElement.d += 'Z';
return newElement;
};
/**
* Read `ellipse` element to extract and transform
* data, to make it ready for a `path` object.
*
* @param {DOMelement} element ellipse element to transform
* @return {object} Data for a `path` element
*/
Pathformer.prototype.ellipseToPath = function (element) {
var startX = element.cx - element.rx,
startY = element.cy;
var endX = parseFloat(element.cx) + parseFloat(element.rx),
endY = element.cy;
var newElement = {};
newElement.d = 'M' + startX + ',' + startY +
'A' + element.rx + ',' + element.ry + ' 0,1,1 ' + endX + ',' + endY +
'A' + element.rx + ',' + element.ry + ' 0,1,1 ' + startX + ',' + endY;
return newElement;
};
/**
* Read `circle` element to extract and transform
* data, to make it ready for a `path` object.
*
* @param {DOMelement} element Circle element to transform
* @return {object} Data for a `path` element
*/
Pathformer.prototype.circleToPath = function (element) {
var newElement = {};
var startX = element.cx - element.r,
startY = element.cy;
var endX = parseFloat(element.cx) + parseFloat(element.r),
endY = element.cy;
newElement.d = 'M' + startX + ',' + startY +
'A' + element.r + ',' + element.r + ' 0,1,1 ' + endX + ',' + endY +
'A' + element.r + ',' + element.r + ' 0,1,1 ' + startX + ',' + endY;
return newElement;
};
/**
* Create `path` elements form original element
* and prepared objects
*
* @param {DOMelement} element Original element to transform
* @param {object} pathData Path data (from `toPath` methods)
* @return {DOMelement} Path element
*/
Pathformer.prototype.pathMaker = function (element, pathData) {
var i, attr, pathTag = document.createElementNS('http://www.w3.org/2000/svg','path');
for(i = 0; i < element.attributes.length; i++) {
attr = element.attributes[i];
if (this.ATTR_WATCH.indexOf(attr.name) === -1) {
pathTag.setAttribute(attr.name, attr.value);
}
}
for(i in pathData) {
pathTag.setAttribute(i, pathData[i]);
}
return pathTag;
};
/**
* Parse attributes of a DOM element to
* get an object of attribute => value
*
* @param {NamedNodeMap} attributes Attributes object from DOM element to parse
* @return {object} Object of attributes
*/
Pathformer.prototype.parseAttr = function (element) {
var attr, output = {};
for (var i = 0; i < element.length; i++) {
attr = element[i];
// Check if no data attribute contains '%', or the transformation is impossible
if (this.ATTR_WATCH.indexOf(attr.name) !== -1 && attr.value.indexOf('%') !== -1) {
throw new Error('Pathformer [parseAttr]: a SVG shape got values in percentage. This cannot be transformed into \'path\' tags. Please use \'viewBox\'.');
}
output[attr.name] = attr.value;
}
return output;
};
'use strict';
var requestAnimFrame, cancelAnimFrame, parsePositiveInt;
/**
* Vivus
* Beta version
*
* Take any SVG and make the animation
* to give give the impression of live drawing
*
* This in more than just inspired from codrops
* At that point, it's a pure fork.
*/
/**
* Class constructor
* option structure
* type: 'delayed'|'async'|'oneByOne'|'script' (to know if the item must be drawn asynchronously or not, default: delayed)
* duration: <int> (in frames)
* start: 'inViewport'|'manual'|'autostart' (start automatically the animation, default: inViewport)
* delay: <int> (delay between the drawing of first and last path)
* dashGap <integer> whitespace extra margin between dashes
* pathTimingFunction <function> timing animation function for each path element of the SVG
* animTimingFunction <function> timing animation function for the complete SVG
* forceRender <boolean> force the browser to re-render all updated path items
* selfDestroy <boolean> removes all extra styling on the SVG, and leaves it as original
*
* The attribute 'type' is by default on 'delayed'.
* - 'delayed'
* all paths are draw at the same time but with a
* little delay between them before start
* - 'async'
* all path are start and finish at the same time
* - 'oneByOne'
* only one path is draw at the time
* the end of the first one will trigger the draw
* of the next one
*
* All these values can be overwritten individually
* for each path item in the SVG
* The value of frames will always take the advantage of
* the duration value.
* If you fail somewhere, an error will be thrown.
* Good luck.
*
* @constructor
* @this {Vivus}
* @param {DOM|String} element Dom element of the SVG or id of it
* @param {Object} options Options about the animation
* @param {Function} callback Callback for the end of the animation
*/
function Vivus (element, options, callback) {
// Setup
this.isReady = false;
this.setElement(element, options);
this.setOptions(options);
this.setCallback(callback);
if (this.isReady) {
this.init();
}
}
/**
* Timing functions
**************************************
*
* Default functions to help developers.
* It always take a number as parameter (between 0 to 1) then
* return a number (between 0 and 1)
*/
Vivus.LINEAR = function (x) {return x;};
Vivus.EASE = function (x) {return -Math.cos(x * Math.PI) / 2 + 0.5;};
Vivus.EASE_OUT = function (x) {return 1 - Math.pow(1-x, 3);};
Vivus.EASE_IN = function (x) {return Math.pow(x, 3);};
Vivus.EASE_OUT_BOUNCE = function (x) {
var base = -Math.cos(x * (0.5 * Math.PI)) + 1,
rate = Math.pow(base,1.5),
rateR = Math.pow(1 - x, 2),
progress = -Math.abs(Math.cos(rate * (2.5 * Math.PI) )) + 1;
return (1- rateR) + (progress * rateR);
};
/**
* Setters
**************************************
*/
/**
* Check and set the element in the instance
* The method will not return anything, but will throw an
* error if the parameter is invalid
*
* @param {DOM|String} element SVG Dom element or id of it
*/
Vivus.prototype.setElement = function (element, options) {
// Basic check
if (typeof element === 'undefined') {
throw new Error('Vivus [constructor]: "element" parameter is required');
}
// Set the element
if (element.constructor === String) {
element = document.getElementById(element);
if (!element) {
throw new Error('Vivus [constructor]: "element" parameter is not related to an existing ID');
}
}
this.parentEl = element;
// Create the object element if the property `file` exists in the options object
if (options && options.file) {
var objElm = document.createElement('object');
objElm.setAttribute('type', 'image/svg+xml');
objElm.setAttribute('data', options.file);
objElm.setAttribute('width', '100%');
objElm.setAttribute('height', '100%');
element.appendChild(objElm);
element = objElm;
}
switch (element.constructor) {
case window.SVGSVGElement:
case window.SVGElement:
this.el = element;
this.isReady = true;
break;
case window.HTMLObjectElement:
// If the Object is already loaded
this.el = element.contentDocument && element.contentDocument.querySelector('svg');
if (this.el) {
this.isReady = true;
return;
}
// If we have to wait for it
var self = this;
element.addEventListener('load', function () {
self.el = element.contentDocument && element.contentDocument.querySelector('svg');
if (!self.el) {
throw new Error('Vivus [constructor]: object loaded does not contain any SVG');
}
else {
self.isReady = true;
self.init();
}
});
break;
default:
throw new Error('Vivus [constructor]: "element" parameter is not valid (or miss the "file" attribute)');
}
};
/**
* Set up user option to the instance
* The method will not return anything, but will throw an
* error if the parameter is invalid
*
* @param {object} options Object from the constructor
*/
Vivus.prototype.setOptions = function (options) {
var allowedTypes = ['delayed', 'async', 'oneByOne', 'scenario', 'scenario-sync'];
var allowedStarts = ['inViewport', 'manual', 'autostart'];
// Basic check
if (options !== undefined && options.constructor !== Object) {
throw new Error('Vivus [constructor]: "options" parameter must be an object');
}
else {
options = options || {};
}
// Set the animation type
if (options.type && allowedTypes.indexOf(options.type) === -1) {
throw new Error('Vivus [constructor]: ' + options.type + ' is not an existing animation `type`');
}
else {
this.type = options.type || allowedTypes[0];
}
// Set the start type
if (options.start && allowedStarts.indexOf(options.start) === -1) {
throw new Error('Vivus [constructor]: ' + options.start + ' is not an existing `start` option');
}
else {
this.start = options.start || allowedStarts[0];
}
this.isIE = (window.navigator.userAgent.indexOf('MSIE') !== -1 || window.navigator.userAgent.indexOf('Trident/') !== -1 || window.navigator.userAgent.indexOf('Edge/') !== -1 );
this.duration = parsePositiveInt(options.duration, 120);
this.delay = parsePositiveInt(options.delay, null);
this.dashGap = parsePositiveInt(options.dashGap, 2);
this.forceRender = options.hasOwnProperty('forceRender') ? !!options.forceRender : this.isIE;
this.selfDestroy = !!options.selfDestroy;
this.onReady = options.onReady;
this.ignoreInvisible = options.hasOwnProperty('ignoreInvisible') ? !!options.ignoreInvisible : false;
this.animTimingFunction = options.animTimingFunction || Vivus.LINEAR;
this.pathTimingFunction = options.pathTimingFunction || Vivus.LINEAR;
if (this.delay >= this.duration) {
throw new Error('Vivus [constructor]: delay must be shorter than duration');
}
};
/**
* Set up callback to the instance
* The method will not return enything, but will throw an
* error if the parameter is invalid
*
* @param {Function} callback Callback for the animation end
*/
Vivus.prototype.setCallback = function (callback) {
// Basic check
if (!!callback && callback.constructor !== Function) {
throw new Error('Vivus [constructor]: "callback" parameter must be a function');
}
this.callback = callback || function () {};
};
/**
* Core
**************************************
*/
/**
* Map the svg, path by path.
* The method return nothing, it just fill the
* `map` array. Each item in this array represent
* a path element from the SVG, with informations for
* the animation.
*
* ```
* [
* {
* el: <DOMobj> the path element
* length: <number> length of the path line
* startAt: <number> time start of the path animation (in frames)
* duration: <number> path animation duration (in frames)
* },
* ...
* ]
* ```
*
*/
Vivus.prototype.mapping = function () {
var i, paths, path, pAttrs, pathObj, totalLength, lengthMeter, timePoint;
timePoint = totalLength = lengthMeter = 0;
paths = this.el.querySelectorAll('path');
for (i = 0; i < paths.length; i++) {
path = paths[i];
if (this.isInvisible(path)) {
continue;
}
pathObj = {
el: path,
length: Math.ceil(path.getTotalLength())
};
// Test if the path length is correct
if (isNaN(pathObj.length)) {
if (window.console && console.warn) {
console.warn('Vivus [mapping]: cannot retrieve a path element length', path);
}
continue;
}
totalLength += pathObj.length;
this.map.push(pathObj);
path.style.strokeDasharray = pathObj.length + ' ' + (pathObj.length + this.dashGap);
path.style.strokeDashoffset = pathObj.length;
// Fix IE glitch
if (this.isIE) {
pathObj.length += this.dashGap;
}
this.renderPath(i);
}
totalLength = totalLength === 0 ? 1 : totalLength;
this.delay = this.delay === null ? this.duration / 3 : this.delay;
this.delayUnit = this.delay / (paths.length > 1 ? paths.length - 1 : 1);
for (i = 0; i < this.map.length; i++) {
pathObj = this.map[i];
switch (this.type) {
case 'delayed':
pathObj.startAt = this.delayUnit * i;
pathObj.duration = this.duration - this.delay;
break;
case 'oneByOne':
pathObj.startAt = lengthMeter / totalLength * this.duration;
pathObj.duration = pathObj.length / totalLength * this.duration;
break;
case 'async':
pathObj.startAt = 0;
pathObj.duration = this.duration;
break;
case 'scenario-sync':
path = paths[i];
pAttrs = this.parseAttr(path);
pathObj.startAt = timePoint + (parsePositiveInt(pAttrs['data-delay'], this.delayUnit) || 0);
pathObj.duration = parsePositiveInt(pAttrs['data-duration'], this.duration);
timePoint = pAttrs['data-async'] !== undefined ? pathObj.startAt : pathObj.startAt + pathObj.duration;
this.frameLength = Math.max(this.frameLength, (pathObj.startAt + pathObj.duration));
break;
case 'scenario':
path = paths[i];
pAttrs = this.parseAttr(path);
pathObj.startAt = parsePositiveInt(pAttrs['data-start'], this.delayUnit) || 0;
pathObj.duration = parsePositiveInt(pAttrs['data-duration'], this.duration);
this.frameLength = Math.max(this.frameLength, (pathObj.startAt + pathObj.duration));
break;
}
lengthMeter += pathObj.length;
this.frameLength = this.frameLength || this.duration;
}
};
/**
* Interval method to draw the SVG from current
* position of the animation. It update the value of
* `currentFrame` and re-trace the SVG.
*
* It use this.handle to store the requestAnimationFrame
* and clear it one the animation is stopped. So this
* attribute can be used to know if the animation is
* playing.
*
* Once the animation at the end, this method will
* trigger the Vivus callback.
*
*/
Vivus.prototype.drawer = function () {
var self = this;
this.currentFrame += this.speed;
if (this.currentFrame <= 0) {
this.stop();
this.reset();
this.callback(this);
} else if (this.currentFrame >= this.frameLength) {
this.stop();
this.currentFrame = this.frameLength;
this.trace();
if (this.selfDestroy) {
this.destroy();
}
this.callback(this);
} else {
this.trace();
this.handle = requestAnimFrame(function () {
self.drawer();
});
}
};
/**
* Draw the SVG at the current instant from the
* `currentFrame` value. Here is where most of the magic is.
* The trick is to use the `strokeDashoffset` style property.
*
* For optimisation reasons, a new property called `progress`
* is added in each item of `map`. This one contain the current
* progress of the path element. Only if the new value is different
* the new value will be applied to the DOM element. This
* method save a lot of resources to re-render the SVG. And could
* be improved if the animation couldn't be played forward.
*
*/
Vivus.prototype.trace = function () {
var i, progress, path, currentFrame;
currentFrame = this.animTimingFunction(this.currentFrame / this.frameLength) * this.frameLength;
for (i = 0; i < this.map.length; i++) {
path = this.map[i];
progress = (currentFrame - path.startAt) / path.duration;
progress = this.pathTimingFunction(Math.max(0, Math.min(1, progress)));
if (path.progress !== progress) {
path.progress = progress;
path.el.style.strokeDashoffset = Math.floor(path.length * (1 - progress));
this.renderPath(i);
}
}
};
/**
* Method forcing the browser to re-render a path element
* from it's index in the map. Depending on the `forceRender`
* value.
* The trick is to replace the path element by it's clone.
* This practice is not recommended because it's asking more
* ressources, too much DOM manupulation..
* but it's the only way to let the magic happen on IE.
* By default, this fallback is only applied on IE.
*
* @param {Number} index Path index
*/
Vivus.prototype.renderPath = function (index) {
if (this.forceRender && this.map && this.map[index]) {
var pathObj = this.map[index],
newPath = pathObj.el.cloneNode(true);
pathObj.el.parentNode.replaceChild(newPath, pathObj.el);
pathObj.el = newPath;
}
};
/**
* When the SVG object is loaded and ready,
* this method will continue the initialisation.
*
* This this mainly due to the case of passing an
* object tag in the constructor. It will wait
* the end of the loading to initialise.
*
*/
Vivus.prototype.init = function () {
// Set object variables
this.frameLength = 0;
this.currentFrame = 0;
this.map = [];
// Start
new Pathformer(this.el);
this.mapping();
this.starter();
if (this.onReady) {
this.onReady(this);
}
};
/**
* Trigger to start of the animation.
* Depending on the `start` value, a different script
* will be applied.
*
* If the `start` value is not valid, an error will be thrown.
* Even if technically, this is impossible.
*
*/
Vivus.prototype.starter = function () {
switch (this.start) {
case 'manual':
return;
case 'autostart':
this.play();
break;
case 'inViewport':
var self = this,
listener = function () {
if (self.isInViewport(self.parentEl, 1)) {
self.play();
window.removeEventListener('scroll', listener);
}
};
window.addEventListener('scroll', listener);
listener();
break;
}
};
/**
* Controls
**************************************
*/
/**
* Get the current status of the animation between
* three different states: 'start', 'progress', 'end'.
* @return {string} Instance status
*/
Vivus.prototype.getStatus = function () {
return this.currentFrame === 0 ? 'start' : this.currentFrame === this.frameLength ? 'end' : 'progress';
};
/**
* Reset the instance to the initial state : undraw
* Be careful, it just reset the animation, if you're
* playing the animation, this won't stop it. But just
* make it start from start.
*
*/
Vivus.prototype.reset = function () {
return this.setFrameProgress(0);
};
/**
* Set the instance to the final state : drawn
* Be careful, it just set the animation, if you're
* playing the animation on rewind, this won't stop it.
* But just make it start from the end.
*
*/
Vivus.prototype.finish = function () {
return this.setFrameProgress(1);
};
/**
* Set the level of progress of the drawing.
*
* @param {number} progress Level of progress to set
*/
Vivus.prototype.setFrameProgress = function (progress) {
progress = Math.min(1, Math.max(0, progress));
this.currentFrame = Math.round(this.frameLength * progress);
this.trace();
return this;
};
/**
* Play the animation at the desired speed.
* Speed must be a valid number (no zero).
* By default, the speed value is 1.
* But a negative value is accepted to go forward.
*
* And works with float too.
* But don't forget we are in JavaScript, se be nice
* with him and give him a 1/2^x value.
*
* @param {number} speed Animation speed [optional]
*/
Vivus.prototype.play = function (speed) {
if (speed && typeof speed !== 'number') {
throw new Error('Vivus [play]: invalid speed');
}
this.speed = speed || 1;
if (!this.handle) {
this.drawer();
}
return this;
};
/**
* Stop the current animation, if on progress.
* Should not trigger any error.
*
*/
Vivus.prototype.stop = function () {
if (this.handle) {
cancelAnimFrame(this.handle);
delete this.handle;
}
return this;
};
/**
* Destroy the instance.
* Remove all bad styling attributes on all
* path tags
*
*/
Vivus.prototype.destroy = function () {
var i, path;
for (i = 0; i < this.map.length; i++) {
path = this.map[i];
path.el.style.strokeDashoffset = null;
path.el.style.strokeDasharray = null;
this.renderPath(i);
}
};
/**
* Utils methods
* include methods from Codrops
**************************************
*/
/**
* Method to best guess if a path should added into
* the animation or not.
*
* 1. Use the `data-vivus-ignore` attribute if set
* 2. Check if the instance must ignore invisible paths
* 3. Check if the path is visible
*
* For now the visibility checking is unstable.
* It will be used for a beta phase.
*
* Other improvments are planned. Like detecting
* is the path got a stroke or a valid opacity.
*/
Vivus.prototype.isInvisible = function (el) {
var rect,
ignoreAttr = el.getAttribute('data-ignore');
if (ignoreAttr !== null) {
return ignoreAttr !== 'false';
}
if (this.ignoreInvisible) {
rect = el.getBoundingClientRect();
return !rect.width && !rect.height;
}
else {
return false;
}
};
/**
* Parse attributes of a DOM element to
* get an object of {attributeName => attributeValue}
*
* @param {object} element DOM element to parse
* @return {object} Object of attributes
*/
Vivus.prototype.parseAttr = function (element) {
var attr, output = {};
if (element && element.attributes) {
for (var i = 0; i < element.attributes.length; i++) {
attr = element.attributes[i];
output[attr.name] = attr.value;
}
}
return output;
};
/**
* Reply if an element is in the page viewport
*
* @param {object} el Element to observe
* @param {number} h Percentage of height
* @return {boolean}
*/
Vivus.prototype.isInViewport = function (el, h) {
var scrolled = this.scrollY(),
viewed = scrolled + this.getViewportH(),
elBCR = el.getBoundingClientRect(),
elHeight = elBCR.height,
elTop = scrolled + elBCR.top,
elBottom = elTop + elHeight;
// if 0, the element is considered in the viewport as soon as it enters.
// if 1, the element is considered in the viewport only when it's fully inside
// value in percentage (1 >= h >= 0)
h = h || 0;
return (elTop + elHeight * h) <= viewed && (elBottom) >= scrolled;
};
/**
* Alias for document element
*
* @type {DOMelement}
*/
Vivus.prototype.docElem = window.document.documentElement;
/**
* Get the viewport height in pixels
*
* @return {integer} Viewport height
*/
Vivus.prototype.getViewportH = function () {
var client = this.docElem.clientHeight,
inner = window.innerHeight;
if (client < inner) {
return inner;
}
else {
return client;
}
};
/**
* Get the page Y offset
*
* @return {integer} Page Y offset
*/
Vivus.prototype.scrollY = function () {
return window.pageYOffset || this.docElem.scrollTop;
};
/**
* Alias for `requestAnimationFrame` or
* `setTimeout` function for deprecated browsers.
*
*/
requestAnimFrame = (function () {
return (
window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(/* function */ callback){
return window.setTimeout(callback, 1000 / 60);
}
);
})();
/**
* Alias for `cancelAnimationFrame` or
* `cancelTimeout` function for deprecated browsers.
*
*/
cancelAnimFrame = (function () {
return (
window.cancelAnimationFrame ||
window.webkitCancelAnimationFrame ||
window.mozCancelAnimationFrame ||
window.oCancelAnimationFrame ||
window.msCancelAnimationFrame ||
function(id){
return window.clearTimeout(id);
}
);
})();
/**
* Parse string to integer.
* If the number is not positive or null
* the method will return the default value
* or 0 if undefined
*
* @param {string} value String to parse
* @param {*} defaultValue Value to return if the result parsed is invalid
* @return {number}
*
*/
parsePositiveInt = function (value, defaultValue) {
var output = parseInt(value, 10);
return (output >= 0) ? output : defaultValue;
};
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define([], function() {
return Vivus;
});
} else if (typeof exports === 'object') {
// Node. Does not work with strict CommonJS, but
// only CommonJS-like environments that support module.exports,
// like Node.
module.exports = Vivus;
} else {
// Browser globals
window.Vivus = Vivus;
}
}(window, document));
function easeOutBounce (x) {
var base = -Math.cos(x * (0.5 * Math.PI)) + 1;
var rate = Math.pow(base,1.5);
var rateR = Math.pow(1 - x, 2);
var progress = -Math.abs(Math.cos(rate * (2.5 * Math.PI) )) + 1;
return (1- rateR) + (progress * rateR);
}
var timing,
timingProps = {
type: 'delayed',
duration: 150,
start: 'autostart',
pathTimingFunction: Vivus.LINEAR,
animTimingFunction: Vivus.LINEAR
};
function timingTest (buttonEl, property, type) {
var activeSibling = buttonEl.parentNode.querySelector('button.active');
activeSibling.classList.remove('active');
buttonEl.classList.add('active');
timingProps.type = (property === 'type') ? type : timingProps.type;
timingProps.pathTimingFunction = (property === 'path') ? Vivus[type] : timingProps.pathTimingFunction;
timingProps.animTimingFunction = (property === 'anim') ? Vivus[type] : timingProps.animTimingFunction;
timing && timing.stop().destroy();
timing = new Vivus('timing-example', timingProps);
}
var hi2 = new Vivus('hi-there', {type: 'scenario-sync', duration: 20, start: 'autostart', dashGap: 20, forceRender: false},
function () {
if (window.console) {
console.log('Animation finished. [log triggered from callback]');
}
}),
hi = new Vivus('hi-there', {type: 'oneByOne', duration: 150}),
obt2 = new Vivus('obturateur2', {type: 'async', duration: 150}),
obt3 = new Vivus('obturateur3', {type: 'oneByOne', duration: 150}),
pola = new Vivus('polaroid', {type: 'scenario-sync', duration: 20, forceRender: false});