Last active
December 19, 2015 23:09
-
-
Save karlwhite/6032693 to your computer and use it in GitHub Desktop.
Sketch.js: Added screen buffer to improve performance on mobile (tested on iPad) Known issue: On iPad there is a slight, but noticeable, screen flash as it copies the canvas to the buffer. Usage: Simple pass {buffer:true} as an option when initializing, e.g. canvas.sketch({buffer:true});
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var __slice = Array.prototype.slice; | |
(function($) { | |
var Sketch; | |
$.fn.sketch = function() { | |
var args, key, sketch; | |
key = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : []; | |
if (this.length > 1) { | |
$.error('Sketch.js can only be called on one element at a time.'); | |
} | |
sketch = this.data('sketch'); | |
if (typeof key === 'string' && sketch) { | |
if (sketch[key]) { | |
if (typeof sketch[key] === 'function') { | |
return sketch[key].apply(sketch, args); | |
} else if (args.length === 0) { | |
return sketch[key]; | |
} else if (args.length === 1) { | |
return sketch[key] = args[0]; | |
} | |
} else { | |
return $.error('Sketch.js did not recognize the given command.'); | |
} | |
} else if (sketch) { | |
return sketch; | |
} else { | |
this.data('sketch', new Sketch(this.get(0), key)); | |
return this; | |
} | |
}; | |
Sketch = (function() { | |
function Sketch(el, opts) { | |
this.el = el; | |
this.canvas = $(el); | |
this.context = el.getContext('2d'); | |
this.options = $.extend({ | |
buffer: false, | |
toolLinks: true, | |
defaultTool: 'marker', | |
defaultColor: '#000000', | |
defaultSize: 5 | |
}, opts); | |
this.painting = false; | |
this.color = this.options.defaultColor; | |
this.size = this.options.defaultSize; | |
this.tool = this.options.defaultTool; | |
this.actions = []; | |
this.action = []; | |
this.canvas.bind('click mousedown mouseup mousemove mouseleave mouseout touchstart touchmove touchend touchcancel', this.onEvent); | |
if (this.options.toolLinks) { | |
$('body').delegate("a[href=\"#" + (this.canvas.attr('id')) + "\"]", 'click', function(e) { | |
var $canvas, $this, key, sketch, _i, _len, _ref; | |
$this = $(this); | |
$canvas = $($this.attr('href')); | |
sketch = $canvas.data('sketch'); | |
_ref = ['color', 'size', 'tool']; | |
for (_i = 0, _len = _ref.length; _i < _len; _i++) { | |
key = _ref[_i]; | |
if ($this.attr("data-" + key)) { | |
if ( sketch ) sketch.set(key, $(this).attr("data-" + key)); | |
} | |
} | |
if ($(this).attr('data-download')) { | |
if ( sketch ) sketch.download($(this).attr('data-download')); | |
} | |
if ($(this).attr('data-clear')) { | |
if ( sketch ) sketch.clear(); | |
} | |
return false; | |
}); | |
} | |
if (this.options.buffer) { | |
this.buffer = document.createElement('img'); | |
this.buffer.src = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=='; | |
} | |
} | |
Sketch.prototype.download = function(format) { | |
var mime; | |
format || (format = "png"); | |
if (format === "jpg") { | |
format = "jpeg"; | |
} | |
mime = "image/" + format; | |
return window.open(this.el.toDataURL(mime)); | |
}; | |
Sketch.prototype.clear = function() { | |
this.actions = []; | |
this.action = []; | |
if ( this.buffer ) { | |
this.buffer.src = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=='; | |
} | |
this.redraw(); | |
}; | |
Sketch.prototype.set = function(key, value) { | |
this[key] = value; | |
return this.canvas.trigger("sketch.change" + key, value); | |
}; | |
Sketch.prototype.startPainting = function() { | |
if (this.painting) { | |
this.stopPainting(); | |
} | |
this.painting = true; | |
return this.action = { | |
tool: this.tool, | |
color: this.color, | |
size: parseFloat(this.size), | |
events: [] | |
}; | |
}; | |
Sketch.prototype.stopPainting = function() { | |
if ( this.painting ) { | |
if (this.action) { | |
this.actions.push(this.action); | |
} | |
this.painting = false; | |
this.action = null; | |
var ret = this.redraw(); | |
// Periodically update buffer, and clear out old actions | |
if ( this.buffer && this.actions.length > 10 ) { | |
var bufferImage = this.el.toDataURL("image/png").replace("image/png", "image/octet-stream"); | |
this.buffer.src = bufferImage; | |
this.actions = []; | |
this.buffer.width = this.el.width; | |
this.buffer.height = this.el.height; | |
this.context.drawImage( this.buffer, 0, 0, this.buffer.width, this.buffer.height, 0, 0, this.el.width, this.el.height ); | |
} | |
return ret; | |
} else { | |
var ret = this.redraw(); | |
} | |
}; | |
Sketch.prototype.onEvent = function(e) { | |
if (e.originalEvent && e.originalEvent.targetTouches) { | |
e.pageX = e.originalEvent.targetTouches[0].pageX; | |
e.pageY = e.originalEvent.targetTouches[0].pageY; | |
} | |
$.sketch.tools[$(this).data('sketch').tool].onEvent.call($(this).data('sketch'), e); | |
e.preventDefault(); | |
return false; | |
}; | |
Sketch.prototype.redraw = function() { | |
var sketch; | |
this.el.width = this.canvas.width(); | |
this.el.height = this.canvas.height(); | |
this.context = this.el.getContext('2d'); | |
sketch = this; | |
// Render existing buffer | |
if ( this.buffer ) { | |
this.buffer.width = this.el.width; | |
this.buffer.height = this.el.height; | |
this.context.drawImage( this.buffer, 0, 0, this.buffer.width, this.buffer.height, 0, 0, this.el.width, this.el.height ); | |
} | |
$.each(this.actions, function() { | |
if (this.tool) { | |
return $.sketch.tools[this.tool].draw.call(sketch, this); | |
} | |
}); | |
if (this.painting && this.action) { | |
return $.sketch.tools[this.action.tool].draw.call(sketch, this.action); | |
} | |
}; | |
return Sketch; | |
})(); | |
$.sketch = { | |
tools: {} | |
}; | |
$.sketch.tools.marker = { | |
onEvent: function(e) { | |
switch (e.type) { | |
case 'mousedown': | |
case 'touchstart': | |
this.startPainting(); | |
break; | |
case 'mouseup': | |
case 'mouseout': | |
case 'mouseleave': | |
case 'touchend': | |
case 'touchcancel': | |
this.stopPainting(); | |
} | |
if (this.painting) { | |
this.action.events.push({ | |
x: e.pageX - this.canvas.offset().left, | |
y: e.pageY - this.canvas.offset().top, | |
event: e.type | |
}); | |
return this.redraw(); | |
} | |
}, | |
draw: function(action) { | |
var event, previous, _i, _len, _ref; | |
this.context.lineJoin = "round"; | |
this.context.lineCap = "round"; | |
this.context.beginPath(); | |
this.context.moveTo(action.events[0].x, action.events[0].y); | |
_ref = action.events; | |
for (_i = 0, _len = _ref.length; _i < _len; _i++) { | |
event = _ref[_i]; | |
this.context.lineTo(event.x, event.y); | |
previous = event; | |
} | |
this.context.strokeStyle = action.color; | |
this.context.lineWidth = action.size; | |
return this.context.stroke(); | |
} | |
}; | |
return $.sketch.tools.eraser = { | |
onEvent: function(e) { | |
return $.sketch.tools.marker.onEvent.call(this, e); | |
}, | |
draw: function(action) { | |
var oldcomposite; | |
oldcomposite = this.context.globalCompositeOperation; | |
this.context.globalCompositeOperation = "copy"; | |
action.color = "rgba(0,0,0,0)"; | |
$.sketch.tools.marker.draw.call(this, action); | |
return this.context.globalCompositeOperation = oldcomposite; | |
} | |
}; | |
})(jQuery); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment