使用canvas绘制图片蒙版/获取绘制区域坐标/转成base64

vue2/canvas所有代码(已验证,可正常运行)

在这里插入图片描述

数据来源于GPT4:GPT4o

<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">&times;</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>
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吉吉安

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值