jianghh-canvas
Version:
canvas画板,支持笔写、手写、鼠标流畅绘图,自动保存,撤销重做
223 lines (222 loc) • 8.16 kB
JavaScript
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
};