1217 lines
108 KiB
JavaScript
1217 lines
108 KiB
JavaScript
|
|
/**
|
||
|
|
* fabric.brushes - A collection of brushes for fabric.js (version 4 and up).
|
||
|
|
*
|
||
|
|
* Made by Arjan Haverkamp, https://www.webgear.nl
|
||
|
|
* Copyright 2021 Arjan Haverkamp
|
||
|
|
* MIT Licensed
|
||
|
|
* @version 1.0 - 2021-06-02
|
||
|
|
* @url https://github.com/av01d/fabric-brushes
|
||
|
|
*
|
||
|
|
* Inspiration sources:
|
||
|
|
* - https://github.com/tennisonchan/fabric-brush
|
||
|
|
* - https://mrdoob.com/projects/harmony/
|
||
|
|
* - http://perfectionkills.com/exploring-canvas-drawing-techniques/
|
||
|
|
*/
|
||
|
|
|
||
|
|
(function(fabric) {
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Trim a canvas. Returns the left-top coordinate where trimming began.
|
||
|
|
* @param {canvas} canvas A canvas element to trim. This element will be trimmed (reference).
|
||
|
|
* @returns {Object} Left-top coordinate of trimmed area. Example: {x:65, y:104}
|
||
|
|
* @see: https://stackoverflow.com/a/22267731/3360038
|
||
|
|
*/
|
||
|
|
fabric.util.trimCanvas = function(canvas) {
|
||
|
|
var ctx = canvas.getContext('2d'),
|
||
|
|
w = canvas.width,
|
||
|
|
h = canvas.height,
|
||
|
|
pix = {x:[], y:[]}, n,
|
||
|
|
imageData = ctx.getImageData(0,0,w,h),
|
||
|
|
fn = function(a,b) { return a-b };
|
||
|
|
for (var y = 0; y < h; y++) {
|
||
|
|
for (var x = 0; x < w; x++) {
|
||
|
|
if (imageData.data[((y * w + x) * 4)+3] > 0) {
|
||
|
|
pix.x.push(x);
|
||
|
|
pix.y.push(y);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
pix.x.sort(fn);
|
||
|
|
pix.y.sort(fn);
|
||
|
|
n = pix.x.length-1;
|
||
|
|
//if (n == -1) {
|
||
|
|
// // Nothing to trim... empty canvas?
|
||
|
|
//}
|
||
|
|
|
||
|
|
w = pix.x[n] - pix.x[0];
|
||
|
|
h = pix.y[n] - pix.y[0];
|
||
|
|
if(!w){
|
||
|
|
return
|
||
|
|
}
|
||
|
|
var cut = ctx.getImageData(pix.x[0], pix.y[0], w, h);
|
||
|
|
|
||
|
|
canvas.width = w;
|
||
|
|
canvas.height = h;
|
||
|
|
ctx.putImageData(cut, 0, 0);
|
||
|
|
|
||
|
|
return {x:pix.x[0], y:pix.y[0]};
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Extract r,g,b,a components from any valid color.
|
||
|
|
* Returns {undefined} when color cannot be parsed.
|
||
|
|
*
|
||
|
|
* @param {number} color Any color string (named, hex, rgb, rgba)
|
||
|
|
* @returns {(Array|undefined)} Example: [0,128,255,1]
|
||
|
|
* @see https://gist.github.com/oriadam/396a4beaaad465ca921618f2f2444d49
|
||
|
|
*/
|
||
|
|
fabric.util.colorValues = function(color) {
|
||
|
|
if (!color) { return; }
|
||
|
|
if (color.toLowerCase() === 'transparent') { return [0, 0, 0, 0]; }
|
||
|
|
if (color[0] === '#') {
|
||
|
|
if (color.length < 7) {
|
||
|
|
// convert #RGB and #RGBA to #RRGGBB and #RRGGBBAA
|
||
|
|
color = '#' + color[1] + color[1] + color[2] + color[2] + color[3] + color[3] + (color.length > 4 ? color[4] + color[4] : '');
|
||
|
|
}
|
||
|
|
return [parseInt(color.substr(1, 2), 16),
|
||
|
|
parseInt(color.substr(3, 2), 16),
|
||
|
|
parseInt(color.substr(5, 2), 16),
|
||
|
|
color.length > 7 ? parseInt(color.substr(7, 2), 16)/255 : 1];
|
||
|
|
}
|
||
|
|
if (color.indexOf('rgb') === -1) {
|
||
|
|
// convert named colors
|
||
|
|
var tempElem = document.body.appendChild(document.createElement('fictum')); // intentionally use unknown tag to lower chances of css rule override with !important
|
||
|
|
var flag = 'rgb(1, 2, 3)'; // this flag tested on chrome 59, ff 53, ie9, ie10, ie11, edge 14
|
||
|
|
tempElem.style.color = flag;
|
||
|
|
if (tempElem.style.color !== flag) {
|
||
|
|
return; // color set failed - some monstrous css rule is probably taking over the color of our object
|
||
|
|
}
|
||
|
|
tempElem.style.color = color;
|
||
|
|
if (tempElem.style.color === flag || tempElem.style.color === '') {
|
||
|
|
return; // color parse failed
|
||
|
|
}
|
||
|
|
color = getComputedStyle(tempElem).color;
|
||
|
|
document.body.removeChild(tempElem);
|
||
|
|
}
|
||
|
|
if (color.indexOf('rgb') === 0) {
|
||
|
|
if (color.indexOf('rgba') === -1) {
|
||
|
|
color += ',1'; // convert 'rgb(R,G,B)' to 'rgb(R,G,B)A' which looks awful but will pass the regxep below
|
||
|
|
}
|
||
|
|
return color.match(/[\.\d]+/g).map(function(a) {
|
||
|
|
return +a
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
fabric.Point.prototype.angleBetween = function(that) {
|
||
|
|
return Math.atan2( this.x - that.x, this.y - that.y);
|
||
|
|
};
|
||
|
|
|
||
|
|
fabric.Point.prototype.normalize = function(thickness) {
|
||
|
|
if (null === thickness || undefined === thickness) {
|
||
|
|
thickness = 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
var length = this.distanceFrom({ x: 0, y: 0 });
|
||
|
|
|
||
|
|
if (length > 0) {
|
||
|
|
this.x = this.x / length * thickness;
|
||
|
|
this.y = this.y / length * thickness;
|
||
|
|
}
|
||
|
|
|
||
|
|
return this;
|
||
|
|
};
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Convert a brush drawing on the upperCanvas to an image on the fabric canvas.
|
||
|
|
* This makes the drawing editable, it can be moved, rotated, scaled, skewed etc.
|
||
|
|
*/
|
||
|
|
fabric.BaseBrush.prototype.convertToImg = function() {
|
||
|
|
var pixelRatio = this.canvas.getRetinaScaling(),
|
||
|
|
c = fabric.util.copyCanvasElement(this.canvas.upperCanvasEl),
|
||
|
|
xy = fabric.util.trimCanvas(c),
|
||
|
|
img = new fabric.Image(c);
|
||
|
|
if(!xy){
|
||
|
|
return
|
||
|
|
}
|
||
|
|
img.set({left:xy.x/pixelRatio,top:xy.y/pixelRatio,'scaleX':1/pixelRatio,'scaleY':1/pixelRatio}).setCoords();
|
||
|
|
this.canvas.add(img).clearContext(this.canvas.contextTop);
|
||
|
|
this.canvas.clearContext(this.canvas.contextTop);
|
||
|
|
}
|
||
|
|
|
||
|
|
fabric.util.getRandom = function(max, min) {
|
||
|
|
min = min ? min : 0;
|
||
|
|
return Math.random() * ((max ? max : 1) - min) + min;
|
||
|
|
};
|
||
|
|
|
||
|
|
fabric.util.clamp = function (n, max, min) {
|
||
|
|
if (typeof min !== 'number') { min = 0; }
|
||
|
|
return n > max ? max : n < min ? min : n;
|
||
|
|
};
|
||
|
|
|
||
|
|
fabric.Stroke = fabric.util.createClass(fabric.Object,{
|
||
|
|
color: null,
|
||
|
|
inkAmount: null,
|
||
|
|
lineWidth: null,
|
||
|
|
|
||
|
|
_point: null,
|
||
|
|
_lastPoint: null,
|
||
|
|
_currentLineWidth: null,
|
||
|
|
|
||
|
|
initialize: function(ctx, pointer, range, color, lineWidth, inkAmount) {
|
||
|
|
var rx = fabric.util.getRandom(range),
|
||
|
|
c = fabric.util.getRandom(Math.PI * 2),
|
||
|
|
c0 = fabric.util.getRandom(Math.PI * 2),
|
||
|
|
x0 = rx * Math.sin(c0),
|
||
|
|
y0 = rx / 2 * Math.cos(c0),
|
||
|
|
cos = Math.cos(c),
|
||
|
|
sin = Math.sin(c);
|
||
|
|
|
||
|
|
this.ctx = ctx;
|
||
|
|
this.color = color;
|
||
|
|
this._point = new fabric.Point(pointer.x + x0 * cos - y0 * sin, pointer.y + x0 * sin + y0 * cos);
|
||
|
|
this.lineWidth = lineWidth;
|
||
|
|
this.inkAmount = inkAmount;
|
||
|
|
this._currentLineWidth = lineWidth;
|
||
|
|
|
||
|
|
ctx.lineCap = 'round';
|
||
|
|
},
|
||
|
|
|
||
|
|
update: function(pointer, subtractPoint, distance) {
|
||
|
|
this._lastPoint = fabric.util.object.clone(this._point);
|
||
|
|
this._point = this._point.addEquals({ x: subtractPoint.x, y: subtractPoint.y });
|
||
|
|
|
||
|
|
var n = this.inkAmount / (distance + 1),
|
||
|
|
per = (n > 0.3 ? 0.2 : n < 0 ? 0 : n);
|
||
|
|
this._currentLineWidth = this.lineWidth * per;
|
||
|
|
},
|
||
|
|
|
||
|
|
draw: function() {
|
||
|
|
var ctx = this.ctx;
|
||
|
|
ctx.save();
|
||
|
|
this.line(ctx, this._lastPoint, this._point, this.color, this._currentLineWidth);
|
||
|
|
ctx.restore();
|
||
|
|
},
|
||
|
|
|
||
|
|
line: function(ctx, point1, point2, color, lineWidth) {
|
||
|
|
ctx.strokeStyle = color;
|
||
|
|
ctx.lineWidth = lineWidth;
|
||
|
|
ctx.beginPath();
|
||
|
|
ctx.moveTo(point1.x, point1.y);
|
||
|
|
ctx.lineTo(point2.x, point2.y);
|
||
|
|
ctx.stroke();
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
/**
|
||
|
|
* CrayonBrush
|
||
|
|
* Based on code by Tennison Chan.
|
||
|
|
*/
|
||
|
|
fabric.CrayonBrush = fabric.util.createClass(fabric.BaseBrush, {
|
||
|
|
color: '#000',
|
||
|
|
opacity: 0.6,
|
||
|
|
width: 30,
|
||
|
|
|
||
|
|
_baseWidth: 15,
|
||
|
|
_inkAmount: 10,
|
||
|
|
_latestStrokeLength: 0,
|
||
|
|
_point: null,
|
||
|
|
_sep: 3,
|
||
|
|
_size: 0,
|
||
|
|
_latest: null,
|
||
|
|
_drawn: false,
|
||
|
|
|
||
|
|
initialize: function(canvas, opt) {
|
||
|
|
opt = opt || {};
|
||
|
|
|
||
|
|
this.canvas = canvas;
|
||
|
|
this.width = opt.width || this.width;
|
||
|
|
this.color = opt.color || this.color;
|
||
|
|
this.opacity = opt.opacity || this.opacity;
|
||
|
|
this._baseWidth = this.width/2
|
||
|
|
this._point = new fabric.Point(0, 0);
|
||
|
|
},
|
||
|
|
|
||
|
|
onMouseDown: function(pointer) {
|
||
|
|
this.canvas.contextTop.globalAlpha = this.opacity;
|
||
|
|
this._size = this.width / 2 + this._baseWidth;
|
||
|
|
this._drawn = false;
|
||
|
|
this.set(pointer);
|
||
|
|
},
|
||
|
|
|
||
|
|
onMouseMove: function(pointer) {
|
||
|
|
this.update(pointer);
|
||
|
|
this.draw(this.canvas.contextTop);
|
||
|
|
},
|
||
|
|
|
||
|
|
onMouseUp: function() {
|
||
|
|
if (this._drawn) {
|
||
|
|
this.convertToImg();
|
||
|
|
}
|
||
|
|
this._latest = null;
|
||
|
|
this._latestStrokeLength = 0;
|
||
|
|
this.canvas.contextTop.globalAlpha = 1;
|
||
|
|
},
|
||
|
|
|
||
|
|
set: function(p) {
|
||
|
|
if (this._latest) {
|
||
|
|
this._latest.setFromPoint(this._point);
|
||
|
|
} else {
|
||
|
|
this._latest = new fabric.Point(p.x, p.y);
|
||
|
|
}
|
||
|
|
fabric.Point.prototype.setFromPoint.call(this._point, p);
|
||
|
|
},
|
||
|
|
|
||
|
|
update: function(p) {
|
||
|
|
this.set(p);
|
||
|
|
this._latestStrokeLength = this._point.subtract(this._latest).distanceFrom({ x: 0, y: 0 });
|
||
|
|
},
|
||
|
|
|
||
|
|
draw: function(ctx) {
|
||
|
|
var i, j, p, r, c, x, y, w, h, v, s, stepNum, dotSize, dotNum, range;
|
||
|
|
|
||
|
|
v = this._point.subtract(this._latest);
|
||
|
|
s = Math.ceil(this._size / 2);
|
||
|
|
stepNum = Math.floor(v.distanceFrom({ x: 0, y: 0 }) / s) + 1;
|
||
|
|
v.normalize(s);
|
||
|
|
|
||
|
|
dotSize = this._sep * fabric.util.clamp(this._inkAmount / this._latestStrokeLength * 3, 1, 0.5);
|
||
|
|
dotNum = Math.ceil(this._size * this._sep);
|
||
|
|
|
||
|
|
range = this._size / 2;
|
||
|
|
|
||
|
|
ctx.save();
|
||
|
|
ctx.fillStyle = this.color;
|
||
|
|
ctx.beginPath();
|
||
|
|
for (i = 0; i < dotNum; i++) {
|
||
|
|
for (j = 0; j < stepNum; j++) {
|
||
|
|
p = this._latest.add(v.multiply(j));
|
||
|
|
r = fabric.util.getRandom(range);
|
||
|
|
c = fabric.util.getRandom(Math.PI * 2);
|
||
|
|
w = fabric.util.getRandom(dotSize, dotSize / 2);
|
||
|
|
h = fabric.util.getRandom(dotSize, dotSize / 2);
|
||
|
|
x = p.x + r * Math.sin(c) - w / 2;
|
||
|
|
y = p.y + r * Math.cos(c) - h / 2;
|
||
|
|
ctx.rect(x, y, w, h);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
ctx.fill();
|
||
|
|
ctx.restore();
|
||
|
|
this._drawn = true;
|
||
|
|
},
|
||
|
|
|
||
|
|
_render: function() {}
|
||
|
|
|
||
|
|
}); // End CrayonBrush
|
||
|
|
|
||
|
|
/**
|
||
|
|
* FurBrush
|
||
|
|
* Based on code by Mr. Doob.
|
||
|
|
*/
|
||
|
|
fabric.FurBrush = fabric.util.createClass(fabric.BaseBrush, {
|
||
|
|
color: '#000',
|
||
|
|
opacity: 1,
|
||
|
|
width: 1,
|
||
|
|
|
||
|
|
_count: 0,
|
||
|
|
_points: [],
|
||
|
|
|
||
|
|
initialize: function(canvas, opt) {
|
||
|
|
opt = opt || {};
|
||
|
|
|
||
|
|
this.canvas = canvas;
|
||
|
|
this.width = opt.width || this.width;
|
||
|
|
this.color = opt.color || this.color;
|
||
|
|
this.opacity = opt.opacity || 1;
|
||
|
|
},
|
||
|
|
|
||
|
|
onMouseDown: function(pointer) {
|
||
|
|
this._points = [pointer];
|
||
|
|
this._count = 0;
|
||
|
|
|
||
|
|
var ctx = this.canvas.contextTop,
|
||
|
|
color = fabric.util.colorValues(this.color);
|
||
|
|
|
||
|
|
ctx.strokeStyle = 'rgba(' + color[0] + ',' + color[1] + ',' + color[2] + ',' + (0.1 * this.opacity) + ')';
|
||
|
|
ctx.lineWidth = this.width;
|
||
|
|
|
||
|
|
this._points.push(pointer);
|
||
|
|
},
|
||
|
|
|
||
|
|
onMouseMove: function(pointer) {
|
||
|
|
this._points.push(pointer);
|
||
|
|
|
||
|
|
var i, dx, dy, d,
|
||
|
|
ctx = this.canvas.contextTop,
|
||
|
|
points = this._points,
|
||
|
|
lastPoint = points[points.length - 2];
|
||
|
|
|
||
|
|
ctx.beginPath();
|
||
|
|
ctx.moveTo(lastPoint.x, lastPoint.y);
|
||
|
|
ctx.lineTo(pointer.x, pointer.y);
|
||
|
|
ctx.stroke();
|
||
|
|
|
||
|
|
for (i = 0; i < this._points.length; i++) {
|
||
|
|
dx = this._points[i].x - this._points[this._count].x;
|
||
|
|
dy = this._points[i].y - this._points[this._count].y;
|
||
|
|
d = dx * dx + dy * dy;
|
||
|
|
|
||
|
|
if (d < 2000 && Math.random() > d / 2000) {
|
||
|
|
ctx.beginPath();
|
||
|
|
ctx.moveTo(pointer.x + (dx * 0.5), pointer.y + (dy * 0.5));
|
||
|
|
ctx.lineTo(pointer.x - (dx * 0.5), pointer.y - (dy * 0.5));
|
||
|
|
ctx.stroke();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
this._count++;
|
||
|
|
},
|
||
|
|
|
||
|
|
onMouseUp: function(pointer) {
|
||
|
|
if (this._count > 0) {
|
||
|
|
this.convertToImg();
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
_render: function() {}
|
||
|
|
}); // End FurBrush
|
||
|
|
|
||
|
|
/**
|
||
|
|
* InkBrush
|
||
|
|
* Based on code by Tennison Chan.
|
||
|
|
*/
|
||
|
|
fabric.InkBrush = fabric.util.createClass(fabric.BaseBrush, {
|
||
|
|
color: '#000',
|
||
|
|
opacity: 1,
|
||
|
|
width: 30,
|
||
|
|
|
||
|
|
_baseWidth: 15,
|
||
|
|
_inkAmount: 7,
|
||
|
|
_lastPoint: null,
|
||
|
|
_point: null,
|
||
|
|
_range: 10,
|
||
|
|
_strokes: null,
|
||
|
|
|
||
|
|
initialize: function(canvas, opt) {
|
||
|
|
opt = opt || {};
|
||
|
|
|
||
|
|
this.canvas = canvas;
|
||
|
|
this.width = opt.width || this.width;
|
||
|
|
this.color = opt.color || this.color;
|
||
|
|
this.opacity = opt.opacity || this.opacity;
|
||
|
|
this._baseWidth = this.width
|
||
|
|
this._point = new fabric.Point();
|
||
|
|
},
|
||
|
|
|
||
|
|
_render: function(pointer) {
|
||
|
|
console.log(this.color);
|
||
|
|
var len, i, point = this.setPointer(pointer),
|
||
|
|
subtractPoint = point.subtract(this._lastPoint),
|
||
|
|
distance = point.distanceFrom(this._lastPoint),
|
||
|
|
stroke;
|
||
|
|
|
||
|
|
for (i = 0, len = this._strokes.length; i < len; i++) {
|
||
|
|
stroke = this._strokes[i];
|
||
|
|
stroke.update(point, subtractPoint, distance);
|
||
|
|
stroke.draw();
|
||
|
|
}
|
||
|
|
|
||
|
|
if (distance > 30) {
|
||
|
|
// this.drawSplash(point, this._inkAmount);
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
onMouseDown: function(pointer) {
|
||
|
|
this.canvas.contextTop.globalAlpha = this.opacity;
|
||
|
|
this._resetTip(pointer);
|
||
|
|
},
|
||
|
|
|
||
|
|
onMouseMove: function(pointer) {
|
||
|
|
if (this.canvas._isCurrentlyDrawing) {
|
||
|
|
this._render(pointer);
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
onMouseUp: function() {
|
||
|
|
this.convertToImg();
|
||
|
|
this.canvas.contextTop.globalAlpha = 1;
|
||
|
|
},
|
||
|
|
hexToRgba:function(hex, alpha) {
|
||
|
|
|
||
|
|
},
|
||
|
|
drawSplash: function(pointer, maxSize) {
|
||
|
|
var c, r, i, point,
|
||
|
|
ctx = this.canvas.contextTop,
|
||
|
|
num = fabric.util.getRandom(12),
|
||
|
|
range = maxSize * 10,
|
||
|
|
color = this.color;
|
||
|
|
|
||
|
|
ctx.save();
|
||
|
|
for (i = 0; i < num; i++) {
|
||
|
|
r = fabric.util.getRandom(range, 1);
|
||
|
|
c = fabric.util.getRandom(Math.PI * 2);
|
||
|
|
point = new fabric.Point(pointer.x + r * Math.sin(c), pointer.y + r * Math.cos(c));
|
||
|
|
|
||
|
|
ctx.fillStyle = color;
|
||
|
|
ctx.beginPath();
|
||
|
|
ctx.arc(point.x, point.y, fabric.util.getRandom(maxSize) / 2, 0, Math.PI * 2, false);
|
||
|
|
ctx.fill();
|
||
|
|
}
|
||
|
|
ctx.restore();
|
||
|
|
},
|
||
|
|
|
||
|
|
setPointer: function(pointer) {
|
||
|
|
var point = new fabric.Point(pointer.x, pointer.y);
|
||
|
|
|
||
|
|
this._lastPoint = fabric.util.object.clone(this._point);
|
||
|
|
this._point = point;
|
||
|
|
|
||
|
|
return point;
|
||
|
|
},
|
||
|
|
|
||
|
|
_resetTip: function(pointer) {
|
||
|
|
var len, i, point = this.setPointer(pointer);
|
||
|
|
|
||
|
|
this._strokes = [];
|
||
|
|
this.size = this.width;
|
||
|
|
this._range = this.size / 2;
|
||
|
|
|
||
|
|
for (i = 0, len = this.size; i < len; i++) {
|
||
|
|
this._strokes[i] = new fabric.Stroke(this.canvas.contextTop, point, this._range, this.color, this.width+10, this._inkAmount);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}); // End InkBrush
|
||
|
|
|
||
|
|
/**
|
||
|
|
* LongfurBrush
|
||
|
|
* Based on code by Mr. Doob.
|
||
|
|
*/
|
||
|
|
fabric.LongfurBrush = fabric.util.createClass(fabric.BaseBrush, {
|
||
|
|
color: '#000',
|
||
|
|
opacity: 1,
|
||
|
|
width: 1,
|
||
|
|
|
||
|
|
_count: 0,
|
||
|
|
_points: [],
|
||
|
|
|
||
|
|
initialize: function(canvas, opt) {
|
||
|
|
opt = opt || {};
|
||
|
|
|
||
|
|
this.canvas = canvas;
|
||
|
|
this.width = opt.width || this.width;
|
||
|
|
this.color = opt.color || this.color;
|
||
|
|
this.opacity = opt.opacity || 1;
|
||
|
|
},
|
||
|
|
|
||
|
|
onMouseDown: function(pointer) {
|
||
|
|
this._points = [pointer];
|
||
|
|
this._count = 0;
|
||
|
|
|
||
|
|
var ctx = this.canvas.contextTop,
|
||
|
|
color = fabric.util.colorValues(this.color);
|
||
|
|
|
||
|
|
//ctx.globalCompositeOperation = 'source-over';
|
||
|
|
ctx.strokeStyle = 'rgba(' + color[0] + ',' + color[1] + ',' + color[2] + ',' + (0.05 * this.opacity) + ')';
|
||
|
|
ctx.lineWidth = this.width;
|
||
|
|
},
|
||
|
|
|
||
|
|
onMouseMove: function(pointer) {
|
||
|
|
this._points.push(pointer);
|
||
|
|
|
||
|
|
var i, dx, dy, d, size,
|
||
|
|
ctx = this.canvas.contextTop,
|
||
|
|
points = this._points;
|
||
|
|
|
||
|
|
for (i = 0; i < this._points.length; i++) {
|
||
|
|
size = -Math.random();
|
||
|
|
|
||
|
|
dx = this._points[i].x - this._points[this._count].x;
|
||
|
|
dy = this._points[i].y - this._points[this._count].y;
|
||
|
|
d = dx * dx + dy * dy;
|
||
|
|
|
||
|
|
if (d < 1000 && Math.random() > d / 1000) {
|
||
|
|
ctx.beginPath();
|
||
|
|
ctx.moveTo(this._points[this._count].x + (dx * size), this._points[this._count].y + (dy * size));
|
||
|
|
ctx.lineTo(this._points[i].x - (dx * size) + Math.random() * 2, this._points[i].y - (dy * size) + Math.random() * 2);
|
||
|
|
ctx.stroke();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
this._count++;
|
||
|
|
},
|
||
|
|
|
||
|
|
onMouseUp: function(pointer) {
|
||
|
|
if (this._count > 0) {
|
||
|
|
this.convertToImg();
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
_render: function() {}
|
||
|
|
}); // End LongfurBrush
|
||
|
|
/**
|
||
|
|
* WritingBrush
|
||
|
|
* Based on code by Tennison Chan.
|
||
|
|
*/
|
||
|
|
fabric.WritingBrush = fabric.util.createClass(fabric.BaseBrush, {
|
||
|
|
color: '#000',
|
||
|
|
opacity: 1,
|
||
|
|
width: 30,
|
||
|
|
|
||
|
|
_baseWidth: 15,
|
||
|
|
_lastPoint: null,
|
||
|
|
_lineWidth: 2,
|
||
|
|
_point: null,
|
||
|
|
_size: 0,
|
||
|
|
|
||
|
|
initialize: function(canvas, opt) {
|
||
|
|
opt = opt || {};
|
||
|
|
|
||
|
|
this.canvas = canvas;
|
||
|
|
this.width = opt.width || this.width;
|
||
|
|
this.color = opt.color || this.color;
|
||
|
|
this.opacity = opt.opacity || this.opacity;
|
||
|
|
this._baseWidth = this.width;
|
||
|
|
this.canvas.contextTop.globalAlpha = this.opacity;
|
||
|
|
this._point = new fabric.Point();
|
||
|
|
|
||
|
|
this.canvas.contextTop.lineJoin = 'round';
|
||
|
|
this.canvas.contextTop.lineCap = 'round';
|
||
|
|
},
|
||
|
|
|
||
|
|
_render: function(pointer) {
|
||
|
|
var ctx, lineWidthDiff, i, len;
|
||
|
|
ctx = this.canvas.contextTop;
|
||
|
|
ctx.beginPath();
|
||
|
|
// let num = this._size / this._lineWidth / 2 / 1.2
|
||
|
|
let num = this.width/1.25 / 2
|
||
|
|
|
||
|
|
for(i = 0, len = this.width/1.25 ; i < len; i++) {
|
||
|
|
// for(i = 0, len = (this._size / this._lineWidth) / 1.2; i < len; i++) {
|
||
|
|
lineWidthDiff = (this._lineWidth - 1) * i;
|
||
|
|
|
||
|
|
ctx.globalAlpha = 0.8 * this.opacity;
|
||
|
|
ctx.moveTo(this._lastPoint.x -lineWidthDiff+num, this._lastPoint.y + lineWidthDiff-num);
|
||
|
|
ctx.lineTo(pointer.x - lineWidthDiff+num, pointer.y + lineWidthDiff-num);
|
||
|
|
ctx.stroke();
|
||
|
|
}
|
||
|
|
|
||
|
|
this._lastPoint = new fabric.Point(pointer.x, pointer.y);
|
||
|
|
},
|
||
|
|
|
||
|
|
onMouseDown: function(pointer) {
|
||
|
|
this._lastPoint = pointer;
|
||
|
|
this.canvas.contextTop.strokeStyle = this.color;
|
||
|
|
this.canvas.contextTop.lineWidth = this._lineWidth;
|
||
|
|
this._size = this.width + this._baseWidth;
|
||
|
|
},
|
||
|
|
|
||
|
|
onMouseMove: function(pointer) {
|
||
|
|
if (this.canvas._isCurrentlyDrawing) {
|
||
|
|
this._render(pointer);
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
onMouseUp: function() {
|
||
|
|
this.canvas.contextTop.globalAlpha = this.opacity;
|
||
|
|
this.canvas.contextTop.globalAlpha = 1;
|
||
|
|
this.convertToImg();
|
||
|
|
}
|
||
|
|
}); // End WritingBrush
|
||
|
|
/**
|
||
|
|
* MarkerBrush
|
||
|
|
* Based on code by Tennison Chan.
|
||
|
|
*/
|
||
|
|
fabric.MarkerBrush = fabric.util.createClass(fabric.BaseBrush, {
|
||
|
|
color: '#000',
|
||
|
|
opacity: 1,
|
||
|
|
width: 30,
|
||
|
|
|
||
|
|
_baseWidth: 15,
|
||
|
|
_lastPoint: null,
|
||
|
|
_lineWidth: 2,
|
||
|
|
_point: null,
|
||
|
|
_size: 0,
|
||
|
|
|
||
|
|
initialize: function(canvas, opt) {
|
||
|
|
opt = opt || {};
|
||
|
|
|
||
|
|
this.canvas = canvas;
|
||
|
|
this.width = opt.width || this.width;
|
||
|
|
this.color = opt.color || this.color;
|
||
|
|
this.opacity = opt.opacity || this.opacity;
|
||
|
|
this._baseWidth = this.width;
|
||
|
|
this.canvas.contextTop.globalAlpha = this.opacity;
|
||
|
|
this._point = new fabric.Point();
|
||
|
|
|
||
|
|
this.canvas.contextTop.lineJoin = 'round';
|
||
|
|
this.canvas.contextTop.lineCap = 'round';
|
||
|
|
},
|
||
|
|
|
||
|
|
_render: function(pointer) {
|
||
|
|
|
||
|
|
var ctx, lineWidthDiff, i, len;
|
||
|
|
ctx = this.canvas.contextTop;
|
||
|
|
|
||
|
|
ctx.beginPath();
|
||
|
|
let num = this.width/1.25 / 2
|
||
|
|
for(i = 0, len = this.width/1.25 ; i < len; i++) {
|
||
|
|
lineWidthDiff = (this._lineWidth - 1) * i;
|
||
|
|
ctx.globalAlpha = 0.8 * this.opacity;
|
||
|
|
ctx.moveTo(this._lastPoint.x + lineWidthDiff-num, (this._lastPoint.y + lineWidthDiff-num));
|
||
|
|
ctx.lineTo(pointer.x + lineWidthDiff-num, pointer.y + lineWidthDiff-num);
|
||
|
|
ctx.stroke();
|
||
|
|
}
|
||
|
|
|
||
|
|
this._lastPoint = new fabric.Point(pointer.x, pointer.y);
|
||
|
|
},
|
||
|
|
|
||
|
|
onMouseDown: function(pointer) {
|
||
|
|
this._lastPoint = pointer;
|
||
|
|
this.canvas.contextTop.strokeStyle = this.color;
|
||
|
|
this.canvas.contextTop.lineWidth = this._lineWidth;
|
||
|
|
this._size = this.width + this._baseWidth/2;
|
||
|
|
},
|
||
|
|
|
||
|
|
onMouseMove: function(pointer) {
|
||
|
|
if (this.canvas._isCurrentlyDrawing) {
|
||
|
|
this._render(pointer);
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
onMouseUp: function() {
|
||
|
|
this.canvas.contextTop.globalAlpha = this.opacity;
|
||
|
|
this.canvas.contextTop.globalAlpha = 1;
|
||
|
|
this.convertToImg();
|
||
|
|
}
|
||
|
|
}); // End MarkerBrush
|
||
|
|
/**
|
||
|
|
* MarkerBrush
|
||
|
|
* Based on code by Tennison Chan.
|
||
|
|
*/
|
||
|
|
fabric.MarkerBrush1 = fabric.util.createClass(fabric.BaseBrush, {
|
||
|
|
color: '#000',
|
||
|
|
opacity: 1,
|
||
|
|
width: 30,
|
||
|
|
content: 0,
|
||
|
|
|
||
|
|
_baseWidth: 15,
|
||
|
|
_lastPoint: null,
|
||
|
|
_lineWidth: 2,
|
||
|
|
_point: null,
|
||
|
|
_size: 0,
|
||
|
|
|
||
|
|
initialize: function(canvas, opt) {
|
||
|
|
opt = opt || {};
|
||
|
|
|
||
|
|
this.canvas = canvas;
|
||
|
|
this.width = opt.width || this.width;
|
||
|
|
this.color = opt.color || this.color;
|
||
|
|
this.opacity = opt.opacity || this.opacity;
|
||
|
|
this._baseWidth = this.width;
|
||
|
|
this.content = opt.content || canvas.width/2;
|
||
|
|
this.canvas.contextTop.globalAlpha = this.opacity;
|
||
|
|
this._point = new fabric.Point();
|
||
|
|
|
||
|
|
this.canvas.contextTop.lineJoin = 'round';
|
||
|
|
this.canvas.contextTop.lineCap = 'round';
|
||
|
|
},
|
||
|
|
|
||
|
|
_render: function(pointer) {
|
||
|
|
var ctx, lineWidthDiff, i, len;
|
||
|
|
|
||
|
|
ctx = this.canvas.contextTop;
|
||
|
|
|
||
|
|
ctx.beginPath();
|
||
|
|
|
||
|
|
for(i = 0, len = (this._size / this._lineWidth) / 2; i < len; i++) {
|
||
|
|
lineWidthDiff = (this._lineWidth - 1) * i;
|
||
|
|
let half = this.canvas.width/2
|
||
|
|
ctx.globalAlpha = 0.8 * this.opacity;
|
||
|
|
ctx.moveTo(this._lastPoint.x, this._lastPoint.y);
|
||
|
|
let x = content > this._lastPoint.x?content - this._lastPoint.x+content :content*2 - this._lastPoint.x
|
||
|
|
ctx.lineTo(x, this._lastPoint.y);
|
||
|
|
// ctx.lineTo(pointer.y + lineWidthDiff,pointer.x + lineWidthDiff);
|
||
|
|
ctx.stroke();
|
||
|
|
}
|
||
|
|
|
||
|
|
this._lastPoint = new fabric.Point(pointer.x, pointer.y);
|
||
|
|
},
|
||
|
|
|
||
|
|
onMouseDown: function(pointer) {
|
||
|
|
this._lastPoint = pointer;
|
||
|
|
this.canvas.contextTop.strokeStyle = this.color;
|
||
|
|
this.canvas.contextTop.lineWidth = this._lineWidth;
|
||
|
|
this._size = this.width + this._baseWidth;
|
||
|
|
},
|
||
|
|
|
||
|
|
onMouseMove: function(pointer) {
|
||
|
|
if (this.canvas._isCurrentlyDrawing) {
|
||
|
|
this._render(pointer);
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
onMouseUp: function() {
|
||
|
|
this.canvas.contextTop.globalAlpha = this.opacity;
|
||
|
|
this.canvas.contextTop.globalAlpha = 1;
|
||
|
|
this.convertToImg();
|
||
|
|
}
|
||
|
|
}); // End MarkerBrush
|
||
|
|
/**
|
||
|
|
* RibbonBrush
|
||
|
|
* Based on code by Mr. Doob.
|
||
|
|
*/
|
||
|
|
fabric.RibbonBrush = fabric.util.createClass(fabric.BaseBrush, {
|
||
|
|
color: '#000',
|
||
|
|
opacity: 1,
|
||
|
|
width: 1,
|
||
|
|
|
||
|
|
_nrPainters: 50,
|
||
|
|
_painters: [],
|
||
|
|
_lastPoint: null,
|
||
|
|
_interval: null,
|
||
|
|
|
||
|
|
initialize: function(canvas, opt) {
|
||
|
|
opt = opt || {};
|
||
|
|
this.canvas = canvas;
|
||
|
|
this.width = opt.width || this.width;
|
||
|
|
this._nrPainters = opt._nrPainters || this._nrPainters;
|
||
|
|
this.color = opt.color || this.color;
|
||
|
|
this.opacity = opt.opacity || this.opacity;
|
||
|
|
for (var i = 0; i < this._nrPainters; i++) {
|
||
|
|
this._painters.push({ dx:this.canvas.width / 2, dy:this.canvas.height / 2, ax:0, ay:0, div:.1, ease:Math.random() * .2 + .6 });
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
update: function() {
|
||
|
|
var ctx = this.canvas.contextTop, painters = this._painters;
|
||
|
|
for (var i = 0; i < painters.length; i++) {
|
||
|
|
ctx.beginPath();
|
||
|
|
ctx.moveTo(painters[i].dx, painters[i].dy);
|
||
|
|
painters[i].dx -= painters[i].ax = (painters[i].ax + (painters[i].dx - this._lastPoint.x) * painters[i].div) * painters[i].ease;
|
||
|
|
painters[i].dy -= painters[i].ay = (painters[i].ay + (painters[i].dy - this._lastPoint.y) * painters[i].div) * painters[i].ease;
|
||
|
|
ctx.lineTo(painters[i].dx, painters[i].dy);
|
||
|
|
ctx.stroke();
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
onMouseDown: function(pointer) {
|
||
|
|
var ctx = this.canvas.contextTop,
|
||
|
|
color = fabric.util.colorValues(this.color);
|
||
|
|
|
||
|
|
this._painters = [];
|
||
|
|
for (var i = 0; i < this._nrPainters; i++) {
|
||
|
|
this._painters.push({ dx:this.canvas.width / 2, dy:this.canvas.height / 2, ax:0, ay:0, div:.1, ease:Math.random() * .2 + .6 });
|
||
|
|
}
|
||
|
|
|
||
|
|
this._lastPoint = pointer;
|
||
|
|
|
||
|
|
//ctx.globalCompositeOperation = 'source-over';
|
||
|
|
ctx.strokeStyle = 'rgba(' + color[0] + ',' + color[1] + ',' + color[2] + ',' + (0.05 * this.opacity) + ')';
|
||
|
|
ctx.lineWidth = this.width;
|
||
|
|
|
||
|
|
for (var i = 0; i < this._nrPainters; i++) {
|
||
|
|
this._painters[i].dx = pointer.x;
|
||
|
|
this._painters[i].dy = pointer.y;
|
||
|
|
}
|
||
|
|
|
||
|
|
var self = this;
|
||
|
|
this._interval = setInterval(function() { self.update() }, 1000/60);
|
||
|
|
},
|
||
|
|
|
||
|
|
onMouseMove: function(pointer) {
|
||
|
|
this._lastPoint = pointer;
|
||
|
|
},
|
||
|
|
|
||
|
|
onMouseUp: function(pointer) {
|
||
|
|
clearInterval(this._interval);
|
||
|
|
this.convertToImg();
|
||
|
|
},
|
||
|
|
|
||
|
|
_render: function() {}
|
||
|
|
}); // End RibbonBrush
|
||
|
|
|
||
|
|
/**
|
||
|
|
* ShadedBrush
|
||
|
|
* Based on code by Mr. Doob.
|
||
|
|
*/
|
||
|
|
fabric.ShadedBrush = fabric.util.createClass(fabric.BaseBrush, {
|
||
|
|
color: '#000',
|
||
|
|
opacity: .3,
|
||
|
|
width: 1,
|
||
|
|
shadeDistance: 1000,
|
||
|
|
|
||
|
|
_points: [],
|
||
|
|
|
||
|
|
initialize: function(canvas, opt) {
|
||
|
|
opt = opt || {};
|
||
|
|
|
||
|
|
this.canvas = canvas;
|
||
|
|
this.width = opt.width || this.width;
|
||
|
|
this.color = opt.color || this.color;
|
||
|
|
this.opacity = opt.opacity || this.opacity;
|
||
|
|
this.shadeDistance = opt.shadeDistance || 1000;
|
||
|
|
},
|
||
|
|
|
||
|
|
onMouseDown: function(pointer) {
|
||
|
|
this._points = [pointer];
|
||
|
|
|
||
|
|
var ctx = this.canvas.contextTop,
|
||
|
|
color = fabric.util.colorValues(this.color);
|
||
|
|
|
||
|
|
ctx.strokeStyle = 'rgba(' + color[0] + ',' + color[1] + ',' + color[2] + ',' + this.opacity + ')';
|
||
|
|
ctx.lineWidth = this.width;
|
||
|
|
ctx.lineJoin = ctx.lineCap = 'round';
|
||
|
|
},
|
||
|
|
|
||
|
|
onMouseMove: function(pointer) {
|
||
|
|
this._points.push(pointer);
|
||
|
|
|
||
|
|
var ctx = this.canvas.contextTop,
|
||
|
|
points = this._points;
|
||
|
|
|
||
|
|
ctx.beginPath();
|
||
|
|
ctx.moveTo(points[points.length - 2].x, points[points.length - 2].y);
|
||
|
|
ctx.lineTo(points[points.length - 1].x, points[points.length - 1].y);
|
||
|
|
ctx.stroke();
|
||
|
|
|
||
|
|
for (var i = 0, len = points.length; i < len; i++) {
|
||
|
|
dx = points[i].x - points[points.length-1].x;
|
||
|
|
dy = points[i].y - points[points.length-1].y;
|
||
|
|
d = dx * dx + dy * dy;
|
||
|
|
|
||
|
|
if (d < this.shadeDistance) {
|
||
|
|
ctx.beginPath();
|
||
|
|
ctx.moveTo( points[points.length-1].x + (dx * 0.2), points[points.length-1].y + (dy * 0.2));
|
||
|
|
ctx.lineTo( points[i].x - (dx * 0.2), points[i].y - (dy * 0.2));
|
||
|
|
ctx.stroke();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
onMouseUp: function(pointer) {
|
||
|
|
if (this._points.length > 1) {
|
||
|
|
this.convertToImg();
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
_render: function() {}
|
||
|
|
}); // End ShadedBrush
|
||
|
|
|
||
|
|
/**
|
||
|
|
* SketchyBrush
|
||
|
|
* Based on code by Mr. Doob.
|
||
|
|
*/
|
||
|
|
fabric.SketchyBrush = fabric.util.createClass(fabric.BaseBrush, {
|
||
|
|
color: '#000',
|
||
|
|
opacity: 1,
|
||
|
|
width: 1,
|
||
|
|
|
||
|
|
_count: 0,
|
||
|
|
_points: [],
|
||
|
|
|
||
|
|
initialize: function(canvas, opt) {
|
||
|
|
opt = opt || {};
|
||
|
|
|
||
|
|
this.canvas = canvas;
|
||
|
|
this.width = opt.width || this.width;
|
||
|
|
this.color = opt.color || this.color;
|
||
|
|
this.opacity = opt.opacity || this.opacity;
|
||
|
|
},
|
||
|
|
|
||
|
|
onMouseDown: function(pointer) {
|
||
|
|
this._count = 0;
|
||
|
|
this._points = [pointer];
|
||
|
|
|
||
|
|
var ctx = this.canvas.contextTop,
|
||
|
|
color = fabric.util.colorValues(this.color);
|
||
|
|
//ctx.globalCompositeOperation = 'source-over';
|
||
|
|
ctx.strokeStyle = 'rgba(' + color[0] + ',' + color[1] + ',' + color[2] + ',' + (0.05 * this.opacity) + ')';
|
||
|
|
ctx.lineWidth = this.width;
|
||
|
|
},
|
||
|
|
|
||
|
|
onMouseMove: function(pointer) {
|
||
|
|
this._points.push(pointer);
|
||
|
|
|
||
|
|
var i, dx, dy, d, factor = .3 * this.width,
|
||
|
|
ctx = this.canvas.contextTop,
|
||
|
|
points = this._points,
|
||
|
|
count = this._count,
|
||
|
|
lastPoint = points[points.length - 2];
|
||
|
|
|
||
|
|
ctx.beginPath();
|
||
|
|
ctx.moveTo(lastPoint.x, lastPoint.y);
|
||
|
|
ctx.lineTo(pointer.x, pointer.y);
|
||
|
|
ctx.stroke();
|
||
|
|
|
||
|
|
for (i = 0; i < points.length; i++) {
|
||
|
|
dx = points[i].x - points[count].x;
|
||
|
|
dy = points[i].y - points[count].y;
|
||
|
|
d = dx * dx + dy * dy;
|
||
|
|
|
||
|
|
if (d < 4000 && Math.random() > d / 2000) {
|
||
|
|
ctx.beginPath();
|
||
|
|
ctx.moveTo(points[count].x + (dx * factor), points[count].y + (dy * factor));
|
||
|
|
ctx.lineTo(points[i].x - (dx * factor), points[i].y - (dy * factor));
|
||
|
|
ctx.stroke();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
this._count++;
|
||
|
|
},
|
||
|
|
|
||
|
|
onMouseUp: function(pointer) {
|
||
|
|
if (this._count > 0) {
|
||
|
|
this.convertToImg();
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
_render: function() {}
|
||
|
|
}); // End SketchyBrush
|
||
|
|
|
||
|
|
/**
|
||
|
|
* SpraypaintBrush
|
||
|
|
* Based on code by Tennison Chan.
|
||
|
|
*/
|
||
|
|
fabric.SpraypaintBrush = fabric.util.createClass(fabric.BaseBrush, {
|
||
|
|
color: '#000',
|
||
|
|
opacity: 1,
|
||
|
|
width: 30,
|
||
|
|
|
||
|
|
_baseWidth: 40,
|
||
|
|
_inkAmount: 0,
|
||
|
|
_interval: 20,
|
||
|
|
_lastPoint: null,
|
||
|
|
_point: null,
|
||
|
|
brush: null,
|
||
|
|
sprayBrushDataUrl: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAT0AAAE/CAMAAAADjeSkAAAyGmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS4zLWMwMTEgNjYuMTQ1NjYxLCAyMDEyLzAyLzA2LTE0OjU2OjI3ICAgICAgICAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iPgogICAgICAgICA8eG1wOkNyZWF0b3JUb29sPkFkb2JlIEZpcmV3b3JrcyBDUzYgKE1hY2ludG9zaCk8L3htcDpDcmVhdG9yVG9vbD4KICAgICAgICAgPHhtcDpDcmVhdGVEYXRlPjIwMjEtMDUtMjdUMTQ6NTg6NTVaPC94bXA6Q3JlYXRlRGF0ZT4KICAgICAgICAgPHhtcDpNb2RpZnlEYXRlPjIwMjEtMDUtMjdUMTQ6NTk6MDJaPC94bXA6TW9kaWZ5RGF0ZT4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgICAgIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICAgICAgICAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyI+CiAgICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2UvcG5nPC9kYzpmb3JtYXQ+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAg
|
||
|
|
|
||
|
|
initialize: function(canvas, opt) {
|
||
|
|
var self = this;
|
||
|
|
opt = opt || {};
|
||
|
|
|
||
|
|
this.canvas = canvas;
|
||
|
|
this.width = opt.width || this.width;
|
||
|
|
this.opacity = opt.opacity || this.opacity;
|
||
|
|
this.color = opt.color || this.color;
|
||
|
|
|
||
|
|
this.canvas.contextTop.lineJoin = 'round';
|
||
|
|
this.canvas.contextTop.lineCap = 'round';
|
||
|
|
|
||
|
|
this._reset();
|
||
|
|
|
||
|
|
fabric.Image.fromURL(this.sprayBrushDataUrl, function(brush) {
|
||
|
|
self.brush = brush;
|
||
|
|
self.brush.filters = [];
|
||
|
|
self._setColor(self.color || this.color);
|
||
|
|
}, { crossOrigin: 'anonymous' });
|
||
|
|
},
|
||
|
|
|
||
|
|
|
||
|
|
_setColor: function(color) {
|
||
|
|
this.color = color;
|
||
|
|
this.brush.filters[0] = new fabric.Image.filters.BlendColor({ color:color, alpha:1, mode:'tint' });
|
||
|
|
this.brush.applyFilters();
|
||
|
|
},
|
||
|
|
|
||
|
|
onMouseDown: function(pointer) {
|
||
|
|
this.canvas.contextTop.globalAlpha = this.opacity;
|
||
|
|
this._point = new fabric.Point(pointer.x, pointer.y);
|
||
|
|
this._lastPoint = this._point;
|
||
|
|
|
||
|
|
// this.size = this.width + this._baseWidth;
|
||
|
|
this.size = this.width + this.width/2;
|
||
|
|
this._inkAmount = 0;
|
||
|
|
|
||
|
|
this._setColor(this.color);
|
||
|
|
this._render();
|
||
|
|
},
|
||
|
|
|
||
|
|
onMouseMove: function(pointer) {
|
||
|
|
this._lastPoint = this._point;
|
||
|
|
this._point = new fabric.Point(pointer.x, pointer.y);
|
||
|
|
},
|
||
|
|
|
||
|
|
onMouseUp: function() {
|
||
|
|
var self = this;
|
||
|
|
setTimeout(function() {
|
||
|
|
self.convertToImg();
|
||
|
|
self._reset();
|
||
|
|
}, this._interval);
|
||
|
|
},
|
||
|
|
|
||
|
|
_render: function() {
|
||
|
|
var self = this;
|
||
|
|
|
||
|
|
function draw() {
|
||
|
|
var point, distance, angle, amount, x, y;
|
||
|
|
|
||
|
|
point = new fabric.Point(self._point.x || 0, self._point.y || 0);
|
||
|
|
distance = point.distanceFrom(self._lastPoint);
|
||
|
|
angle = point.angleBetween(self._lastPoint);
|
||
|
|
amount = (100 / self.size) / (Math.pow(distance, 2) + 1);
|
||
|
|
|
||
|
|
self._inkAmount += amount;
|
||
|
|
self._inkAmount = Math.max(self._inkAmount - distance / 10, 0);
|
||
|
|
|
||
|
|
x = self._lastPoint.x + Math.sin(angle) - self.size / 2;
|
||
|
|
y = self._lastPoint.y + Math.cos(angle) - self.size / 2;
|
||
|
|
self.canvas.contextTop.drawImage(self.brush._element, x, y, self.size, self.size);
|
||
|
|
|
||
|
|
if (self.canvas._isCurrentlyDrawing) {
|
||
|
|
setTimeout(draw, self._interval);
|
||
|
|
} else {
|
||
|
|
self._reset();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
draw();
|
||
|
|
},
|
||
|
|
|
||
|
|
_reset: function() {
|
||
|
|
this._point = null;
|
||
|
|
this._lastPoint = null;
|
||
|
|
this.canvas.contextTop.globalAlpha = 1;
|
||
|
|
}
|
||
|
|
}); // End SpraypaintBrush
|
||
|
|
|
||
|
|
/**
|
||
|
|
* SquaresBrush
|
||
|
|
* Based on code by Mr. Doob.
|
||
|
|
*/
|
||
|
|
fabric.SquaresBrush = fabric.util.createClass(fabric.BaseBrush, {
|
||
|
|
color: '#000',
|
||
|
|
bgColor: '#fff',
|
||
|
|
opacity: 1,
|
||
|
|
width: 1,
|
||
|
|
|
||
|
|
_lastPoint: null,
|
||
|
|
_drawn: false,
|
||
|
|
|
||
|
|
initialize: function(canvas, opt) {
|
||
|
|
opt = opt || {};
|
||
|
|
|
||
|
|
this.canvas = canvas;
|
||
|
|
this.width = opt.width || this.width;
|
||
|
|
this.color = opt.color || this.color;
|
||
|
|
this.bgColor = opt.bgColor || '#fff';
|
||
|
|
this.opacity = opt.opacity || this.opacity;
|
||
|
|
},
|
||
|
|
|
||
|
|
onMouseDown: function(pointer) {
|
||
|
|
var ctx = this.canvas.contextTop,
|
||
|
|
color = fabric.util.colorValues(this.color),
|
||
|
|
bgColor = fabric.util.colorValues(this.bgColor);
|
||
|
|
|
||
|
|
this._lastPoint = pointer;
|
||
|
|
this._drawn = false;
|
||
|
|
|
||
|
|
//ctx.globalCompositeOperation = 'source-over';
|
||
|
|
this.canvas.contextTop.globalAlpha = this.opacity;
|
||
|
|
ctx.fillStyle = 'rgba(' + bgColor[0] + ',' + bgColor[1] + ',' + bgColor[2] + ',' + bgColor[3] + ')';
|
||
|
|
ctx.strokeStyle = 'rgba(' + color[0] + ',' + color[1] + ',' + color[2] + ',' + color[3] + ')';
|
||
|
|
ctx.lineWidth = this.width;
|
||
|
|
},
|
||
|
|
|
||
|
|
onMouseMove: function(pointer) {
|
||
|
|
var ctx = this.canvas.contextTop,
|
||
|
|
dx = pointer.x - this._lastPoint.x,
|
||
|
|
dy = pointer.y - this._lastPoint.y,
|
||
|
|
angle = 1.57079633,
|
||
|
|
px = Math.cos(angle) * dx - Math.sin(angle) * dy,
|
||
|
|
py = Math.sin(angle) * dx + Math.cos(angle) * dy;
|
||
|
|
|
||
|
|
ctx.beginPath();
|
||
|
|
ctx.moveTo(this._lastPoint.x - px, this._lastPoint.y - py);
|
||
|
|
ctx.lineTo(this._lastPoint.x + px, this._lastPoint.y + py);
|
||
|
|
ctx.lineTo(pointer.x + px, pointer.y + py);
|
||
|
|
ctx.lineTo(pointer.x - px, pointer.y - py);
|
||
|
|
ctx.lineTo(this._lastPoint.x - px, this._lastPoint.y - py);
|
||
|
|
ctx.fill();
|
||
|
|
ctx.stroke();
|
||
|
|
|
||
|
|
this._lastPoint = pointer;
|
||
|
|
this._drawn = true;
|
||
|
|
},
|
||
|
|
|
||
|
|
onMouseUp: function(pointer) {
|
||
|
|
if (this._drawn) {
|
||
|
|
this.convertToImg();
|
||
|
|
}
|
||
|
|
this.canvas.contextTop.globalAlpha = 1;
|
||
|
|
},
|
||
|
|
|
||
|
|
_render: function() {}
|
||
|
|
}); // End SquaresBrush
|
||
|
|
|
||
|
|
/**
|
||
|
|
* WebBrush
|
||
|
|
* Based on code by Mr. Doob.
|
||
|
|
*/
|
||
|
|
fabric.WebBrush = fabric.util.createClass(fabric.BaseBrush, {
|
||
|
|
color: '#000',
|
||
|
|
opacity: 1,
|
||
|
|
width: 1,
|
||
|
|
|
||
|
|
_count: 0,
|
||
|
|
_points: [],
|
||
|
|
|
||
|
|
initialize: function(canvas, opt) {
|
||
|
|
opt = opt || {};
|
||
|
|
|
||
|
|
this.canvas = canvas;
|
||
|
|
this.width = opt.width || this.width;
|
||
|
|
this.color = opt.color || this.color;
|
||
|
|
this.opacity = opt.opacity || 1;
|
||
|
|
},
|
||
|
|
|
||
|
|
onMouseDown: function(pointer) {
|
||
|
|
this._points = [pointer];
|
||
|
|
this._count = 0;
|
||
|
|
this._colorValues = fabric.util.colorValues(this.color);
|
||
|
|
},
|
||
|
|
|
||
|
|
onMouseMove: function(pointer) {
|
||
|
|
this._points.push(pointer);
|
||
|
|
|
||
|
|
var ctx = this.canvas.contextTop,
|
||
|
|
points = this._points,
|
||
|
|
lastPoint = points[points.length - 2],
|
||
|
|
colorValues = this._colorValues,
|
||
|
|
i, dx, dy, d;
|
||
|
|
|
||
|
|
ctx.lineWidth = this.width;
|
||
|
|
ctx.strokeStyle = 'rgba(' + colorValues[0] + ',' + colorValues[1] + ',' + colorValues[2] + ',' + (.5 * this.opacity) + ')';
|
||
|
|
|
||
|
|
ctx.beginPath();
|
||
|
|
ctx.moveTo(lastPoint.x, lastPoint.y);
|
||
|
|
ctx.lineTo(pointer.x, pointer.y);
|
||
|
|
ctx.stroke();
|
||
|
|
|
||
|
|
ctx.strokeStyle = 'rgba(' + colorValues[0] + ',' + colorValues[1] + ',' + colorValues[2] + ',' + (.1 * this.opacity) + ')';
|
||
|
|
|
||
|
|
for (i = 0; i < points.length; i++) {
|
||
|
|
dx = points[i].x - points[this._count].x;
|
||
|
|
dy = points[i].y - points[this._count].y;
|
||
|
|
d = dx * dx + dy * dy;
|
||
|
|
|
||
|
|
if (d < 2500 && Math.random() > .9) {
|
||
|
|
ctx.beginPath();
|
||
|
|
ctx.moveTo(points[this._count].x, points[this._count].y);
|
||
|
|
ctx.lineTo(points[i].x, points[i].y);
|
||
|
|
ctx.stroke();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
this._count++;
|
||
|
|
},
|
||
|
|
|
||
|
|
onMouseUp: function(pointer) {
|
||
|
|
if (this._count > 0) {
|
||
|
|
this.convertToImg();
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
_render: function() {}
|
||
|
|
}); // End WebBrush
|
||
|
|
|
||
|
|
})(typeof fabric !== 'undefined' ? fabric : require('fabric').fabric);
|