UNPKG

@jianghh/canvas-graffiti

Version:

canvas涂鸦库,支持手写、笔写、鼠标,选中元素,并且移动、删除等功能。集成撤销重做操作。

309 lines (308 loc) 12.4 kB
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 };