vue2/canvas所有代码(已验证,可正常运行)
数据来源于GPT4:GPT4o
javascript
<template>
<div class="canvas-container">
<div class="modal-content">
<div class="modal_header">
<div>请点击鼠标选择重绘区域</div>
<div class="modal_header_close">
<span class="close" @click="closeModal">×</span>
</div>
</div>
<canvas
ref="canvas"
@mousedown="startDrawing"
@mousemove="draw"
@mouseup="finishDrawing"
@touchstart="startDrawing"
@touchmove="draw"
@touchend="finishDrawing"
></canvas>
<button @click="resetDrawing">重置</button>
<button @click="sendMuskAction(muskItem)">开始重绘</button>
<div>
startX: {{ Math.floor(startX) }}, startY: {{ Math.floor(startY) }}, endX: {{ Math.floor(endX) }}, endY: {{ Math.floor(endY) }}
</div>
</div>
</div>
</template>
<script>
export default {
name: "CanvasDemo",
data() {
return {
imageUrl:
"https://ts1.cn.mm.bing.net/th/id/R-C.6b9130073d73ef5ef0c45012a0774147?rik=7vPGVRRTb6MDcw&riu=http%3a%2f%2fchina-ybxgg.com%2fuploadfile%2fimage%2f%e6%b3%95%e5%85%b0%2f20200330110537873.jpg&ehk=lSqw%2bi0Cu2aykuT7KdHvQwVenva%2baGZ7oB2G6uBDxLs%3d&risl=&pid=ImgRaw&r=0",
muskItem: null, // 蒙版操作的mj的item
showModal: false, //是否显示蒙版
isDrawing: false, //是否正在绘图
startX: 0, //蒙版开始x坐标
startY: 0, //蒙版开始y坐标
endX: 0, //蒙版结束y坐标
endY: 0, //蒙版结束Fy坐标
rectangles: [], // 存储多个矩形的数组
context: null, //canvas context
image: new Image(), //需要蒙版操作的图片
// imageUrl: 'http://ipv4.sharemoon.club:29000/images/2024/01/02/1a9cf051c1996eebdd2af2a86e95f1f6.png',//需要蒙版操作的图片url
originalWidth: 0, //图片原始宽度
originalHeight: 0, //图片原始高度
scale: 1, //缩放比例
muskBase64: null, //绘制的蒙版
};
},
methods: {
//======================================蒙版begin======================================
loadImage(imageUrl) {
this.image.onload = () => this.drawImage();
this.image.src = imageUrl;
},
drawImage() {
const canvas = this.$refs.canvas;
this.originalWidth = this.image.width;
this.originalHeight = this.image.height;
// 根据模态框大小调整图片显示大小 offsetWidth 父级宽度
this.scale = Math.min(
canvas.parentElement.offsetWidth / this.originalWidth,
canvas.parentElement.offsetHeight / this.originalHeight
) * 3;
canvas.width = this.originalWidth * this.scale;
canvas.height = this.originalHeight * this.scale;
this.context = canvas.getContext('2d');
this.context.drawImage(this.image, 0, 0, canvas.width, canvas.height);
},
startDrawing(event) {
console.log("start draw")
const rect = this.$refs.canvas.getBoundingClientRect();
const scaleX = this.$refs.canvas.width / rect.width;
const scaleY = this.$refs.canvas.height / rect.height;
// 检查是否为触摸事件
if(event.touches) {
// 对于触摸事件,使用touches数组中的第一个触摸点
this.startX = (event.touches[0].clientX - rect.left) * scaleX;
this.startY = (event.touches[0].clientY - rect.top) * scaleY;
} else {
// 对于鼠标事件,使用clientX和clientY
this.startX = (event.clientX - rect.left) * scaleX;
this.startY = (event.clientY - rect.top) * scaleY;
}
this.isDrawing = true;
},
draw(event) {
if (!this.isDrawing) return;
const rect = this.$refs.canvas.getBoundingClientRect();
const scaleX = this.$refs.canvas.width / rect.width;
const scaleY = this.$refs.canvas.height / rect.height;
let x = null
let y = null
if(event.touches) {
// 对于触摸事件,使用touches数组中的第一个触摸点
x = (event.touches[0].clientX - rect.left) * scaleX;
y = (event.touches[0].clientY - rect.top) * scaleY;
} else {
// 对于鼠标事件,使用clientX和clientY
x = (event.clientX - rect.left) * scaleX;
y = (event.clientY - rect.top) * scaleY;
}
this.context.clearRect(0, 0, this.$refs.canvas.width, this.$refs.canvas.height);
this.context.drawImage(this.image, 0, 0, this.$refs.canvas.width, this.$refs.canvas.height);
// 重新绘制已存在的矩形
this.rectangles.forEach(rect => {
this.context.beginPath();
this.context.rect(rect.startX, rect.startY, rect.endX - rect.startX, rect.endY - rect.startY);
this.context.fillStyle = 'rgba(255, 255, 255, 0.5)';
this.context.fill();
this.context.strokeStyle = 'red';
this.context.stroke();
});
this.context.beginPath();
this.context.rect(this.startX, this.startY, x - this.startX, y - this.startY);
this.context.fillStyle = 'rgba(255, 255, 255, 0.5)';
this.context.fill();
this.context.strokeStyle = 'red';
this.context.stroke();
},
finishDrawing(event) {
this.isDrawing = false;
const rect = this.$refs.canvas.getBoundingClientRect();
const scaleX = this.$refs.canvas.width / rect.width;
const scaleY = this.$refs.canvas.height / rect.height;
if(event.changedTouches && event.changedTouches.length > 0) {
// 对于触摸事件,使用touches数组中的第一个触摸点
this.endX = (event.changedTouches[0].clientX - rect.left) * scaleX;
this.endY = (event.changedTouches[0].clientY - rect.top) * scaleY;
} else {
// 对于鼠标事件,使用clientX和clientY
this.endX = (event.clientX - rect.left) * scaleX;
this.endY = (event.clientY - rect.top) * scaleY;
}
// 创建蒙版
// 将当前矩形添加到数组中
this.rectangles.push({
startX: this.startX,
startY: this.startY,
endX: this.endX,
endY: this.endY
});
console.log(this.rectangles);
this.createMask();
},
resetDrawing() {
this.context.clearRect(0, 0, this.$refs.canvas.width, this.$refs.canvas.height);
this.context.drawImage(this.image, 0, 0, this.$refs.canvas.width, this.$refs.canvas.height);
this.rectangles = []; // 清空矩形数组
},
createMask() {
const canvas = document.createElement('canvas');
canvas.width = this.originalWidth; // 设置画布宽度
canvas.height = this.originalHeight; // 设置画布高度
const ctx = canvas.getContext('2d'); // 获取画布上下文
// 填充黑色背景
ctx.fillStyle = 'black';
ctx.fillRect(0, 0, canvas.width, canvas.height);// 填充黑色背景
// 填充白色矩形
ctx.fillStyle = 'white';
this.rectangles.forEach(rect => { // 遍历矩形数组
ctx.fillRect(rect.startX / this.scale, rect.startY / this.scale,// 除以缩放比例
(rect.endX - rect.startX) / this.scale,// 除以缩放比例
(rect.endY - rect.startY) / this.scale
);
});
// 输出Base64
const base64Image = canvas.toDataURL();// 输出Base64
console.log(base64Image,"base64Image");
this.muskBase64 = base64Image
},
closeModal() {
this.showModal = false;
this.resetDrawing();
},
//======================================蒙版end======================================
},
mounted() {
this.loadImage(this.imageUrl);
// Code to run when the component is mounted goes here
},
};
</script>
<style scoped>
.canvas-container {
width: 600px;
height: 600px;
border: 1px solid #1764e8;
display: flex;
justify-content: center;
align-items: center;
}
.modal-content {
max-width: 90%;
background-color: #fefefe;
padding: 20px;
border: 1px solid #888;
display: flex;
flex-direction: column;
justify-content: flex-start; /* 从顶部开始布局元素 */
align-items: center;
overflow: auto; /* 如果内容过多,允许滚动 */
position: relative;
}
.shangchuan /deep/.el-upload--picture-card {
margin-left: -155px !important;
}
canvas {
width: 80%; /* 使用100%宽度确保与父元素宽度一致 */
height: auto; /* 高度自动调整 */
}
</style>