<template>
|
<div class="page">
|
<div class="main">
|
<div id="canvas_panel">
|
<canvas
|
id="canvas"
|
:style="{
|
backgroundImage: `url(${backgroundImage})`,
|
backgroundSize: 'cover',
|
backgroundPosition: 'center',
|
}"
|
>当前浏览器不支持canvas。</canvas
|
>
|
</div>
|
</div>
|
<div class="footer">
|
<BrushSize :size="brushSize" @change-size="onChangeSize" />
|
<ColorPicker :color="brushColor" @change-color="onChangeColor" />
|
<ToolBtns :tool="brushTool" @change-tool="onChangeTool" />
|
</div>
|
</div>
|
</template>
|
|
<script>
|
import BrushSize from "./components/brushSize.vue";
|
import ColorPicker from "./components/colorPicker.vue";
|
import ToolBtns from "./components/toolBtns.vue";
|
export default {
|
name: "graffiti",
|
components: { BrushSize, ColorPicker, ToolBtns },
|
data() {
|
return {
|
canvas: null,
|
context: null,
|
painting: false,
|
historyData: [], // 存储历史数据,用于撤销
|
brushSize: 5, // 笔刷大小
|
brushColor: "#000000", // 笔刷颜色
|
brushTool: "brush",
|
canvasOffset: {
|
left: 0,
|
top: 0,
|
},
|
backgroundImage:
|
"https://t7.baidu.com/it/u=1819248061,230866778&fm=193&f=GIF",
|
};
|
},
|
mounted() {
|
this.canvas = document.getElementById("canvas");
|
if (this.canvas.getContext) {
|
this.context = this.canvas.getContext("2d", { willReadFrequently: true });
|
this.initCanvas();
|
// window.addEventListener('resize', updateCanvasPosition);
|
window.addEventListener("scroll", this.updateCanvasOffset); // 添加滚动条滚动事件监听器
|
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);
|
}
|
this.toolClear();
|
},
|
methods: {
|
changeBackground(imgUrl) {
|
this.backgroundImage = imgUrl;
|
},
|
initCanvas() {
|
const that = this
|
function resetCanvas() {
|
const elPanel = document.getElementById("canvas_panel");
|
that.canvas.width = elPanel.clientWidth;
|
that.canvas.height = elPanel.clientHeight;
|
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;
|
}
|
},
|
toolClear() {
|
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
this.resetToolActive();
|
},
|
toolSave() {
|
const imageDataUrl = this.canvas.toDataURL("image/png");
|
console.log(imageDataUrl);
|
// const imgUrl = canvas.toDataURL('image/png');
|
// const el = document.createElement('a');
|
// el.setAttribute('href', imgUrl);
|
// el.setAttribute('target', '_blank');
|
// el.setAttribute('download', `graffiti-${Date.now()}`);
|
// document.body.appendChild(el);
|
// el.click();
|
// document.body.removeChild(el);
|
// resetToolActive();
|
},
|
toolUndo() {
|
if (this.historyData.length <= 0) {
|
this.resetToolActive();
|
return;
|
}
|
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: 1038px;
|
height: 866px;
|
}
|
|
.main {
|
flex: 1;
|
}
|
|
.footer {
|
display: flex;
|
justify-content: space-around;
|
align-items: center;
|
height: 88px;
|
background-color: #fff;
|
}
|
|
#canvas_panel {
|
margin: 12px;
|
height: calc(100% - 24px);
|
/* 消除空格影响 */
|
font-size: 0;
|
background-color: #fff;
|
box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.12);
|
}
|
|
#canvas {
|
cursor: crosshair;
|
/* background: url('https://t7.baidu.com/it/u=1819248061,230866778&fm=193&f=GIF') no-repeat !important; */
|
}
|
</style>
|