330 lines
11 KiB
JavaScript
330 lines
11 KiB
JavaScript
/*
|
|
* Lazy Line Painter
|
|
* SVG Stroke animation.
|
|
*
|
|
* https://github.com/camoconnell/lazy-line-painter
|
|
* http://www.camoconnell.com
|
|
*
|
|
* Licensed under the MIT license.
|
|
*
|
|
*/
|
|
|
|
(function($, window, undefined) {
|
|
|
|
"use strict";
|
|
|
|
var dataKey = 'lazyLinePainter';
|
|
var methods = {
|
|
|
|
/*
|
|
PUBLIC : SETUP LAZY LINE DATA
|
|
*/
|
|
init: function(_options) {
|
|
|
|
return this.each(function() {
|
|
|
|
var $this = $(this);
|
|
var data = $this.data(dataKey);
|
|
$this.addClass('lazy-line');
|
|
|
|
// If the plugin hasn't been initialized yet
|
|
if (!data) {
|
|
|
|
// Collect settings, define defaults
|
|
var options = $.extend({
|
|
'width': null,
|
|
'height': null,
|
|
'strokeWidth': 2,
|
|
'strokeColor': '#000',
|
|
'strokeOverColor': null,
|
|
'strokeCap': 'round',
|
|
'strokeJoin': 'round',
|
|
'strokeOpacity': 1,
|
|
'arrowEnd': 'none',
|
|
'strokeDash': null,
|
|
'onComplete': null,
|
|
'delay': null,
|
|
'overrideKey': null,
|
|
'drawSequential': true,
|
|
'speedMultiplier': 1
|
|
}, _options);
|
|
|
|
// Set up path information
|
|
// if overrideKey has been defined - use overrideKey as key within the svgData object.
|
|
// else - use the elements id as key within the svgData object.
|
|
var target = options.overrideKey ? options.overrideKey : $this.attr('id').replace('#', '');
|
|
var w = options.svgData[target].dimensions.width;
|
|
var h = options.svgData[target].dimensions.height;
|
|
|
|
// target stroke path
|
|
options.svgData = options.svgData[target].strokepath;
|
|
|
|
// Create svg element and set dimensions
|
|
if (options.width === null) {
|
|
options.width = w;
|
|
}
|
|
if (options.height === null) {
|
|
options.height = h;
|
|
}
|
|
var svg = getSVGElement({
|
|
viewBox: '0 0 ' + w + ' ' + h,
|
|
preserveAspectRatio: 'xMidYMid'
|
|
});
|
|
options.svg = $(svg);
|
|
$this.append(options.svg);
|
|
$this.data(dataKey, options);
|
|
}
|
|
});
|
|
},
|
|
|
|
/*
|
|
PUBLIC : PAINT LAZY LINE DATA
|
|
*/
|
|
paint: function() {
|
|
return this.each(function() {
|
|
var $this = $(this);
|
|
var data = $this.data(dataKey);
|
|
var init = function() {
|
|
// Set width / height of container element
|
|
$this.css({
|
|
'width': data.width,
|
|
'height': data.height
|
|
});
|
|
|
|
// Build array of path objects
|
|
data.paths = [];
|
|
data.longestDuration = 0;
|
|
data.playhead = 0;
|
|
|
|
// Loop paths
|
|
for (var i = 0; i < data.svgData.length; i++) {
|
|
|
|
var path = getPath(data, i);
|
|
var length = path.getTotalLength();
|
|
path.style.strokeDasharray = length + ' ' + length;
|
|
path.style.strokeDashoffset = length;
|
|
path.style.display = 'block';
|
|
path.getBoundingClientRect();
|
|
|
|
var duration = data.svgData[i].duration * data.speedMultiplier;
|
|
if (duration > data.longestDuration) {
|
|
data.longestDuration = duration;
|
|
}
|
|
|
|
data.paths.push({
|
|
'duration': duration,
|
|
'drawStartTime': data.playhead,
|
|
'path': path,
|
|
'length': length
|
|
});
|
|
data.playhead += duration;
|
|
}
|
|
|
|
data.totalDuration = (data.drawSequential) ? data.playhead : data.longestDuration;
|
|
data.rAF = requestAnimationFrame(function(timestamp) {
|
|
draw(timestamp, data);
|
|
});
|
|
};
|
|
|
|
// if delay isset
|
|
if (data.delay === null) {
|
|
init();
|
|
} else {
|
|
setTimeout(init, data.delay);
|
|
}
|
|
});
|
|
},
|
|
|
|
/*
|
|
TOGGLE PAUSE/RESUME ANIMATION
|
|
*/
|
|
pauseResume: function() {
|
|
|
|
return this.each(function() {
|
|
|
|
var data = $(this).data(dataKey);
|
|
|
|
if (!data.paused) {
|
|
data.paused = true;
|
|
cancelAnimationFrame(data.rAF);
|
|
} else {
|
|
data.paused = false;
|
|
// resume
|
|
requestAnimationFrame(function(timestamp) {
|
|
adjustStartTime(timestamp, data)
|
|
});
|
|
}
|
|
});
|
|
},
|
|
/*
|
|
ERASE LAZY LINE DATA
|
|
*/
|
|
erase: function() {
|
|
|
|
return this.each(function() {
|
|
var $this = $(this);
|
|
$this.removeData(dataKey);
|
|
$this.find('svg').empty();
|
|
|
|
});
|
|
},
|
|
|
|
/*
|
|
DESTROY LAZY LINE DATA & ELEMENT
|
|
*/
|
|
destroy: function() {
|
|
|
|
return this.each(function() {
|
|
|
|
var $this = $(this);
|
|
$this.removeData(dataKey);
|
|
$this.remove();
|
|
});
|
|
},
|
|
/*
|
|
STAMP LAZY LINE DATA
|
|
*/
|
|
stamp: function() {
|
|
|
|
return this.each(function() {
|
|
|
|
var $this = $(this),
|
|
d = $this.data(dataKey);
|
|
|
|
var init = function() {
|
|
|
|
// Set width / height of container element
|
|
$this.css({
|
|
'width': d.width,
|
|
'height': d.height
|
|
});
|
|
|
|
// Loop paths
|
|
//$.each(d.svgData, function (i, val) {
|
|
for (i = 0; i < d.svgData.length; i++) {
|
|
d.paper.path(d.svgData[i].path).attr(applyStyles(d, d.svgData[i]));
|
|
}
|
|
|
|
};
|
|
|
|
// if delay isset
|
|
if (d.delay === null)
|
|
init();
|
|
else
|
|
setTimeout(init, d.delay);
|
|
});
|
|
}
|
|
};
|
|
|
|
var adjustStartTime = function(timestamp, o) {
|
|
o.startTime = timestamp - o.elapsed_time;
|
|
requestAnimationFrame(function(timestamp) {
|
|
draw(timestamp, o);
|
|
});
|
|
}
|
|
|
|
var draw = function(timestamp, o) {
|
|
|
|
if (o.startTime == null) {
|
|
o.startTime = timestamp;
|
|
}
|
|
o.elapsed_time = timestamp - o.startTime;
|
|
|
|
for (var i = 0; i < o.paths.length; i++) {
|
|
|
|
var path_elapsed_time;
|
|
if (o.drawSequential) {
|
|
path_elapsed_time = o.elapsed_time - o.paths[i].drawStartTime;
|
|
if (path_elapsed_time < 0) path_elapsed_time = 0;
|
|
} else {
|
|
path_elapsed_time = o.elapsed_time;
|
|
}
|
|
|
|
// don't redraw paths that are finished or paths that aren't up yet
|
|
if (path_elapsed_time < o.paths[i].duration && path_elapsed_time > 0) {
|
|
var frame_length = path_elapsed_time / o.paths[i].duration * o.paths[i].length;
|
|
o.paths[i].path.style.strokeDashoffset = o.paths[i].length - frame_length;
|
|
} else if (path_elapsed_time > o.paths[i].duration) {
|
|
o.paths[i].path.style.strokeDashoffset = 0;
|
|
}
|
|
}
|
|
|
|
// whether to continue
|
|
if (o.elapsed_time < o.totalDuration) {
|
|
o.rAF = requestAnimationFrame(function(timestamp) {
|
|
draw(timestamp, o);
|
|
});
|
|
} else {
|
|
if (o.onComplete != null) o.onComplete();
|
|
}
|
|
}
|
|
|
|
var applyStyles = function(data, value) {
|
|
|
|
var styles = {
|
|
"stroke": (!value.strokeColor) ? data.strokeColor : value.strokeColor,
|
|
"fill-opacity": 0,
|
|
"stroke-dasharray": (!value.strokeDash) ? data.strokeDash : value.strokeDash,
|
|
"stroke-opacity": (!value.strokeOpacity) ? data.strokeOpacity : value.strokeOpacity,
|
|
"stroke-width": (!value.strokeWidth) ? data.strokeWidth : value.strokeWidth,
|
|
"stroke-linecap": (!value.strokeCap) ? data.strokeCap : value.strokeCap,
|
|
"stroke-linejoin": (!value.strokeJoin) ? data.strokeJoin : value.strokeJoin
|
|
};
|
|
|
|
return styles;
|
|
};
|
|
|
|
/*
|
|
PRIVATE : SET PATH DATA
|
|
*/
|
|
var getPath = function(data, i) {
|
|
var path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
|
var $path = $(path);
|
|
data.svg.append($path);
|
|
$path.attr(getAttributes(data, data.svgData[i]));
|
|
return path;
|
|
};
|
|
|
|
/*
|
|
PRIVATE : GET STYLE DATA
|
|
*/
|
|
var getAttributes = function(data, value) {
|
|
var attributes = {
|
|
"d": value.path,
|
|
"stroke": (!value.strokeColor) ? data.strokeColor : value.strokeColor,
|
|
"fill-opacity": 0,
|
|
"stroke-dasharray": (!value.strokeDash) ? data.strokeDash : value.strokeDash,
|
|
"stroke-opacity": (!value.strokeOpacity) ? data.strokeOpacity : value.strokeOpacity,
|
|
"stroke-width": (!value.strokeWidth) ? data.strokeWidth : value.strokeWidth,
|
|
"stroke-linecap": (!value.strokeCap) ? data.strokeCap : value.strokeCap,
|
|
"stroke-linejoin": (!value.strokeJoin) ? data.strokeJoin : value.strokeJoin,
|
|
"arrow-end": (!value.arrowEnd) ? data.arrowEnd : value.arrowEnd,
|
|
"markerWidth": "4",
|
|
"markerHeight": "3",
|
|
"orient": "auto"
|
|
};
|
|
return attributes;
|
|
};
|
|
|
|
/*
|
|
PRIVATE : GET STYLE DATA
|
|
*/
|
|
var getSVGElement = function(attr) {
|
|
var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
svg.setAttributeNS(null, 'viewBox', attr.viewBox);
|
|
svg.setAttributeNS(null, 'preserveAspectRatio', attr.preserveAspectRatio);
|
|
svg.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
|
|
return svg;
|
|
};
|
|
|
|
$.fn.lazylinepainter = function(method) {
|
|
if (methods[method]) {
|
|
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
|
|
} else if (typeof method === 'object' || !method) {
|
|
return methods.init.apply(this, arguments);
|
|
} else {
|
|
// error
|
|
}
|
|
};
|
|
|
|
})(jQuery, window); |