Don't animate all strokes at once

This commit is contained in:
Luis Leiva 2017-12-16 13:09:51 +01:00
parent 6d37c9d1fd
commit 9cb607e7c0
2 changed files with 91 additions and 105 deletions

View File

@ -1,5 +1,5 @@
/*! /*!
* An animation plugin for jQuery Sketchable | v1.0 | Luis A. Leiva | MIT license * An animation plugin for jQuery Sketchable | v1.1 | Luis A. Leiva | MIT license
*/ */
/* eslint-env browser */ /* eslint-env browser */
@ -19,95 +19,87 @@
var self = this; var self = this;
var data = $instance.data(namespace); var data = $instance.data(namespace);
// Note: requires strokes to be set in advance. // Note: To animate a sketchable canvas, strokes must be set in advance.
var sketch = data.sketch; var sketch = data.sketch;
var strokes = data.strokes; var strokes = data.strokes;
var events = data.options.events; var events = data.options.events;
var graphics = data.options.graphics; var graphics = data.options.graphics;
// Reformat strokes to handle multitouch.
var fmtStrokes = []; var fmtStrokes = [];
var pointCount = 0;
for (var s = 0; s < strokes.length; s++) { for (var s = 0; s < strokes.length; s++) {
var fmtCoords = [];
var coords = strokes[s]; var coords = strokes[s];
for (var c = 0; c < coords.length; c++) { for (var c = 0; c < coords.length; c++) {
// Add strokeId to easily handle multistrokes.
var pt = toPoint(coords[c]); var pt = toPoint(coords[c]);
pt.strokeId = s; // The strokeId is not available in jsketchable < 2.2, so add it.
fmtCoords.push(pt); if (!pt.strokeId) pt.strokeId = s;
pointCount++; fmtStrokes.push(pt);
} }
fmtStrokes.push(fmtCoords);
} }
if (typeof events.animationstart === 'function') { if (typeof events.animationstart === 'function')
events.animationstart($instance, data); events.animationstart($instance, data);
}
var raf = {}; // Trigger one animation per stroke. var raf;
var pts = 1; // Will reach the total number of points. var frame = 0;
for (var s = 0; s < fmtStrokes.length; s++) { sketch.lineStyle(graphics.strokeStyle, graphics.lineWidth);
(function(s) {
var coords = fmtStrokes[s];
var frame = 0;
(function loop() { (function loop() {
raf[s] = requestAnimationFrame(loop); raf = requestAnimationFrame(loop);
try { // Here be dragons, thus surround by try/catch.
drawLine(sketch, coords, frame, graphics); try {
} catch (err) { drawLine(sketch, fmtStrokes, frame, graphics);
console.error(err); } catch (err) {
cancelAnimationFrame(raf[s]); console.error(err);
} cancelAnimationFrame(raf);
// Advance local count and check if current animation should end. }
if (++frame === coords.length - 1) { // Advance local count and check if current animation should end.
cancelAnimationFrame(raf[s]); if (++frame === fmtStrokes.length - 1) {
} cancelAnimationFrame(raf);
// Advance global count and check if actual animation has ended. if (typeof events.animationend === 'function')
if (++pts === pointCount - 1 && typeof events.animationend === 'function') { events.animationend($instance, data);
events.animationend($instance, data); }
} })();
})();
})(s);
}
/** /**
* Cancel current animation. * Cancel current animation.
* @return {AnimateSketch}. * @return {AnimateSketch}.
*/ */
this.cancel = function() { this.cancel = function() {
for (var s in raf) { cancelAnimationFrame(raf);
cancelAnimationFrame(raf[s]);
}
return this; return this;
}; };
/** /**
* Draw line on jSketch canvas at time t. * Draw line on jSketch canvas at time t.
* Optionally set graphics options.
* @private * @private
* @param {object} sketch - jSketch canvas. * @param {object} sketch - jSketch canvas.
* @param {array} coords - Stroke coordinates. * @param {array} coords - Stroke coordinates.
* @param {number} t - Time iterator. * @param {number} t - Time iterator.
* @param {object} [graphics] - Graphics options.
*/ */
function drawLine(sketch, coords, t, graphics) { function drawLine(sketch, coords, t) {
var currPt = coords[t]; var currPt = coords[t];
var nextPt = coords[t + 1]; var nextPt = coords[t + 1];
if (sketch.data.firstPointSize && (t === 1 || currPt.strokeId !== nextPt.strokeId)) { if (t === 0 || currPt.strokeId !== nextPt.strokeId) {
var pt = t > 1 ? nextPt : currPt; // Draw first point.
sketch.beginFill(sketch.data.strokeStyle).fillCircle(pt.x, pt.y, sketch.data.firstPointSize); if (sketch.data.firstPointSize) {
var pt = t > 0 ? nextPt : currPt;
sketch.beginFill(sketch.data.strokeStyle)
.fillCircle(pt.x, pt.y, sketch.data.firstPointSize)
.endFill();
}
// Trigger step event for subsequent strokes.
if (t > 0 && typeof events.animationstep === 'function')
events.animationstep($instance, data);
// Flag stroke change.
sketch.closePath().beginPath();
} }
sketch.lineStyle(graphics.strokeStyle, graphics.lineWidth).beginPath(); if (currPt.strokeId === nextPt.strokeId)
if (currPt.strokeId === nextPt.strokeId) {
sketch.line(currPt.x, currPt.y, nextPt.x, nextPt.y).stroke(); sketch.line(currPt.x, currPt.y, nextPt.x, nextPt.y).stroke();
} }
sketch.closePath();
};
/** /**
* Convert point array to object. * Convert point array to object.
@ -117,8 +109,9 @@
*/ */
function toPoint(p) { function toPoint(p) {
if (!(p instanceof Array)) return p; if (!(p instanceof Array)) return p;
return { x: p[0], y: p[1], t: p[2] }; // Point coords is an array with 4 items: [x, y, time, is_drawing, strokeId].
}; return { x: p[0], y: p[1], time: p[2], strokeId: p[4] };
}
} }
/** /**

View File

@ -21,95 +21,87 @@
var self = this; var self = this;
var data = dataBind(instance.elem)[namespace]; var data = dataBind(instance.elem)[namespace];
// Note: requires strokes to be set in advance. // Note: To animate a sketchable canvas, strokes must be set in advance.
var sketch = data.sketch; var sketch = data.sketch;
var strokes = data.strokes; var strokes = data.strokes;
var events = data.options.events; var events = data.options.events;
var graphics = data.options.graphics; var graphics = data.options.graphics;
// Reformat strokes to handle multitouch.
var fmtStrokes = []; var fmtStrokes = [];
var pointCount = 0;
for (var s = 0; s < strokes.length; s++) { for (var s = 0; s < strokes.length; s++) {
var fmtCoords = [];
var coords = strokes[s]; var coords = strokes[s];
for (var c = 0; c < coords.length; c++) { for (var c = 0; c < coords.length; c++) {
// Add strokeId to easily handle multistrokes.
var pt = toPoint(coords[c]); var pt = toPoint(coords[c]);
pt.strokeId = s; // The strokeId is not available in jsketchable < 2.2, so add it.
fmtCoords.push(pt); if (!pt.strokeId) pt.strokeId = s;
pointCount++; fmtStrokes.push(pt);
} }
fmtStrokes.push(fmtCoords);
} }
if (typeof events.animationstart === 'function') { if (typeof events.animationstart === 'function')
events.animationstart(instance, data); events.animationstart(instance, data);
}
var raf = {}; // Trigger one animation per stroke. var raf;
var pts = 1; // Will reach the total number of points. var frame = 0;
for (var s = 0; s < fmtStrokes.length; s++) { sketch.lineStyle(graphics.strokeStyle, graphics.lineWidth);
(function(s) {
var coords = fmtStrokes[s];
var frame = 0;
(function loop() { (function loop() {
raf[s] = requestAnimationFrame(loop); raf = requestAnimationFrame(loop);
try { // Here be dragons, thus surround by try/catch.
drawLine(sketch, coords, frame, graphics); try {
} catch (err) { drawLine(sketch, fmtStrokes, frame, graphics);
console.error(err); } catch (err) {
cancelAnimationFrame(raf[s]); console.error(err);
} cancelAnimationFrame(raf);
// Advance local count and check if current animation should end. }
if (++frame === coords.length - 1) { // Advance local count and check if current animation should end.
cancelAnimationFrame(raf[s]); if (++frame === fmtStrokes.length - 1) {
} cancelAnimationFrame(raf);
// Advance global count and check if actual animation has ended. if (typeof events.animationend === 'function')
if (++pts === pointCount - 1 && typeof events.animationend === 'function') { events.animationend(instance, data);
events.animationend(instance, data); }
} })();
})();
})(s);
}
/** /**
* Cancel current animation. * Cancel current animation.
* @return {AnimateSketch}. * @return {AnimateSketch}.
*/ */
this.cancel = function() { this.cancel = function() {
for (var s in raf) { cancelAnimationFrame(raf);
cancelAnimationFrame(raf[s]);
}
return this; return this;
}; };
/** /**
* Draw line on jSketch canvas at time t. * Draw line on jSketch canvas at time t.
* Optionally set graphics options.
* @private * @private
* @param {object} sketch - jSketch canvas. * @param {object} sketch - jSketch canvas.
* @param {array} coords - Stroke coordinates. * @param {array} coords - Stroke coordinates.
* @param {number} t - Time iterator. * @param {number} t - Time iterator.
* @param {object} [graphics] - Graphics options.
*/ */
function drawLine(sketch, coords, t, graphics) { function drawLine(sketch, coords, t) {
var currPt = coords[t]; var currPt = coords[t];
var nextPt = coords[t + 1]; var nextPt = coords[t + 1];
if (sketch.data.firstPointSize && (t === 1 || currPt.strokeId !== nextPt.strokeId)) { if (t === 0 || currPt.strokeId !== nextPt.strokeId) {
var pt = t > 1 ? nextPt : currPt; // Draw first point.
sketch.beginFill(sketch.data.strokeStyle).fillCircle(pt.x, pt.y, sketch.data.firstPointSize); if (sketch.data.firstPointSize) {
var pt = t > 0 ? nextPt : currPt;
sketch.beginFill(sketch.data.strokeStyle)
.fillCircle(pt.x, pt.y, sketch.data.firstPointSize)
.endFill();
}
// Trigger step event for subsequent strokes.
if (t > 0 && typeof events.animationstep === 'function')
events.animationstep(instance, data);
// Flag stroke change.
sketch.closePath().beginPath();
} }
sketch.lineStyle(graphics.strokeStyle, graphics.lineWidth).beginPath(); if (currPt.strokeId === nextPt.strokeId)
if (currPt.strokeId === nextPt.strokeId) {
sketch.line(currPt.x, currPt.y, nextPt.x, nextPt.y).stroke(); sketch.line(currPt.x, currPt.y, nextPt.x, nextPt.y).stroke();
} }
sketch.closePath();
};
/** /**
* Convert point array to object. * Convert point array to object.
@ -119,8 +111,9 @@
*/ */
function toPoint(p) { function toPoint(p) {
if (!(p instanceof Array)) return p; if (!(p instanceof Array)) return p;
return { x: p[0], y: p[1], t: p[2] }; // Point coords is an array with 4 items: [x, y, time, is_drawing, strokeId].
}; return { x: p[0], y: p[1], time: p[2], strokeId: p[4] };
}
} }
/** /**