1597 lines
47 KiB
JavaScript
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);
|