Files
aida_front/public/js/fabric.brushes.js

1286 lines
110 KiB
JavaScript
Raw Normal View History

2024-03-15 09:21:17 +08:00
/**
* 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) {
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);
2024-03-20 13:34:07 +08:00
2024-03-15 09:21:17 +08:00
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
2024-03-20 13:34:07 +08:00
/**
* PenBrush
* Based on code by Tennison Chan.
*/
fabric.PenBrush = 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++) {
var randomNum = Math.random() * (0.6 - 0.2) + 0.2;
var color = this.color.replace(/1(?=\))/, randomNum);
this.canvas.contextTop.strokeStyle = color;
console.log(color);
lineWidthDiff = (this._lineWidth - 1) * i;
ctx.globalAlpha = 0.8 * this.opacity;
ctx.moveTo(this._lastPoint.x, this._lastPoint.y + lineWidthDiff-num);
ctx.lineTo(pointer.x, pointer.y + lineWidthDiff-num);
ctx.stroke();
}
this._lastPoint = new fabric.Point(pointer.x, pointer.y);
},
onMouseDown: function(pointer) {
this._lastPoint = pointer;
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 PenBrush
2024-03-15 09:21:17 +08:00
/**
* 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);