<!-- 涂色连线题控件 -->
|
<template>
|
<div class="page">
|
<div class="main">
|
<div id="canvas_panel">
|
<canvas id="canvas" :style="{
|
backgroundSize: 'cover',
|
backgroundPosition: 'center',
|
}">当前浏览器不支持canvas。</canvas>
|
</div>
|
</div>
|
<div class="footer">
|
<BrushSize :size="brushSize" @change-size="onChangeSize" @change-color="onChangeColor" />
|
<ToolBtns :tool="brushTool" @change-tool="onChangeTool" />
|
</div>
|
</div>
|
</template>
|
|
<script>
|
import BrushSize from "./components/brushSize.vue";
|
import ToolBtns from "./components/toolBtns.vue";
|
export default {
|
name: "graffiti",
|
components: { BrushSize, ToolBtns },
|
props: {
|
save: {
|
type: Function,
|
},
|
},
|
data() {
|
return {
|
canvas: null,
|
context: null,
|
painting: false, // 记录状态,鼠标是否在按下状态
|
historyData: [], // 存储历史数据,用于撤销
|
brushSize: 2, // 笔刷大小
|
brushColor: "#000000", // 笔刷颜色
|
brushTool: "brush",
|
canvasOffset: {
|
left: 0,
|
top: 0,
|
},
|
};
|
},
|
mounted() {
|
this.canvas = (this.container ? this.container : document).getElementById(
|
"canvas"
|
);
|
if (this.canvas.getContext) {
|
this.context = this.canvas.getContext("2d", { willReadFrequently: true });
|
// window.addEventListener('resize', updateCanvasPosition);
|
(this.container ? this.container : document).addEventListener(
|
"scroll",
|
this.updateCanvasOffset,
|
true
|
); // 添加滚动条滚动事件监听器
|
this.getCanvasOffset();
|
this.context.lineGap = "round";
|
this.context.lineJoin = "round";
|
this.canvas.addEventListener("mousedown", this.downCallback);
|
this.canvas.addEventListener("mousemove", this.moveCallback);
|
this.canvas.addEventListener("mouseup", this.closePaint);
|
this.canvas.addEventListener("mouseleave", this.closePaint);
|
setTimeout(() => {
|
this.initCanvas();
|
}, 300);
|
}
|
this.toolClear();
|
},
|
methods: {
|
// 初始化 画布,设置大小背景色
|
initCanvas() {
|
const that = this;
|
const resetCanvas = () => {
|
const elPanel = (
|
this.container ? this.container : document
|
).getElementById("canvas_panel");
|
console.log("clientWidth"+elPanel.clientWidth);
|
console.log("clientWidth"+elPanel.clientHeight);
|
try {
|
that.canvas.width = elPanel.clientWidth;
|
that.canvas.height = elPanel.clientHeight;
|
} catch (error) { }
|
|
that.context = that.canvas.getContext("2d", {
|
willReadFrequently: true,
|
}); // 添加这一行
|
that.context.fillStyle = "white";
|
that.context.fillRect(0, 0, that.canvas.width, that.canvas.height);
|
that.context.fillStyle = "black";
|
that.getCanvasOffset(); // 更新画布位置
|
};
|
resetCanvas();
|
// 监听窗口大小 ,窗口改变重新渲染画布
|
window.addEventListener("resize", resetCanvas);
|
},
|
|
// 获取canvas的偏移值
|
getCanvasOffset() {
|
const rect = this.canvas.getBoundingClientRect();
|
this.canvasOffset.left = rect.left * (this.canvas.width / rect.width); // 兼容缩放场景
|
this.canvasOffset.top = rect.top * (this.canvas.height / rect.height);
|
},
|
|
// 计算当前鼠标相对于canvas的坐标
|
calcRelativeCoordinate(x, y) {
|
return {
|
x: x - this.canvasOffset.left,
|
y: y - this.canvasOffset.top,
|
};
|
},
|
// 鼠标抬起方法
|
downCallback(event) {
|
// 先保存之前的数据,用于撤销时恢复(绘制前保存,不是绘制后再保存)
|
const data = this.context.getImageData(
|
0,
|
0,
|
this.canvas.width,
|
this.canvas.height
|
);
|
this.saveData(data);
|
const { clientX, clientY } = event;
|
const { x, y } = this.calcRelativeCoordinate(clientX, clientY);
|
this.context.beginPath();
|
this.context.moveTo(x, y);
|
this.context.lineWidth = this.brushSize;
|
this.context.strokeStyle =
|
this.brushTool === "eraser" ? "#FFFFFF" : this.brushColor;
|
this.painting = true;
|
},
|
// 鼠标移动方法(计算坐标并渲染轨迹)
|
moveCallback(event) {
|
if (!this.painting) {
|
return;
|
}
|
const { clientX, clientY } = event;
|
const { x, y } = this.calcRelativeCoordinate(clientX, clientY);
|
this.context.lineTo(x, y);
|
this.context.stroke();
|
},
|
closePaint() {
|
this.painting = false;
|
},
|
// 重新计算画布的偏移值
|
updateCanvasOffset() {
|
this.getCanvasOffset();
|
},
|
// 改变笔刷大小
|
onChangeSize(size) {
|
this.brushSize = size;
|
},
|
// 改变笔刷颜色
|
onChangeColor(color) {
|
this.brushColor = color;
|
},
|
// 保存,清空等按钮
|
onChangeTool(tool) {
|
this.brushTool = tool;
|
switch (tool) {
|
case "clear":
|
this.toolClear();
|
break;
|
case "undo":
|
this.toolUndo();
|
break;
|
case "save":
|
this.toolSave();
|
break;
|
}
|
},
|
// 清空canvas所有内容(背景图除外)
|
toolClear() {
|
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
this.resetToolActive();
|
},
|
// 保存画布背景和划线到本地方法
|
toolSave() {
|
var imgData = this.canvas.toDataURL('image/jpeg');
|
console.log(imgData);
|
if (this.save) {
|
this.save(imgData);
|
}
|
},
|
// 返回上一步方法(撤销)
|
toolUndo() {
|
if (this.historyData.length <= 0) {
|
this.resetToolActive();
|
return;
|
}
|
// 将画的上一步数据写入canvas 重新渲染
|
const lastIndex = this.historyData.length - 1;
|
this.context.putImageData(this.historyData[lastIndex], 0, 0);
|
this.historyData.pop();
|
this.resetToolActive();
|
},
|
// 存储数据
|
saveData(data) {
|
this.historyData.length >= 50 && this.historyData.shift(); // 设置储存上限为50步
|
this.historyData.push(data);
|
},
|
// 清除、撤销、保存状态不需要保持,操作完后恢复笔刷状态
|
resetToolActive() {
|
setTimeout(() => {
|
this.brushTool = "brush";
|
}, 1000);
|
},
|
},
|
};
|
</script>
|
|
<style scoped>
|
.page {
|
display: flex;
|
flex-direction: column;
|
width: 100%;
|
height: 100%;
|
}
|
|
.main {
|
flex: 1;
|
}
|
|
.footer {
|
display: flex;
|
justify-content: space-around;
|
align-items: center;
|
height: 88px;
|
}
|
|
#canvas_panel {
|
width: 100%;
|
height: 100%;
|
margin-bottom: 12px;
|
/* 消除空格影响 */
|
font-size: 0;
|
background-color: #fff;
|
border-bottom: 1px solid #ccc;
|
}
|
|
#canvas {
|
cursor: crosshair;
|
}
|
</style>
|