Skip to content

Instantly share code, notes, and snippets.

@vitalyrotari
Last active December 22, 2017 16:01
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vitalyrotari/288b094e38e8e6af8a7c to your computer and use it in GitHub Desktop.
Save vitalyrotari/288b094e38e8e6af8a7c to your computer and use it in GitHub Desktop.
jQuery Eraser Plugin
/*
* Forked from: https://github.com/boblemarin/jQuery.eraser
*/
(function( $ ){
var $win = $(window);
var Eraser = function(element, options) {
this.$element = $(element);
this.options = options;
this.init_();
};
Eraser.prototype.init_ = function() {
if (this.initialized_) {
return;
}
var pos = this.$element.offset();
this.paths_ = [];
this.data = {
w: this.$element.width(),
h: this.$element.height(),
posX: pos.left,
posY: pos.top,
canvas: document.createElement('canvas'),
size: (this.options && this.options.size) ? this.options.size : 40,
complete: false,
completeRatio: (this.options && this.options.completeRatio) ? this.options.completeRatio : .7,
completeFunction: (this.options && this.options.completeFunction) ? this.options.completeFunction : null,
parts: [],
colParts: 0,
numParts: 0,
ratio: 0,
source: this.$element[0],
touchDown: false,
touchID: -999,
touchX: 0,
touchY: 0,
ptouchX: 0,
ptouchY: 0
};
this.data.ctx = this.data.canvas.getContext("2d");
this.$element.after(this.data.canvas);
var id = this.$element.attr('id')
, className = this.$element.attr('class');
if (id) {
this.data.canvas.id = this.$element.attr('id');
}
if (className) {
this.data.canvas.className = this.$element.attr('class');
}
this.drawCanvas();
this.$element.remove();
$(this.data.canvas)
.on('mousedown.eraser', $.proxy(this.mouseDown, this))
.on('touchstart.eraser', $.proxy(this.touchStart, this))
.on('touchmove.eraser', $.proxy(this.touchMove, this))
.on('touchend.eraser', $.proxy(this.touchEnd, this));
this.collectParts();
var resize = $.proxy(function() {
var $canvas = $(this.data.canvas)
, $parent = $canvas.parent()
, pos = $canvas.offset();
this.data.posX = pos.left;
this.data.posY = pos.top;
this.data.w = $parent.width();
this.data.h = $parent.height();
this.drawCanvas();
this.collectParts();
}, this);
$win.on('resize.eraser.redraw', resize);
$.data(this.data.canvas, 'Eraser', this);
this.initialized_ = true;
};
Eraser.prototype.collectParts = function() {
this.data.colParts = Math.floor(this.data.w / this.data.size);
this.data.numParts = this.data.colParts * Math.floor(this.data.h / this.data.size);
this.data.parts.length = 0;
var n = this.data.numParts;
while(n--) {
this.data.parts.push(1);
}
};
Eraser.prototype.drawCanvas = function() {
this.data.canvas.width = this.data.w;
this.data.canvas.height = this.data.h;
this.data.ctx.drawImage(this.data.source, 0, 0 , this.data.w, this.data.h);
this.data.ctx.globalCompositeOperation = "destination-out";
this.data.ctx.strokeStyle = 'rgba(255,0,0,255)';
this.data.ctx.lineWidth = this.data.size;
this.data.ctx.lineCap = "round";
if (this.paths_.length) {
var touchX = this.data.touchX
, touchY = this.data.touchY;
for (var i=0, item; i<this.paths_.length; i++) {
item = this.paths_[i];
this.data.touchX = item.x1;
this.data.touchY = item.y1;
this.drawLine(item.x2, item.y2, true);
}
this.data.touchX = touchX;
this.data.touchY = touchY;
}
};
Eraser.prototype.drawLine = function(tx, ty, skipPaths) {
var x1 = this.data.touchX-1
, y1 = this.data.touchY;
this.data.ctx.beginPath();
this.data.ctx.moveTo(x1, y1);
this.data.touchX = tx;
this.data.touchY = ty;
this.data.ctx.lineTo(this.data.touchX, this.data.touchY);
this.data.ctx.stroke();
if (!this.data.complete && !skipPaths) {
this.paths_.push({
x1: x1,
y1: y1,
x2: tx,
y2: ty
});
}
}
Eraser.prototype.touchStart = function(event) {
if (!this.data.touchDown ) {
var t = event.originalEvent.changedTouches[0]
, tx = t.pageX - this.data.posX
, ty = t.pageY - this.data.posY;
this.evaluatePoint(tx, ty);
this.data.touchDown = true;
this.data.touchID = t.identifier;
this.data.touchX = tx;
this.data.touchY = ty;
event.preventDefault();
}
};
Eraser.prototype.touchMove = function(event) {
if (this.data.touchDown) {
var ta = event.originalEvent.changedTouches
, n = ta.length;
while(n--) {
if (ta[n].identifier == this.data.touchID) {
var tx = ta[n].pageX - this.data.posX
, ty = ta[n].pageY - this.data.posY;
this.evaluatePoint(tx, ty);
this.drawLine(tx, ty);
event.preventDefault();
break;
}
}
}
};
Eraser.prototype.touchEnd = function(event) {
if (this.data.touchDown ) {
var ta = event.originalEvent.changedTouches
, n = ta.length;
while(n--) {
if (ta[n].identifier == this.data.touchID) {
this.data.touchDown = false;
event.preventDefault();
break;
}
}
}
};
Eraser.prototype.evaluatePoint = function(tx, ty) {
var p = Math.floor(tx/this.data.size) + Math.floor(ty / this.data.size) * this.data.colParts;
if (p >= 0 && p < this.data.numParts ) {
this.data.ratio += this.data.parts[p];
this.data.parts[p] = 0;
if (!this.data.complete) {
if (this.data.ratio/this.data.numParts >= this.data.completeRatio) {
this.data.complete = true;
if (this.data.completeFunction != null ) {
this.data.completeFunction();
}
}
}
}
};
Eraser.prototype.mouseDown = function(event) {
var tx = event.pageX - this.data.posX
, ty = event.pageY - this.data.posY;
this.data.touchDown = true;
this.data.touchX = tx;
this.data.touchY = ty;
this.evaluatePoint(tx, ty);
this.drawLine(tx, ty);
$(this.data.canvas).on('mousemove.eraser', $.proxy(this.mouseMove, this));
$(document).on('mouseup.eraser', $.proxy(this.mouseUp, this));
event.preventDefault();
};
Eraser.prototype.mouseMove = function(event) {
var tx = event.pageX - this.data.posX
, ty = event.pageY - this.data.posY;
this.evaluatePoint(tx, ty);
this.drawLine(tx, ty);
event.preventDefault();
};
Eraser.prototype.mouseUp = function(event) {
this.data.touchDown = false;
$(this.data.canvas).unbind('mousemove.eraser');
$(document).unbind('mouseup.eraser');
event.preventDefault();
};
Eraser.prototype.clear = function() {
this.data.ctx.clearRect(0, 0, this.data.w, this.data.h);
var n = this.data.numParts;
while(n--) {
this.data.parts[n] = 0;
}
this.data.ratio = this.data.numParts;
this.data.complete = true;
if (this.data.completeFunction != null) {
this.data.completeFunction();
}
};
Eraser.prototype.size = function(value) {
if (value) {
this.data.size = value;
this.data.ctx.lineWidth = value;
}
};
Eraser.prototype.reset = function() {
this.paths_.length = 0;
$win.trigger('resize.eraser.redraw');
this.data.ratio = 0;
this.data.complete = false;
};
$.fn.eraser = function(method) {
return this.each(function() {
var instance = $.data(this, 'Eraser');
if (!instance) {
var options = (typeof method === 'object') ? method : {};
new Eraser(this, options);
}
if (typeof method === 'string' ) {
if (typeof instance[method] === 'function') {
instance[method].apply(instance, arguments);
} else {
$.error('Method ' + method + ' does not yet exist on jQuery.eraser');
}
}
});
};
})( jQuery );
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment