@jianghh/canvas-graffiti
Version:
canvas涂鸦库,支持手写、笔写、鼠标,选中元素,并且移动、删除等功能。集成撤销重做操作。
309 lines (308 loc) • 12.4 kB
JavaScript
var L = Object.defineProperty;
var B = (s, t, i) => t in s ? L(s, t, { enumerable: !0, configurable: !0, writable: !0, value: i }) : s[t] = i;
var h = (s, t, i) => (B(s, typeof t != "symbol" ? t + "" : t, i), i), E = (s, t, i) => {
if (!t.has(s))
throw TypeError("Cannot " + i);
};
var o = (s, t, i) => (E(s, t, "read from private field"), i ? i.call(s) : t.get(s)), l = (s, t, i) => {
if (t.has(s))
throw TypeError("Cannot add the same private member more than once");
t instanceof WeakSet ? t.add(s) : t.set(s, i);
}, r = (s, t, i, e) => (E(s, t, "write to private field"), e ? e.call(s, i) : t.set(s, i), i);
var p = (s, t, i) => (E(s, t, "access private method"), i);
import { tools as S } from "./tools/index.js";
import { CacheStack as z } from "./stack/index.js";
import { GraffitiEle as G } from "./element/index.js";
import { genRectByTwoPoint as Y, isRectIntersect as q } from "./element/index.js";
import H from "./assets/cursor.png.js";
import W from "./assets/eraser.png.js";
import { updateCtx as T } from "./utils/index.js";
import { EleGroup as X } from "./element/group.js";
const j = "#1493ee";
var d, g, C, x, u, f, w, a, c, m, k, v, b;
class F {
constructor(t, i) {
l(this, m);
// 改变画布宽高
l(this, v);
h(this, "options", {
initialTool: "Marker",
createBufferCanvasStyle: {},
allowType: ["pen", "mouse", "touch"],
allowButton: [0],
shadowColor: "rgba(1,1,1,0.6)",
shadowBlur: 0,
lineWidth: 2,
color: "#333"
});
h(this, "customizeHandle");
// canvas 容器
h(this, "el");
// 存储栈
h(this, "cacheStack");
h(this, "beginPoint", { x: 0, y: 0 });
h(this, "endPoint");
h(this, "points", []);
// 工具名
l(this, d, void 0);
// 离屏渲染画布
h(this, "bufferCanvas");
// 允许绘图方式
h(this, "allowType");
// 鼠标允许绘图方式
h(this, "allowButton");
// 涂鸦元素数组
h(this, "graffitiEleList", []);
// 设备分辨倍率
l(this, g, 1);
// 画布样式宽度
l(this, C, void 0);
// 画布样式高度
l(this, x, void 0);
// 元素组对象实例
h(this, "eleGroup");
// 全局画笔粗细
l(this, u, void 0);
// 全局填充色
l(this, f, void 0);
// 全局描边色
l(this, w, void 0);
// 阴影颜色
l(this, a, void 0);
// 阴影范围大小
l(this, c, void 0);
Object.assign(this.options, t), i && (this.customizeHandle = i), this.dpr = t.devicePixelRatio || window.devicePixelRatio, typeof this.options.el == "string" ? this.el = document.querySelector(this.options.el) : this.el = this.options.el, this.options.width ? this.width = this.options.width : this.width = this.el.width, this.options.height ? this.height = this.options.height : this.height = this.el.height, this.options.lineWidth && (this.lineWidth = this.options.lineWidth), this.options.color && (this.strokeStyle = this.fillStyle = this.options.color), r(this, c, this.options.shadowBlur || 0), r(this, a, this.options.shadowColor || "rgba(1,1,1,0.6)"), this.toolName = this.options.initialTool, this.allowType = this.options.allowType, this.allowButton = this.options.allowButton, this.ctx.scale(this.dpr, this.dpr), this.cacheStack = new z(t.cacheSize || 5), this.init(), p(this, m, k).call(this);
}
get width() {
return o(this, C);
}
set width(t) {
r(this, C, t), this.ctx.canvas.width = t * this.dpr, this.ctx.canvas.style.width = t + "px";
}
get height() {
return o(this, x);
}
set height(t) {
r(this, x, t), this.ctx.canvas.height = t * this.dpr, this.ctx.canvas.style.height = t + "px";
}
set lineWidth(t) {
r(this, u, t), this.ctx.lineWidth = t;
}
get lineWidth() {
return o(this, u);
}
set dpr(t) {
r(this, g, t);
}
get dpr() {
return o(this, g);
}
set strokeStyle(t) {
r(this, w, t), this.ctx.strokeStyle = t;
}
get strokeStyle() {
return o(this, w);
}
set fillStyle(t) {
r(this, f, t), this.ctx.fillStyle = t;
}
get fillStyle() {
return o(this, f);
}
set shadowColor(t) {
r(this, a, t), this.ctx.shadowColor = t;
}
get shadowColor() {
return o(this, a);
}
set shadowBlur(t) {
r(this, c, t), this.ctx.shadowBlur = t;
}
get shadowBlur() {
return o(this, c);
}
// 当前工具
get tool() {
return S[o(this, d)];
}
set toolName(t) {
var i, e;
this.eleGroup && (this.eleGroup.cancelSelected(), (e = (i = this.customizeHandle) == null ? void 0 : i.onGroupHandle) == null || e.call(this, this.eleGroup)), t === "Cursor" ? this.ctx.canvas.style.cursor = "url(" + H + "), auto" : t === "Erase" ? this.ctx.canvas.style.cursor = "url(" + W + "), auto" : this.ctx.canvas.style.cursor = "crosshair", r(this, d, t);
}
get toolName() {
return o(this, d);
}
// 上下文
get ctx() {
return this.el.getContext("2d");
}
// 离屏渲染画布上下文
get bufferCtx() {
var t;
return (t = this.bufferCanvas) == null ? void 0 : t.getContext("2d");
}
/**
* 初始化
*/
init() {
this.el.style.touchAction = "none", this.pointerdown = this.pointerdown.bind(this), this.pointermove = this.pointermove.bind(this), this.pointerup = this.pointerup.bind(this), this.bindCanvasEventListener();
}
updateAllowType(t) {
this.allowType = t;
}
plugin(...t) {
t.forEach((i) => {
S[i.name] ? console.error("tool注入失败,已有同名的tool对象,请检查") : S[i.name] = i.tool;
});
}
// 绑定事件
bindCanvasEventListener() {
this.el.addEventListener("pointerdown", this.pointerdown);
}
// 解绑事件
removeEventListener(t = ["pointerdown", "pointermove", "pointerup"]) {
t.forEach((i) => {
this.el.removeEventListener(i, this[i]), document.removeEventListener(i, this[i]);
});
}
// 按下事件
pointerdown(t) {
var i, e;
if (!this.allowType.includes("touch") && t.pointerType === "touch") {
if (t.pressure === 0)
return;
} else if (!this.allowType.includes(t.pointerType))
return;
t.pointerType === "mouse" && !this.allowButton.includes(t.button) || (this.beginPoint = { x: t.offsetX, y: t.offsetY }, t.preventDefault(), this.tool.buffer && this.createBufferCanvas(), this.cacheStack.preItem || this.emitStackChange(), this.tool && ((e = (i = this.tool) == null ? void 0 : i.pointerdown) == null || e.call(this, t)), this.el.addEventListener("pointermove", this.pointermove), document.addEventListener("pointerup", this.pointerup));
}
// 拖动事件
pointermove(t) {
var i, e;
if (!this.allowType.includes("touch") && t.pointerType === "touch") {
if (t.pressure === 0)
return;
} else if (!this.allowType.includes(t.pointerType))
return;
t.preventDefault(), (e = (i = this.tool) == null ? void 0 : i.pointermove) == null || e.call(this, t);
}
// 抬起事件
pointerup(t) {
var i, e, n;
if (!this.allowType.includes("touch") && t.pointerType === "touch") {
if (t.pressure === 0)
return;
} else if (!this.allowType.includes(t.pointerType))
return;
t.preventDefault(), (e = (i = this.tool) == null ? void 0 : i.pointerup) == null || e.call(this, t), this.removeEventListener(["pointermove", "pointerup"]), this.toolName !== "Cursor" && this.emitStackChange(), this.beginPoint = { x: 0, y: 0 }, this.endPoint = void 0, this.points = [], (n = this.bufferCanvas) == null || n.remove();
}
// 撤销
revoke() {
var i, e, n, y;
const t = this.cacheStack.pop();
t && this.setCanvasData(t), (e = (i = this.customizeHandle) == null ? void 0 : i.onActionHandle) == null || e.call(this, t, this.cacheStack.revokeSize, this.cacheStack.redoSize), this.eleGroup && (this.eleGroup = null, (y = (n = this.customizeHandle) == null ? void 0 : n.onGroupHandle) == null || y.call(this, null));
}
// 重做
redo() {
var i, e, n, y;
const t = this.cacheStack.popRedo();
t && this.setCanvasData(t), (e = (i = this.customizeHandle) == null ? void 0 : i.onActionHandle) == null || e.call(this, t, this.cacheStack.revokeSize, this.cacheStack.redoSize), this.eleGroup && (this.eleGroup = null, (y = (n = this.customizeHandle) == null ? void 0 : n.onGroupHandle) == null || y.call(this, null));
}
updateCanvasSize(t) {
p(this, v, b).call(this, t), this.emitStackChange();
}
// 清空画布
flush() {
this.ctx.clearRect(0, 0, this.el.width, this.el.height);
}
// 清除画布,同时删除内部元素
clear() {
this.flush(), this.graffitiEleList = [], this.eleGroup = null;
}
emitStackChange() {
var i, e;
const t = this.getCanvasData();
this.cacheStack.push(t), (e = (i = this.customizeHandle) == null ? void 0 : i.onActionHandle) == null || e.call(this, t, this.cacheStack.revokeSize, this.cacheStack.redoSize);
}
// 绘制ele元素数据
drawEles(t) {
this.ctx.save(), t ? t.forEach((i) => {
var e;
T(this, i), (e = S[i.tool].drawEle) == null || e.call(this, i.points);
}) : (this.graffitiEleList = this.graffitiEleList.filter((i) => !i.isDeleted), this.graffitiEleList.forEach((i) => {
var e;
T(this, i), (e = S[i.tool].drawEle) == null || e.call(this, i.points);
})), this.ctx.restore();
}
// 获取足够重绘的必要数据
getCanvasData() {
return {
eleInfoList: this.graffitiEleList.map((i) => ({
tool: i.tool,
left: i.left,
top: i.top,
right: i.right,
bottom: i.bottom,
points: i.points,
shadowColor: i.shadowColor,
lineWidth: i.lineWidth,
shadowBlur: i.shadowBlur,
strokeStyle: i.strokeStyle,
fillStyle: i.fillStyle
})),
width: this.width,
height: this.height,
lineWidth: this.lineWidth,
shadowBlur: this.shadowBlur,
shadowColor: this.shadowColor,
fillStyle: this.fillStyle,
strokeStyle: this.strokeStyle,
dpr: this.dpr
};
}
//重写画布内容
setCanvasData(t) {
this.flush(), this.graffitiEleList = t.eleInfoList.map((i) => new G(i)), this.width !== t.width || this.height !== t.height ? (Object.assign(this, t), p(this, v, b).call(this, { width: t.width, height: t.height })) : (Object.assign(this, t), this.ctx.scale(this.dpr, this.dpr), p(this, m, k).call(this), this.drawEles());
}
// 创建离屏渲染画布
createBufferCanvas() {
const t = this.el.cloneNode();
t.style.zIndex += 100, t.style.pointerEvents = "none", t.style.position = "absolute", t.style.left = this.el.offsetLeft + "px", t.style.top = this.el.offsetTop + "px";
const i = this.options.createBufferCanvasStyle;
if (i)
for (const e in i)
t.setAttribute(e, i[e]);
this.el.parentElement.appendChild(t), this.bufferCanvas = t, this.bufferCtx.scale(this.dpr, this.dpr), this.bufferCtx.fillStyle = this.fillStyle, this.bufferCtx.strokeStyle = this.strokeStyle, this.bufferCtx.globalAlpha = 0.3, this.bufferCtx.lineWidth = 1;
}
// 销毁
destroy(t) {
this.removeEventListener(), this.cacheStack.clear(), this.clear(), this.options = null, this.customizeHandle = null, t && this.el.remove();
}
// 获取 canvas.toDataURL
toDataURL(t = "image/png", i = 0.92) {
return this.el.toDataURL(t, i);
}
// 获取 canvas 的图片文件
toPicFile(t = "canvas.png") {
return new Promise((i) => {
this.el.toBlob((e) => {
e && i(new File([e], t));
});
});
}
}
d = new WeakMap(), g = new WeakMap(), C = new WeakMap(), x = new WeakMap(), u = new WeakMap(), f = new WeakMap(), w = new WeakMap(), a = new WeakMap(), c = new WeakMap(), m = new WeakSet(), k = function() {
this.ctx.lineCap = "round", this.ctx.lineJoin = "round", this.ctx.imageSmoothingEnabled = !0, this.fillStyle = o(this, f), this.strokeStyle = o(this, w), this.lineWidth = o(this, u), this.shadowBlur = o(this, c), this.shadowColor = o(this, a);
}, v = new WeakSet(), b = function(t) {
const { width: i, height: e } = t;
this.cacheStack.preItem || this.emitStackChange(), i && (this.width = i), e && (this.height = e), p(this, m, k).call(this), this.ctx.scale(this.dpr, this.dpr), this.drawEles();
};
export {
z as CacheStack,
F as CanvasGraffiti,
X as EleGroup,
G as GraffitiEle,
j as SYSTEM_COLOR,
F as default,
Y as genRectByTwoPoint,
q as isRectIntersect
};