<!-- 涂色连线题控件 -->
|
<template>
|
<div class="page" :style="{ height: imgHeight + 120 + 'px' }">
|
<div class="main">
|
<div id="canvas_panel">
|
<canvas
|
id="canvas"
|
:style="{
|
backgroundSize: 'cover',
|
backgroundPosition: 'center',
|
}"
|
>当前浏览器不支持canvas。</canvas
|
>
|
</div>
|
</div>
|
<div class="footer" :style="{ backgroundColor: bcColor }">
|
<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: {
|
page: {
|
type: Number,
|
},
|
bcImg: {
|
type: String,
|
},
|
imgWidth: {
|
type: Number,
|
},
|
imgHeight: {
|
type: Number,
|
},
|
bcColor: {
|
type: String,
|
default: "#fff",
|
},
|
},
|
data() {
|
return {
|
canvas: null,
|
context: null,
|
painting: false, // 记录状态,鼠标是否在按下状态
|
historyData: [], // 存储历史数据,用于撤销
|
brushSize: 5, // 笔刷大小
|
brushColor: "#000000", // 笔刷颜色
|
brushTool: "brush",
|
canvasOffset: {
|
left: 0,
|
top: 0,
|
},
|
backgroundImage: "",
|
};
|
},
|
mounted() {
|
this.backgroundImage = this.bcImg;
|
this.canvas = (this.container ? this.container : document).getElementById(
|
"canvas"
|
);
|
if (this.canvas.getContext) {
|
this.context = this.canvas.getContext("2d", { willReadFrequently: true });
|
this.initCanvas();
|
// 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);
|
}
|
this.toolClear();
|
const oldData = localStorage.getItem(
|
this.config.activeBook.name + "-graffiti-" + this.page
|
);
|
if (oldData) {
|
this.backgroundImage = oldData;
|
}
|
},
|
// watch:{
|
// backgroundImage:{
|
// handler(newValue) {
|
// if(newValue && this.context) {
|
// const imgData = new Image();
|
// // 分两种情况,初次的图片 直接用外部链接,需要加跨域和时间戳,二次保存的作为背景则不需要
|
// const oldData = localStorage.getItem(this.config.activeBook.name + '-graffiti-' + this.page)
|
// if(oldData) {
|
// imgData.src = newValue
|
// } else {
|
// imgData.src = newValue + '?' + new Date().getTime();
|
// imgData.setAttribute('crossOrigin', '');
|
// }
|
// imgData.onload = () => {
|
// this.context.drawImage(imgData,0,0)
|
// }
|
// }
|
// }
|
// }
|
// },
|
methods: {
|
changeBackground(imgUrl) {
|
this.backgroundImage = imgUrl;
|
},
|
// 初始化 画布,设置大小背景色
|
initCanvas() {
|
const that = this;
|
const resetCanvas = () => {
|
const elPanel = (
|
this.container ? this.container : document
|
).getElementById("canvas_panel");
|
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(); // 更新画布位置
|
// 设置画布背景图
|
const imgData = new Image();
|
// 分两种情况,初次的图片 直接用外部链接,需要加跨域和时间戳,二次保存的作为背景则不需要
|
const oldData = localStorage.getItem(
|
this.config.activeBook.name + "-graffiti-" + this.page
|
);
|
if (oldData) {
|
imgData.src = oldData;
|
} else {
|
imgData.src = this.backgroundImage + "?" + new Date().getTime();
|
imgData.setAttribute("crossOrigin", "");
|
}
|
imgData.onload = () => {
|
this.context.drawImage(imgData, 0, 0, this.imgWidth, this.imgHeight);
|
};
|
};
|
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();
|
// 新加内容,清空后将背景图再设置上去
|
const imgData = new Image();
|
// 分两种情况,初次的图片 直接用外部链接,需要加跨域和时间戳,二次保存的作为背景则不需要
|
const oldData = localStorage.getItem(
|
this.config.activeBook.name + "-graffiti-" + this.page
|
);
|
if (oldData) {
|
imgData.src = oldData;
|
} else {
|
imgData.src = this.backgroundImage + "?" + new Date().getTime();
|
imgData.setAttribute("crossOrigin", "");
|
}
|
imgData.onload = () => {
|
this.context.drawImage(imgData, 0, 0, this.imgWidth, this.imgHeight);
|
};
|
},
|
// 保存画布背景和划线到本地方法
|
toolSave() {
|
var imgData = this.canvas.toDataURL({
|
format: "png",
|
quality: 1,
|
width: 800,
|
});
|
localStorage.setItem(
|
this.config.activeBook.name + "-graffiti-" + this.page,
|
imgData
|
);
|
},
|
// 保存为一张图片并下载的方法
|
saveImgData() {
|
var link = (this.container ? this.container : document).createElement(
|
"a"
|
);
|
var imgData = this.canvas.toDataURL({
|
format: "png",
|
quality: 1,
|
width: 20000,
|
height: 4000,
|
});
|
var strDataURI = imgData.substr(22, imgData.length);
|
var blob = dataURLtoBlob(imgData);
|
var objurl = URL.createObjectURL(blob);
|
link.download = "grid1.png";
|
link.href = objurl;
|
link.click();
|
|
function dataURLtoBlob(dataurl) {
|
var arr = dataurl.split(","),
|
mime = arr[0].match(/:(.*?);/)[1],
|
bstr = atob(arr[1]),
|
n = bstr.length,
|
u8arr = new Uint8Array(n);
|
while (n--) {
|
u8arr[n] = bstr.charCodeAt(n);
|
}
|
return new Blob([u8arr], { type: mime });
|
}
|
},
|
// 返回上一步方法(撤销)
|
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%;
|
}
|
|
.main {
|
flex: 1;
|
}
|
|
.footer {
|
display: flex;
|
justify-content: space-around;
|
align-items: center;
|
height: 88px;
|
}
|
|
#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>
|