Files
FiDA_Front/src/components/Canvas/DepthCanvas/manager/brushes/fabric.brushes.js
2026-03-11 15:34:56 +08:00

1597 lines
47 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/
*/
import { fabric } from "fabric-with-all";
import { sprayBrushDataUrl } from "./data/sprayBrushData.js";
(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.convertToPath = function () {
var pixelRatio = this.canvas.getRetinaScaling(),
c = fabric.util.copyCanvasElement(this.canvas.upperCanvasEl),
xy = fabric.util.trimCanvas(c),
path = this._points
.map((arr) => {
arr[1] = arr[1] / this.canvas.getZoom();
arr[2] = arr[2] / this.canvas.getZoom();
return arr.join(" ");
})
.join(" "),
pathElemetn = new fabric.Path(path);
if (!xy) {
return;
}
let pointerX = this.canvas.viewportTransform[4];
let pointerY = this.canvas.viewportTransform[5];
pathElemetn
.set({
strokeDashArray: [this._width * 3, this._width * 3],
strokeWidth: this._width,
stroke: "black",
fill: "transparent",
custom: {
dashed: true,
},
})
.setCoords();
let group = new fabric.Group([pathElemetn], {
left: (xy.x / pixelRatio - pointerX) / this.canvas.getZoom(),
top: (xy.y / pixelRatio - pointerY) / this.canvas.getZoom(),
custom: {
dashed: true,
},
});
this.canvas.add(group).clearContext(this.canvas.contextTop);
this.canvas.clearContext(this.canvas.contextTop);
};
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;
}
let pointerX = this.canvas.viewportTransform[4];
let pointerY = this.canvas.viewportTransform[5];
img
.set({
left: (xy.x / pixelRatio - pointerX) / this.canvas.getZoom(),
top: (xy.y / pixelRatio - pointerY) / this.canvas.getZoom(),
scaleX: 1 / pixelRatio / this.canvas.getZoom(),
scaleY: 1 / pixelRatio / this.canvas.getZoom(),
custom: {
type: "pencil",
},
})
.setCoords();
// 检查是否有图像处理回调函数
const result = this.canvas?.onBrushImageConverted?.(img);
!result && this.canvas.add(img);
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) {
pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];
this.canvas.contextTop.globalAlpha = this.opacity;
this._size = this.width / 2 + this._baseWidth;
this._drawn = false;
this.set(pointer);
},
onMouseMove: function (pointer) {
pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];
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) {
// 添加坐标转换处理画布缩放和偏移
pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];
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) {
// 添加坐标转换处理画布缩放和偏移
pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];
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) {
pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];
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) {
pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];
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) {
pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];
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) {
pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];
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) {
pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];
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) {
pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];
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) {
pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];
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) {
pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];
this._render(pointer);
}
},
onMouseUp: function () {
this.canvas.contextTop.globalAlpha = this.opacity;
this.canvas.contextTop.globalAlpha = 1;
this.convertToImg();
},
}); // End MarkerBrush
/**
* test
* Based on code by Tennison Chan.
*/
fabric.Test = fabric.util.createClass(fabric.BaseBrush, {
color: "#000",
opacity: 1,
_points: [],
_width: 2,
initialize: function (canvas, opt) {
opt = opt || {};
this.canvas = canvas;
this._width = opt._width || this._width;
this.color = opt.color || this.color;
},
_render: function (pointer) {
var ctx, lineWidthDiff, i, len;
ctx = this.canvas.contextTop;
this._points.push(["L", pointer.x, pointer.y]);
// if(this._points.length % 10 < 5){
let points = this._points;
ctx.beginPath();
ctx.moveTo(points[points.length - 2][1], points[points.length - 2][2]);
ctx.lineTo(points[points.length - 1][1], points[points.length - 1][2]);
ctx.stroke();
// }
},
onMouseDown: function (pointer) {
pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];
this._points = [];
this._points.push(["M", pointer.x, pointer.y]);
var ctx = this.canvas.contextTop;
ctx.strokeStyle = "rgba(" + 0 + "," + 0 + "," + 0 + "," + 1 + ")";
ctx.lineWidth = this._width * this.canvas.getZoom();
ctx.lineJoin = ctx.lineCap = "round";
},
onMouseMove: function (pointer) {
if (this.canvas._isCurrentlyDrawing) {
pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];
this._render(pointer);
}
},
onMouseUp: function () {
this._points.push(["Z"]);
this.convertToPath();
},
}); // End test
/**
* 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 =
this.content > this._lastPoint.x
? this.content - this._lastPoint.x + this.content
: this.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) {
// 添加坐标转换处理画布缩放和偏移
pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];
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) {
// 添加坐标转换处理画布缩放和偏移
pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];
this._render(pointer);
}
},
onMouseUp: function () {
this.canvas.contextTop.globalAlpha = this.opacity;
this.canvas.contextTop.globalAlpha = 1;
this.convertToImg();
},
}); // End MarkerBrush1
/**
* 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;
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) {
pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];
this._lastPoint = pointer;
this.canvas.contextTop.lineWidth = this._lineWidth;
this._size = this.width + this._baseWidth;
},
onMouseMove: function (pointer) {
if (this.canvas._isCurrentlyDrawing) {
pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];
this._render(pointer);
}
},
onMouseUp: function () {
this.canvas.contextTop.globalAlpha = this.opacity;
this.canvas.contextTop.globalAlpha = 1;
this.convertToImg();
},
}); // End PenBrush
/**
* 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: 0.1,
ease: Math.random() * 0.2 + 0.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: 0.1,
ease: Math.random() * 0.2 + 0.6,
});
}
pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];
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 (let 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) {
pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];
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: 0.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) {
pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];
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) {
pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];
this._points.push(pointer);
var ctx = this.canvas.contextTop,
points = this._points,
dx,
dy,
d; // 在此处声明变量
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.canvas.contextTop.globalAlpha = this.opacity;
// 坐标转换
pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];
this._points = [pointer];
this._count = 0;
this._drawn = false;
// 设置绘图样式
var ctx = this.canvas.contextTop;
var color = fabric.util.colorValues(this.color);
ctx.lineWidth = this.width;
ctx.strokeStyle =
"rgba(" + color[0] + "," + color[1] + "," + color[2] + "," + 0.3 * this.opacity + ")";
},
onMouseMove: function (pointer) {
// 坐标转换
pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];
this._points.push(pointer);
var i, dx, dy, d;
var factor = 0.3 * this.width;
var ctx = this.canvas.contextTop;
var points = this._points;
var lastPoint = points.length > 1 ? points[points.length - 2] : points[0]; // 修复第一次无上一个点的问题
// 绘制主线条
ctx.beginPath();
ctx.moveTo(lastPoint.x, lastPoint.y);
ctx.lineTo(pointer.x, pointer.y);
ctx.stroke();
// 增加透明度设置
var color = fabric.util.colorValues(this.color);
ctx.strokeStyle =
"rgba(" + color[0] + "," + color[1] + "," + color[2] + "," + 0.2 * this.opacity + ")";
// 修改循环逻辑,确保在有点时能画出效果
if (this._count > 0) {
// 确保有历史点可用
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 < 4000 && Math.random() > d / 2000) {
ctx.beginPath();
ctx.moveTo(points[this._count].x + dx * factor, points[this._count].y + dy * factor);
ctx.lineTo(points[i].x - dx * factor, points[i].y - dy * factor);
ctx.stroke();
this._drawn = true;
}
}
}
this._count++;
},
onMouseUp: function (pointer) {
if (this._count > 0 || this._drawn) {
this.convertToImg();
}
this.canvas.contextTop.globalAlpha = 1;
},
_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: sprayBrushDataUrl,
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;
pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];
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) {
pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];
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);
pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];
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) {
pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];
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) {
pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];
this._points = [pointer];
this._count = 0;
this._colorValues = fabric.util.colorValues(this.color);
},
onMouseMove: function (pointer) {
pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];
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] +
"," +
0.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] +
"," +
0.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() > 0.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);