UNPKG

jianghh-canvas

Version:

canvas画板,支持笔写、手写、鼠标流畅绘图,自动保存,撤销重做

223 lines (222 loc) 8.16 kB
var c = Object.defineProperty; var p = (n, t, e) => t in n ? c(n, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : n[t] = e; var i = (n, t, e) => (p(n, typeof t != "symbol" ? t + "" : t, e), e); import * as u from "./utils/index.js"; import { useStack as f } from "./stack/index.js"; import { useEventBus as d } from "./event/index.js"; class r { constructor(t) { i(this, "options", { currentTool: "Marker", createBufferCanvasStyle: {}, allowType: ["pen", "mouse", "touch"], allowButton: [0], eraseSize: 16 }); // canvas 容器 i(this, "el"); // 当前是否可撤销 i(this, "isRevoke", !1); // 存储栈 i(this, "cacheStack"); // 起点 i(this, "beginPoint", { x: 0, y: 0 }); // 终点 i(this, "endPoint"); // 移动轨迹 i(this, "points", []); // 工具名 i(this, "currentTool", "Marker"); // 离屏渲染画布 i(this, "bufferCanvas"); // 允许绘图方式 i(this, "allowType"); // 鼠标允许绘图方式 i(this, "allowButton"); i(this, "containerHeightOffset", 0); i(this, "dpr", 1); i(this, "width"); i(this, "height"); i(this, "eraseSize"); i(this, "$on"); i(this, "$emit"); i(this, "$off"); Object.assign(this.options, t), this.dpr = window.devicePixelRatio, typeof this.options.el == "string" ? this.el = document.querySelector(this.options.el) : this.el = this.options.el, this.options.containerHeightOffset && (this.containerHeightOffset = this.options.containerHeightOffset), this.options.width && (this.el.style.width = this.options.width + "px", this.width = this.ctx.canvas.width = this.options.width * this.dpr), this.options.height && (this.el.style.height = this.options.height - this.containerHeightOffset + "px", this.height = this.ctx.canvas.height = (this.options.height - this.containerHeightOffset) * this.dpr), this.ctx.scale(this.dpr, this.dpr), this.options.lineWidth && (this.lineWidth = this.options.lineWidth), this.options.color && (this.color = this.options.color), this.allowType = this.options.allowType, this.allowButton = this.options.allowButton, this.eraseSize = this.options.eraseSize, this.options.currentTool === "Cursor" ? this.el.style.touchAction = "auto" : this.el.style.touchAction = "none", this.currentTool = this.options.currentTool, f(this, t.cacheSize), d(this), this.init(); } // color get color() { return this.ctx.fillStyle; } set color(t) { this.ctx.strokeStyle = t, this.ctx.fillStyle = t; } // 线宽 get lineWidth() { return this.ctx.lineWidth; } set lineWidth(t) { this.ctx.lineWidth = t; } // 当前工具 get tool() { return u[this.currentTool]; } set tool(t) { this.setCurrentTool(t); } // 上下文 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(), this.ctx.lineCap = "round", this.ctx.lineJoin = "round"; } fixContent(t, e) { this.ctx.lineCap = "round", this.ctx.lineJoin = "round", this.color = e, this.lineWidth = t; } // 绑定事件 bindCanvasEventListener() { this.el.addEventListener("pointerdown", this.pointerdown); } // 解绑事件 removeEventListener(t = ["pointerdown", "pointermove", "pointerup"]) { t.forEach((e) => { this.el.removeEventListener(e, this[e]), document.removeEventListener(e, this[e]); }); } // 改变画布高度 updateHeight(t) { this.cacheStack.preItem || this.emitStackChange(); const e = this.color, s = this.lineWidth; this.ctx.canvas.height = (t - this.containerHeightOffset) * this.dpr, this.ctx.canvas.style.height = t - this.containerHeightOffset + "px", this.reviewImg(this.cacheStack.preItem, !0, () => { this.fixContent(s, e), this.ctx.scale(this.dpr, this.dpr), this.emitStackChange(); }); } // 按下 pointerdown(t) { var e, s; t.pointerType === "mouse" && !this.allowButton.includes(t.button) || t.pressure !== 0 && (this.beginPoint = { x: t.offsetX, y: t.offsetY }, t.preventDefault(), this.tool.buffer && this.createBufferCanvas(), this.cacheStack.preItem || this.emitStackChange(), this.tool && ((s = (e = this.tool) == null ? void 0 : e.pointerdown) == null || s.call(this, t)), this.el.addEventListener("pointermove", this.pointermove), document.addEventListener("pointerup", this.pointerup)); } // 移动 pointermove(t) { var e, s; t.preventDefault(), (s = (e = this.tool) == null ? void 0 : e.pointermove) == null || s.call(this, t); } // 抬起 pointerup(t) { var e, s, o; t.preventDefault(), (s = (e = this.tool) == null ? void 0 : e.pointerup) == null || s.call(this, t), this.removeEventListener(["pointermove", "pointerup"]), this.emitStackChange(), this.beginPoint = { x: 0, y: 0 }, this.endPoint = void 0, this.points = [], (o = this.bufferCanvas) == null || o.remove(); } // 撤销 revoke() { const t = this.cacheStack.pop(); t && this.reviewImg(t, !1), this.$emit("cacheChange", t, this.cacheStack.revokeSize, this.cacheStack.redoSize); } redo() { const t = this.cacheStack.popRedo(); t && this.reviewImg(t, !1), this.$emit("cacheChange", t, this.cacheStack.revokeSize, this.cacheStack.redoSize); } // 清除画布 clear() { this.ctx.clearRect(0, 0, this.el.width, this.el.height); } emitStackChange() { const t = this.toDataURL(); this.cacheStack.push(t), this.$emit("cacheChange", t, this.cacheStack.revokeSize, this.cacheStack.redoSize); } reviewImg(t, e = !0, s) { const o = this; if (!t) return; let h = new Image(); h.src = t, h.onload = function() { if (e) o.clear(), o.ctx.drawImage(h, 0, 0, h.width, h.height), s && s(); else { const l = o.color, a = o.lineWidth; o.ctx.canvas.height = h.height, o.ctx.canvas.style.height = h.height / o.dpr + "px", o.el.parentElement.style.height = h.height / o.dpr + o.containerHeightOffset + "px", o.fixContent(a, l), o.ctx.drawImage(h, 0, 0, h.width, h.height), o.ctx.scale(o.dpr, o.dpr), s && s(); } h = null; }; } // 创建离屏渲染画布 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 e = this.options.createBufferCanvasStyle; if (e) for (const s in e) t.setAttribute(s, e[s]); this.el.parentElement.appendChild(t), this.bufferCanvas = t, this.bufferCtx.scale(this.dpr, this.dpr), this.bufferCtx.fillStyle = this.color, this.bufferCtx.strokeStyle = this.color, this.bufferCtx.lineWidth = this.lineWidth; } // 销毁 destroy(t) { this.removeEventListener(), this.cacheStack.clear(), t && this.el.remove(); } // 获取 canvas.toDataURL toDataURL(t = "image/png", e = 0.92) { return this.el.toDataURL(t, e); } // 获取 canvas 的图片文件 toPicFile(t = "0.png") { return new Promise((e) => { this.el.toBlob((s) => { s && e(new File([s], t)); }); }); } setCurrentTool(t) { t === "Cursor" ? this.el.style.touchAction = "auto" : this.el.style.touchAction = "none", this.currentTool = t; } } i(r, "toolList", [ { label: "光标", value: "Cursor" }, { label: "记号笔", value: "Marker" }, { label: "钢笔", value: "Pen" }, { label: "直线", value: "Line" }, { label: "空心矩形", value: "Rect" }, { label: "空心圆", value: "Arc" } ]), i(r, "allowTypes", [ { label: "鼠标", value: "mouse" }, { label: "手写", value: "touch" }, { label: "笔写", value: "pen" } ]); export { r as CanvasGraffiti, r as default };