From 58e6abea10e6c9fd85476d24435cdb43b12aa8c1 Mon Sep 17 00:00:00 2001 From: Luis Leiva Date: Mon, 4 Dec 2017 09:55:17 +0100 Subject: [PATCH] Added animate plugin --- Gruntfile.js | 14 ++ dist/jquery.sketchable.full.min.js | 4 +- dist/jquery.sketchable.memento.min.js | 2 +- dist/sketchable.full.min.js | 4 +- dist/sketchable.memento.min.js | 2 +- jquery.sketchable.animate.js | 186 +++++++++++++++++++++++++ sketchable.animate.js | 188 ++++++++++++++++++++++++++ 7 files changed, 394 insertions(+), 6 deletions(-) create mode 100644 jquery.sketchable.animate.js create mode 100644 sketchable.animate.js diff --git a/Gruntfile.js b/Gruntfile.js index fd018ce..8f228c9 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -11,6 +11,7 @@ module.exports = function(grunt) { 'sketchable.utils.js', 'sketchable.js', 'sketchable.memento.js', + 'sketchable.animate.js', ], dest: 'dist/sketchable.full.js' }, @@ -19,6 +20,7 @@ module.exports = function(grunt) { 'jsketch.js', 'jquery.sketchable.js', 'jquery.sketchable.memento.js', + 'jquery.sketchable.animate.js', ], dest: 'dist/jquery.sketchable.full.js' } @@ -40,6 +42,11 @@ module.exports = function(grunt) { 'dist/sketchable.memento.min.js': [ 'sketchable.memento.js' ] } }, + animate: { + files: { + 'dist/sketchable.animate.min.js': [ 'sketchable.animate.js' ] + } + }, full: { options: { banner: '/*! <%= pkg.description %> (all in one) | v<%= pkg.version %> | <%= grunt.template.today("yyyy-mm-dd") %> */\n' @@ -58,6 +65,11 @@ module.exports = function(grunt) { 'dist/jquery.sketchable.memento.min.js': [ 'jquery.sketchable.memento.js' ] } }, + jqAnimate: { + files: { + 'dist/jquery.sketchable.animate.min.js': [ 'jquery.sketchable.animate.js' ] + } + }, jqFull: { options: { banner: '/*! <%= pkg.description %> (all in one) | v<%= pkg.version %> | <%= grunt.template.today("yyyy-mm-dd") %> */\n' @@ -84,11 +96,13 @@ module.exports = function(grunt) { grunt.registerTask('jsketch', [ 'uglify:jsketch', 'clean' ]); grunt.registerTask('sketchable', [ 'uglify:sketchable', 'uglify:jqSketchable', 'clean' ]); grunt.registerTask('memento', [ 'uglify:memento', 'uglify:jqMemento', 'clean' ]); + grunt.registerTask('animate', [ 'uglify:animate', 'uglify:jqAnimate', 'clean' ]); grunt.registerTask('default', [ 'full', 'jsketch', 'sketchable', 'memento', + 'animate', ]); }; diff --git a/dist/jquery.sketchable.full.min.js b/dist/jquery.sketchable.full.min.js index 32dffe2..383d4e6 100644 --- a/dist/jquery.sketchable.full.min.js +++ b/dist/jquery.sketchable.full.min.js @@ -1,2 +1,2 @@ -/*! jSketch drawing lib (all in one) | v2.0.0 | 2017-11-25 */ -!function(a){function b(a,b){if(!a)throw new Error("Sketchable requires a DOM element.");return"string"==typeof a&&(a=document.querySelector(a)),this.setContext(a),this.stageWidth=a.width,this.stageHeight=a.height,this.data=b,this.setDefaults()}b.prototype={setContext:function(a){if(!a)throw new Error("No canvas element specified.");return this.canvas=a,this.context=a.getContext("2d"),this},setDefaults:function(){return this.saveGraphics({fillStyle:this.data.fillStyle||"#F00",strokeStyle:this.data.strokeStyle||"#F0F",lineWidth:this.data.lineWidth||2,lineCap:this.data.lineCap||"round",lineJoin:this.data.lineJoin||"round",miterLimit:this.data.miterLimit||10}).restoreGraphics()},size:function(a,b){return this.stageWidth=a,this.stageHeight=b,this.canvas.width=a,this.canvas.height=b,this.restoreGraphics(),this},background:function(a){return this.beginFill(a),this.context.fillRect(0,0,this.stageWidth,this.stageHeight),this.endFill(),this},stage:function(a,b,c){return this.size(a,b).background(c),this},beginFill:function(a){return this.saveGraphics(),this.context.fillStyle=a,this},endFill:function(){return this.restoreGraphics(),this},lineStyle:function(a,b,c,d,e){return this.saveGraphics({strokeStyle:a||this.data.strokeStyle,lineWidth:b||this.data.lineWidth,lineCap:c||this.data.lineCap,lineJoin:d||this.data.lineJoin,miterLimit:e||this.data.miterLimit}).restoreGraphics()},moveTo:function(a,b){return this.context.moveTo(a,b),this},lineTo:function(a,b){return this.context.lineTo(a,b),this},line:function(a,b,c,d){return this.context.moveTo(a,b),this.lineTo(c,d),this},curveTo:function(a,b,c,d){return this.context.quadraticCurveTo(c,d,a,b),this},curve:function(a,b,c,d,e,f){return this.context.moveTo(a,b),this.curveTo(c,d,e,f),this},stroke:function(){return this.context.stroke(),this},strokeRect:function(a,b,c,d){return this.context.beginPath(),this.context.strokeRect(a,b,c,d),this.context.closePath(),this},fillRect:function(a,b,c,d){return this.context.beginPath(),this.context.fillRect(a,b,c,d),this.context.closePath(),this},strokeCircle:function(a,b,c){return this.context.beginPath(),this.context.arc(a,b,c,0,2*Math.PI,!1),this.context.stroke(),this.context.closePath(),this},fillCircle:function(a,b,c){return this.context.beginPath(),this.context.arc(a,b,c,0,2*Math.PI,!1),this.context.fill(),this.context.closePath(),this},radialCircle:function(a,b,c,d,e){("undefined"==typeof d||0>d)&&(d=1);var f=this.context.createRadialGradient(a,b,c,a,b,d);e&&"array"===e.constructor.name.toLowerCase()||(e=[this.context.fillStyle,"white"]);for(var g=0;g1){var f=d.length-1,g=d[f],h=d[f-1];g[0]==h[0]&&g[1]==h[1]&&d.splice(f,1)}}function f(a){return a.originalEvent.touches?!1:void l(a)}function g(a){return a.originalEvent.touches?!1:void m(a)}function h(a){return a.originalEvent.touches?!1:void n(a)}function i(a){o(a,l),a.preventDefault()}function j(a){o(a,m),a.preventDefault()}function k(a){o(a,n),a.preventDefault()}function l(b){if(3===b.which)return!1;var c=b.identifier||0,f=a(b.target),g=f.data(p),h=g.options;if(h.interactive){var i=d(b);"function"==typeof h.events.mousedownBefore&&h.events.mousedownBefore(f,g,b),h.graphics.firstPointSize>0&&g.sketch.beginFill(h.graphics.fillStyle).fillCircle(i.x,i.y,h.graphics.firstPointSize).endFill(),g.sketch.isDrawing=!0,g.sketch.beginPath();var j=g.coords[c];j||(j=[]),j.length>0&&g.strokes.push(j),g.coords[c]=[],e(c,g,i),"function"==typeof h.events.mousedown&&h.events.mousedown(f,g,b)}}function m(b){var c=b.identifier||0,f=a(b.target),g=f.data(p),h=g.options;if(h.interactive&&(h.mouseupMovements&&0!==g.strokes.length||g.sketch.isDrawing)){var i=d(b);"function"==typeof h.events.mousemoveBefore&&h.events.mousemoveBefore(f,g,b);var j=g.coords[c],k=j[j.length-1];if(k){var l=g.sketch;g.sketch.isDrawing?l.lineStyle(h.graphics.strokeStyle,h.graphics.lineWidth):h.mouseupMovements.visible!==!1&&l.lineStyle(h.mouseupMovements.strokeStyle||"#DDD",h.mouseupMovements.lineWidth||1),l.line(k[0],k[1],i.x,i.y).stroke()}e(c,g,i),"function"==typeof h.events.mousemove&&h.events.mousemove(f,g,b)}}function n(b){var c=b.identifier||0,d=a(b.target),e=d.data(p),f=e.options;f.interactive&&("function"==typeof f.events.mouseupBefore&&f.events.mouseupBefore(d,e,b),e.sketch.isDrawing=!1,e.sketch.closePath(),e.strokes.push(e.coords[c]),e.coords[c]=[],"function"==typeof f.events.mouseup&&f.events.mouseup(d,e,b))}function o(b,c){var d=a(b.target),e=d.data(p),f=e.options;if(f.multitouch)for(var g=b.originalEvent.changedTouches,h=0;h-1){var e=c(q,b);return e.apply(this,d)}return q[b]?q[b].apply(this,d):(a.error("Unknown method: "+b),this)},a.fn.sketchable.api=q,a.fn.sketchable.plugins={},a.fn.sketchable.defaults={interactive:!0,mouseupMovements:!1,relTimestamps:!1,multitouch:!0,cssCursors:!0,filterCoords:!1,events:{},graphics:{firstPointSize:3,lineWidth:3,strokeStyle:"#F0F",fillStyle:"#F0F",lineCap:"round",lineJoin:"round",miterLimit:10}}}(jQuery),function(a){function b(b){function c(a,c){b.sketchable("handler",function(b,d){d.sketch.clear(),d.sketch.context.drawImage(a,0,0),d.strokes=c.slice()})}function d(a){if(a.ctrlKey)switch(a.which){case 26:a.shiftKey?g.redo():g.undo();break;case 25:g.redo()}}var e=[],f=-1,g=this;this.undo=function(){return f>0&&(f--,this.restore()),this},this.redo=function(){return f0?e[f].strokes=c.strokes.slice():(e.push({image:b[0].toDataURL(),strokes:c.strokes.slice()}),f++)}),this},this.state=function(){return JSON.parse(JSON.stringify(e[f]))},this.restore=function(a){a||(a=e[f]);var b=new Image;b.src=a.image,b.onload=function(){c(this,a.strokes)}},this.init=function(){return a(document).off("keypress",d),a(document).on("keypress",d),this.save()},this.destroy=function(){return a(document).off("keypress",d),this.reset()}}var c="sketchable";a.fn.sketchable.plugins.memento=function(d){function e(a){if(!f.options["_bound$"+a])if(f.options["_bound$"+a]=!0,f.options.events&&"function"==typeof f.options.events[a]){var b=f.options.events[a];f.options.events[a]=function(){b.apply(d,arguments),g[a].apply(d,arguments)}}else f.options.events[a]=g[a]}for(var f=d.sketchable("config"),g={clear:function(a,b){b.memento.reset()},mouseup:function(a,b,c){b.memento.save(c)},destroy:function(a,b){b.memento.destroy()}},h="mouseup clear destroy".split(" "),i=0;id)&&(d=1);var f=this.context.createRadialGradient(a,b,c,a,b,d);e&&"array"===e.constructor.name.toLowerCase()||(e=[this.context.fillStyle,"white"]);for(var g=0;g1){var f=d.length-1,g=d[f],h=d[f-1];g[0]==h[0]&&g[1]==h[1]&&d.splice(f,1)}}function f(a){return a.originalEvent.touches?!1:void l(a)}function g(a){return a.originalEvent.touches?!1:void m(a)}function h(a){return a.originalEvent.touches?!1:void n(a)}function i(a){o(a,l),a.preventDefault()}function j(a){o(a,m),a.preventDefault()}function k(a){o(a,n),a.preventDefault()}function l(b){if(3===b.which)return!1;var c=b.identifier||0,f=a(b.target),g=f.data(p),h=g.options;if(h.interactive){var i=d(b);"function"==typeof h.events.mousedownBefore&&h.events.mousedownBefore(f,g,b),h.graphics.firstPointSize>0&&g.sketch.beginFill(h.graphics.fillStyle).fillCircle(i.x,i.y,h.graphics.firstPointSize).endFill(),g.sketch.isDrawing=!0,g.sketch.beginPath();var j=g.coords[c];j||(j=[]),j.length>0&&g.strokes.push(j),g.coords[c]=[],e(c,g,i),"function"==typeof h.events.mousedown&&h.events.mousedown(f,g,b)}}function m(b){var c=b.identifier||0,f=a(b.target),g=f.data(p),h=g.options;if(h.interactive&&(h.mouseupMovements&&0!==g.strokes.length||g.sketch.isDrawing)){var i=d(b);"function"==typeof h.events.mousemoveBefore&&h.events.mousemoveBefore(f,g,b);var j=g.coords[c],k=j[j.length-1];if(k){var l=g.sketch;g.sketch.isDrawing?l.lineStyle(h.graphics.strokeStyle,h.graphics.lineWidth):h.mouseupMovements.visible!==!1&&l.lineStyle(h.mouseupMovements.strokeStyle||"#DDD",h.mouseupMovements.lineWidth||1),l.line(k[0],k[1],i.x,i.y).stroke()}e(c,g,i),"function"==typeof h.events.mousemove&&h.events.mousemove(f,g,b)}}function n(b){var c=b.identifier||0,d=a(b.target),e=d.data(p),f=e.options;f.interactive&&("function"==typeof f.events.mouseupBefore&&f.events.mouseupBefore(d,e,b),e.sketch.isDrawing=!1,e.sketch.closePath(),e.strokes.push(e.coords[c]),e.coords[c]=[],"function"==typeof f.events.mouseup&&f.events.mouseup(d,e,b))}function o(b,c){var d=a(b.target),e=d.data(p),f=e.options;if(f.multitouch)for(var g=b.originalEvent.changedTouches,h=0;h-1){var e=c(q,b);return e.apply(this,d)}return q[b]?q[b].apply(this,d):(a.error("Unknown method: "+b),this)},a.fn.sketchable.api=q,a.fn.sketchable.plugins={},a.fn.sketchable.defaults={interactive:!0,mouseupMovements:!1,relTimestamps:!1,multitouch:!0,cssCursors:!0,filterCoords:!1,events:{},graphics:{firstPointSize:3,lineWidth:3,strokeStyle:"#F0F",fillStyle:"#F0F",lineCap:"round",lineJoin:"round",miterLimit:10}}}(jQuery),function(a){function b(b){function c(a,c){b.sketchable("handler",function(b,d){d.sketch.clear(),d.sketch.context.drawImage(a,0,0),d.strokes=c.slice()})}function d(a){if(a.ctrlKey)switch(a.which){case 26:a.shiftKey?g.redo():g.undo();break;case 25:g.redo()}}var e=[],f=-1,g=this;this.undo=function(){return f>0&&(f--,this.restore()),this},this.redo=function(){return f0?e[f].strokes=c.strokes.slice():(e.push({image:b[0].toDataURL(),strokes:c.strokes.slice()}),f++)}),this},this.state=function(){return JSON.parse(JSON.stringify(e[f]))},this.restore=function(a){a||(a=e[f]);var b=new Image;b.src=a.image,b.onload=function(){c(this,a.strokes)}},this.init=function(){return a(document).off("keypress",d),a(document).on("keypress",d),this.save()},this.destroy=function(){return a(document).off("keypress",d),this.reset()}}var c="sketchable";a.fn.sketchable.plugins.memento=function(d){function e(a){if(!f.options["_bound.memento$"+a])if(f.options["_bound.memento$"+a]=!0,f.options.events&&"function"==typeof f.options.events[a]){var b=f.options.events[a];f.options.events[a]=function(){b.apply(d,arguments),g[a].apply(d,arguments)}}else f.options.events[a]=g[a]}for(var f=d.sketchable("config"),g={clear:function(a,b){b.memento.reset()},mouseup:function(a,b,c){b.memento.save(c)},destroy:function(a,b){b.memento.destroy()}},h="mouseup clear destroy".split(" "),i=0;i1?f:e;a.beginFill(a.data.strokeStyle).fillCircle(g.x,g.y,a.data.firstPointSize)}a.lineStyle(d.strokeStyle,d.lineWidth).beginPath(),f.strokeId===e.strokeId&&a.line(e.x,e.y,f.x,f.y).stroke(),a.closePath()}function d(a){return a instanceof Array?{x:a[0],y:a[1],t:a[2]}:a}for(var e=a.data(c),f=e.sketch,g=e.strokes,h=e.options.events,i=e.options.graphics,j=[],k=0;k0&&(f--,this.restore()),this},this.redo=function(){return f0?e[f].strokes=c.strokes.slice():(e.push({image:b[0].toDataURL(),strokes:c.strokes.slice()}),f++)}),this},this.state=function(){return JSON.parse(JSON.stringify(e[f]))},this.restore=function(a){a||(a=e[f]);var b=new Image;b.src=a.image,b.onload=function(){c(this,a.strokes)}},this.init=function(){return a(document).off("keypress",d),a(document).on("keypress",d),this.save()},this.destroy=function(){return a(document).off("keypress",d),this.reset()}}var c="sketchable";a.fn.sketchable.plugins.memento=function(d){function e(a){if(!f.options["_bound$"+a])if(f.options["_bound$"+a]=!0,f.options.events&&"function"==typeof f.options.events[a]){var b=f.options.events[a];f.options.events[a]=function(){b.apply(d,arguments),g[a].apply(d,arguments)}}else f.options.events[a]=g[a]}for(var f=d.sketchable("config"),g={clear:function(a,b){b.memento.reset()},mouseup:function(a,b,c){b.memento.save(c)},destroy:function(a,b){b.memento.destroy()}},h="mouseup clear destroy".split(" "),i=0;i0&&(f--,this.restore()),this},this.redo=function(){return f0?e[f].strokes=c.strokes.slice():(e.push({image:b[0].toDataURL(),strokes:c.strokes.slice()}),f++)}),this},this.state=function(){return JSON.parse(JSON.stringify(e[f]))},this.restore=function(a){a||(a=e[f]);var b=new Image;b.src=a.image,b.onload=function(){c(this,a.strokes)}},this.init=function(){return a(document).off("keypress",d),a(document).on("keypress",d),this.save()},this.destroy=function(){return a(document).off("keypress",d),this.reset()}}var c="sketchable";a.fn.sketchable.plugins.memento=function(d){function e(a){if(!f.options["_bound.memento$"+a])if(f.options["_bound.memento$"+a]=!0,f.options.events&&"function"==typeof f.options.events[a]){var b=f.options.events[a];f.options.events[a]=function(){b.apply(d,arguments),g[a].apply(d,arguments)}}else f.options.events[a]=g[a]}for(var f=d.sketchable("config"),g={clear:function(a,b){b.memento.reset()},mouseup:function(a,b,c){b.memento.save(c)},destroy:function(a,b){b.memento.destroy()}},h="mouseup clear destroy".split(" "),i=0;id)&&(d=1);var f=this.context.createRadialGradient(a,b,c,a,b,d);e&&"array"===e.constructor.name.toLowerCase()||(e=[this.context.fillStyle,"white"]);for(var g=0;g1){var f=d.length-1,g=d[f],h=d[f-1];g[0]==h[0]&&g[1]==h[1]&&d.splice(f,1)}}function g(a){return a.touches?!1:void m(a)}function h(a){return a.touches?!1:void n(a)}function i(a){return a.touches?!1:void o(a)}function j(a){p(a,m),a.preventDefault()}function k(a){p(a,n),a.preventDefault()}function l(a){p(a,o),a.preventDefault()}function m(a){if(Event.isRightClick(a))return!1;var b=a.identifier||0,c=a.target,d=dataBind(c)[q],g=d.options;if(g.interactive){var h=e(a);"function"==typeof g.events.mousedownBefore&&g.events.mousedownBefore(c,d,a),g.graphics.firstPointSize>0&&d.sketch.beginFill(g.graphics.fillStyle).fillCircle(h.x,h.y,g.graphics.firstPointSize).endFill(),d.sketch.isDrawing=!0,d.sketch.beginPath();var i=d.coords[b];i||(i=[]),i.length>0&&d.strokes.push(i),d.coords[b]=[],f(b,d,h),"function"==typeof g.events.mousedown&&g.events.mousedown(c,d,a)}}function n(a){var b=a.identifier||0,c=a.target,d=dataBind(c)[q],g=d.options;if(g.interactive&&(g.mouseupMovements&&0!==d.strokes.length||d.sketch.isDrawing)){var h=e(a);"function"==typeof g.events.mousemoveBefore&&g.events.mousemoveBefore(c,d,a);var i=d.coords[b],j=i[i.length-1];if(j){var k=d.sketch;d.sketch.isDrawing?k.lineStyle(g.graphics.strokeStyle,g.graphics.lineWidth):g.mouseupMovements.visible!==!1&&k.lineStyle(g.mouseupMovements.strokeStyle||"#DDD",g.mouseupMovements.lineWidth||1),k.line(j[0],j[1],h.x,h.y).stroke()}f(b,d,h),"function"==typeof g.events.mousemove&&g.events.mousemove(c,d,a)}}function o(a){var b=a.identifier||0,c=a.target,d=dataBind(c)[q],e=d.options;e.interactive&&("function"==typeof e.events.mouseupBefore&&e.events.mouseupBefore(c,d,a),d.sketch.isDrawing=!1,d.sketch.closePath(),d.strokes.push(d.coords[b]),d.coords[b]=[],"function"==typeof e.events.mouseup&&e.events.mouseup(c,d,a))}function p(a,b){var c=a.target,d=dataBind(c)[q],e=d.options;if(e.multitouch)for(var f=a.changedTouches,g=0;g0&&(e--,this.restore()),this},this.redo=function(){return e0?d[e].strokes=c.strokes.slice():(d.push({image:a.toDataURL(),strokes:c.strokes.slice()}),e++)}),this},this.state=function(){return JSON.parse(JSON.stringify(d[e]))},this.restore=function(a){a||(a=d[e]);var c=new Image;c.src=a.image,c.onload=function(){b(this,a.strokes)}},this.init=function(){return Event.remove(document,"keypress",c),Event.add(document,"keypress",c),this.save()},this.destroy=function(){return Event.remove(document,"keypress",c),this.reset()}}var c="sketchable";Sketchable.prototype.plugins.memento=function(a){function d(b){if(!e.options["_bound$"+b])if(e.options["_bound$"+b]=!0,e.options.events&&"function"==typeof e.options.events[b]){var c=e.options.events[b];e.options.events[b]=function(){c.apply(a,arguments),f[b].apply(a,arguments)}}else e.options.events[b]=f[b]}for(var e=a.config(),f={clear:function(a,b){b.memento.reset()},mouseup:function(a,b,c){b.memento.save(c)},destroy:function(a,b){b.memento.destroy()}},g="mouseup clear destroy".split(" "),h=0;hd)&&(d=1);var f=this.context.createRadialGradient(a,b,c,a,b,d);e&&"array"===e.constructor.name.toLowerCase()||(e=[this.context.fillStyle,"white"]);for(var g=0;g1){var f=d.length-1,g=d[f],h=d[f-1];g[0]==h[0]&&g[1]==h[1]&&d.splice(f,1)}}function g(a){return a.touches?!1:void m(a)}function h(a){return a.touches?!1:void n(a)}function i(a){return a.touches?!1:void o(a)}function j(a){p(a,m),a.preventDefault()}function k(a){p(a,n),a.preventDefault()}function l(a){p(a,o),a.preventDefault()}function m(a){if(Event.isRightClick(a))return!1;var b=a.identifier||0,c=a.target,d=dataBind(c)[q],g=d.options;if(g.interactive){var h=e(a);"function"==typeof g.events.mousedownBefore&&g.events.mousedownBefore(c,d,a),g.graphics.firstPointSize>0&&d.sketch.beginFill(g.graphics.fillStyle).fillCircle(h.x,h.y,g.graphics.firstPointSize).endFill(),d.sketch.isDrawing=!0,d.sketch.beginPath();var i=d.coords[b];i||(i=[]),i.length>0&&d.strokes.push(i),d.coords[b]=[],f(b,d,h),"function"==typeof g.events.mousedown&&g.events.mousedown(c,d,a)}}function n(a){var b=a.identifier||0,c=a.target,d=dataBind(c)[q],g=d.options;if(g.interactive&&(g.mouseupMovements&&0!==d.strokes.length||d.sketch.isDrawing)){var h=e(a);"function"==typeof g.events.mousemoveBefore&&g.events.mousemoveBefore(c,d,a);var i=d.coords[b],j=i[i.length-1];if(j){var k=d.sketch;d.sketch.isDrawing?k.lineStyle(g.graphics.strokeStyle,g.graphics.lineWidth):g.mouseupMovements.visible!==!1&&k.lineStyle(g.mouseupMovements.strokeStyle||"#DDD",g.mouseupMovements.lineWidth||1),k.line(j[0],j[1],h.x,h.y).stroke()}f(b,d,h),"function"==typeof g.events.mousemove&&g.events.mousemove(c,d,a)}}function o(a){var b=a.identifier||0,c=a.target,d=dataBind(c)[q],e=d.options;e.interactive&&("function"==typeof e.events.mouseupBefore&&e.events.mouseupBefore(c,d,a),d.sketch.isDrawing=!1,d.sketch.closePath(),d.strokes.push(d.coords[b]),d.coords[b]=[],"function"==typeof e.events.mouseup&&e.events.mouseup(c,d,a))}function p(a,b){var c=a.target,d=dataBind(c)[q],e=d.options;if(e.multitouch)for(var f=a.changedTouches,g=0;g0&&(e--,this.restore()),this},this.redo=function(){return e0?d[e].strokes=c.strokes.slice():(d.push({image:a.toDataURL(),strokes:c.strokes.slice()}),e++)}),this},this.state=function(){return JSON.parse(JSON.stringify(d[e]))},this.restore=function(a){a||(a=d[e]);var c=new Image;c.src=a.image,c.onload=function(){b(this,a.strokes)}},this.init=function(){return Event.remove(document,"keypress",c),Event.add(document,"keypress",c),this.save()},this.destroy=function(){return Event.remove(document,"keypress",c),this.reset()}}var c="sketchable";Sketchable.prototype.plugins.memento=function(a){function d(b){if(!e.options["_bound.memento$"+b])if(e.options["_bound.memento$"+b]=!0,e.options.events&&"function"==typeof e.options.events[b]){var c=e.options.events[b];e.options.events[b]=function(){c.apply(a,arguments),f[b].apply(a,arguments)}}else e.options.events[b]=f[b]}for(var e=a.config(),f={clear:function(a,b){b.memento.reset()},mouseup:function(a,b,c){b.memento.save(c)},destroy:function(a,b){b.memento.destroy()}},g="mouseup clear destroy".split(" "),h=0;h1?f:e;a.beginFill(a.data.strokeStyle).fillCircle(g.x,g.y,a.data.firstPointSize)}a.lineStyle(d.strokeStyle,d.lineWidth).beginPath(),f.strokeId===e.strokeId&&a.line(e.x,e.y,f.x,f.y).stroke(),a.closePath()}function d(a){return a instanceof Array?{x:a[0],y:a[1],t:a[2]}:a}for(var e=dataBind(a.elem)[c],f=e.sketch,g=e.strokes,h=e.options.events,i=e.options.graphics,j=[],k=0;k0&&(e--,this.restore()),this},this.redo=function(){return e0?d[e].strokes=c.strokes.slice():(d.push({image:a.toDataURL(),strokes:c.strokes.slice()}),e++)}),this},this.state=function(){return JSON.parse(JSON.stringify(d[e]))},this.restore=function(a){a||(a=d[e]);var c=new Image;c.src=a.image,c.onload=function(){b(this,a.strokes)}},this.init=function(){return Event.remove(document,"keypress",c),Event.add(document,"keypress",c),this.save()},this.destroy=function(){return Event.remove(document,"keypress",c),this.reset()}}var c="sketchable";Sketchable.prototype.plugins.memento=function(a){function d(b){if(!e.options["_bound$"+b])if(e.options["_bound$"+b]=!0,e.options.events&&"function"==typeof e.options.events[b]){var c=e.options.events[b];e.options.events[b]=function(){c.apply(a,arguments),f[b].apply(a,arguments)}}else e.options.events[b]=f[b]}for(var e=a.config(),f={clear:function(a,b){b.memento.reset()},mouseup:function(a,b,c){b.memento.save(c)},destroy:function(a,b){b.memento.destroy()}},g="mouseup clear destroy".split(" "),h=0;h0&&(e--,this.restore()),this},this.redo=function(){return e0?d[e].strokes=c.strokes.slice():(d.push({image:a.toDataURL(),strokes:c.strokes.slice()}),e++)}),this},this.state=function(){return JSON.parse(JSON.stringify(d[e]))},this.restore=function(a){a||(a=d[e]);var c=new Image;c.src=a.image,c.onload=function(){b(this,a.strokes)}},this.init=function(){return Event.remove(document,"keypress",c),Event.add(document,"keypress",c),this.save()},this.destroy=function(){return Event.remove(document,"keypress",c),this.reset()}}var c="sketchable";Sketchable.prototype.plugins.memento=function(a){function d(b){if(!e.options["_bound.memento$"+b])if(e.options["_bound.memento$"+b]=!0,e.options.events&&"function"==typeof e.options.events[b]){var c=e.options.events[b];e.options.events[b]=function(){c.apply(a,arguments),f[b].apply(a,arguments)}}else e.options.events[b]=f[b]}for(var e=a.config(),f={clear:function(a,b){b.memento.reset()},mouseup:function(a,b,c){b.memento.save(c)},destroy:function(a,b){b.memento.destroy()}},g="mouseup clear destroy".split(" "),h=0;h 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); diff --git a/sketchable.animate.js b/sketchable.animate.js new file mode 100644 index 0000000..93ef31e --- /dev/null +++ b/sketchable.animate.js @@ -0,0 +1,188 @@ +/*! + * Animation plugin for Sketchable | v1.0 | Luis A. Leiva | MIT license + */ + +// XXX: Requires `sketchable.utils.js` to be loaded first. + +/* eslint-env browser */ +/* global Event, dataBind, deepExtend */ +;(function(window) { + + // Custom namespace ID, for private data bindind. + var namespace = 'sketchable'; + + /** + * Brings animation capabilities to Sketchable elements. + * @class + * @version 1.0 + * @param {Sketchable} instance Sketchable element. + */ + function AnimateSketch(instance) { + var self = this; + var data = dataBind(instance.elem)[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.elem, 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.elem, 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 {Sketchable} instance Sketchable element. + * @memberof Sketchable#plugins + */ + Sketchable.prototype.plugins.animate = function(instance) { + // Access the instance configuration. + var config = instance.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 Sketchable instances will have these methods. + deepExtend(instance, { + // Namespace methods to avoid collisions with other plugins. + animate: { + /** + * Animate canvas strokes. + * @return {AnimateSketch} + * @memberof Sketchable + * @example sketchableInstance.strokes(strokeArray).animate.strokes(); + * @example + * // Accessing event hooks: + * sketchableInstance.config({ + * events: { + * animationstart: function(elem, data) { + * // Animation started. + * }, + * animationend: function(elem, data) { + * // Animation finished. + * }, + * } + * }) + * .strokes(strokeArray) + * .animate.strokes(); + */ + strokes: function() { + var data = dataBind(instance.elem)[namespace]; + data.animate = new AnimateSketch(instance); + return instance; + }, + }, + }); + + }; + +})(this);