jsketch/jquery.sketchable.animate.js

187 lines
5.6 KiB
JavaScript

/*!
* An animation plugin for jQuery Sketchable | v1.0 | Luis A. Leiva | MIT license
*/
/* eslint-env browser */
/* global jQuery */
;(function($) {
// Custom namespace ID, for private data bindind.
var namespace = 'sketchable';
/**
* Brings animation capabilities to Sketchable elements.
* @class
* @version 1.0
* @param {Sketchable} $instance jQuery Sketchable element.
*/
function AnimateSketch($instance) {
var self = this;
var data = $instance.data(namespace);
// Note: requires strokes to be set in advance.
var sketch = data.sketch;
var strokes = data.strokes;
var events = data.options.events;
var graphics = data.options.graphics;
// Flatten strokes struct to easily handle multistrokes.
var fmtStrokes = [];
for (var s = 0; s < strokes.length; s++) {
var coords = strokes[s];
for (var c = 0; c < coords.length; c++) {
var pt = toPoint(coords[c]);
pt.strokeId = s;
fmtStrokes.push(pt);
}
}
var raf;
var frame = 1;
var count = fmtStrokes.length - 1;
if (typeof events.animationstart === 'function') events.animationstart($instance, data);
(function loop() {
raf = requestAnimationFrame(loop);
try {
drawLine(sketch, fmtStrokes, frame, graphics);
frame++;
} catch (err) {
console.error(err);
cancelAnimationFrame(raf);
}
if (frame == count) {
cancelAnimationFrame(raf);
if (typeof events.animationend === 'function') events.animationend($instance, data);
}
})();
/**
* Cancel current animation.
*/
this.cancel = function() {
cancelAnimationFrame(raf);
return this;
};
/**
* Draw line on jSketch canvas at time t.
* Optionally set graphics options.
* @private
* @param sketch Object jSketch canvas.
* @param coords Array Stroke coordinates.
* @param t Int Time iterator.
* @param [graphics] Object Graphics options.
*/
function drawLine(sketch, coords, t, graphics) {
var prevPt = coords[t - 1];
var currPt = coords[t];
if (sketch.data.firstPointSize && (t === 1 || currPt.strokeId !== prevPt.strokeId)) {
var pt = t > 1 ? currPt : prevPt;
sketch.beginFill(sketch.data.strokeStyle).fillCircle(pt.x, pt.y, sketch.data.firstPointSize);
}
sketch.lineStyle(graphics.strokeStyle, graphics.lineWidth).beginPath();
if (currPt.strokeId === prevPt.strokeId) {
sketch.line(prevPt.x, prevPt.y, currPt.x, currPt.y).stroke();
}
sketch.closePath();
};
/**
* Convert point array to object.
* @private
* @param {Array} p Point, having [x,y,t] items.
* @return {Object}
*/
function toPoint(p) {
if (!(p instanceof Array)) return p;
return { x: p[0], y: p[1], t: p[2] };
};
}
/**
* Animate plugin constructor for jQuery Sketchable instances.
* @param {Object} $instance jQuery Sketchable instance.
* @memberof $.fn.sketchable.plugins
*/
$.fn.sketchable.plugins.animate = function($instance) {
// Access the instance configuration.
var config = $instance.sketchable('config');
var callbacks = {
clear: function(elem, data) {
data.animate && data.animate.cancel();
},
destroy: function(elem, data) {
data.animate && data.animate.cancel();
},
};
// A helper function to override user-defined event listeners.
function override(evName) {
// Flag event override so that it doesn't get fired more than once.
if (config.options['_bound.animate$' + evName]) return;
config.options['_bound.animate$' + evName] = true;
if (config.options.events && typeof config.options.events[evName] === 'function') {
// User has defined this event, so wrap it.
var fn = config.options.events[evName];
config.options.events[evName] = function() {
// Exec original function first, then exec our callback.
fn.apply($instance, arguments);
callbacks[evName].apply($instance, arguments);
};
} else {
// User has not defined this event, so attach our callback.
config.options.events[evName] = callbacks[evName];
}
}
// Note: the init event is used to create sketchable instances,
// therefore it should NOT be overriden.
var events = 'clear destroy'.split(' ');
for (var i = 0; i < events.length; i++) {
override(events[i]);
}
// Expose public API: all jQuery sketchable instances will have these methods.
$.extend($.fn.sketchable.api, {
// Namespace methods to avoid collisions with other plugins.
animate: {
/**
* Animate canvas strokes.
* @return {AnimateSketch}
* @memberof $.fn.sketchable
* @example jqueryElem.sketchable('strokes', strokesArray).sketchable('animate.strokes');
* @example
* // Accessing event hooks:
* jqueryElem.sketchable('config', {
* events: {
* animationstart: function(elem, data) {
* // Animation started.
* },
* animationend: function(elem, data) {
* // Animation ended.
* },
* }
* })
* .sketchable('strokes', strokesArray)
* .sketchable('animate.strokes');
*/
strokes: function() {
var data = $(this).data(namespace);
data.animate = new AnimateSketch($instance);
return $instance;
},
},
});
};
})(jQuery);