• 用canvas实现图片背景替换、抠图


    在这里插入图片描述

    演示地址
    代码仓库

    关键技术

    canvas apielement-ui (el-color-picker、el-upload)

    原理解析

    1. 用户从本地上传一张图片

    我们拿到图片数据并在页面渲染出来,这一步用到了elementui的el-upload

    el-upload.avatar-uploader(
      ref="logoUpload",
      accept="image/*",
      action="#",
      :auto-upload="false",
      :on-change="handleStatusChange"
    )
      el-button(size="small", type="primary") 点击上传
    

    这里实际上不需要真正上传到服务器,只需要获取到图片的在内存中的url

    handleStatusChange(file) {
      // console.log(file);
      this.originImg = URL.createObjectURL(file.raw);
    },
    

    拿到url之后可以先把图片用img标签渲染在页面上,这样做的目的是为了获取图片的实际尺寸,方面我们等比例缩放在我们的canvas上。

    // 图片加载完成
        loadImg(e) {
          const img = e.target;
          const width = img.offsetWidth;
          const height = img.offsetHeight;
          this.imgWidth = width;
          this.imgHeight = height;
          this.canHeight = (height / width) * this.canWidth;
        }
    

    canvas的width可以根据外层容器来获取

    this.$nextTick(() => {
          const contentWidth = document.querySelector(".origin-box").offsetWidth;
          this.canWidth = Math.min(contentWidth - 12, this.canWidth);
    });
    

    2. 绘制canvas

    绘制图片到canvas,这一步比较简单,

    //这里需要缩放一下,因为我们的画布已经被缩放了
    this.originCtx.scale(this.canWidth / width, this.canWidth / width);
    this.originCtx.drawImage(img, 0, 0);
    

    3. 选中颜色

    点击canvas,我们可以拿到该点上的颜色值,获取方式

    ctx.getImageData(targetX,targetY,1,1)
    

    拿到的是imagedate对象,{data, width, height},data即为我们要的颜色值。

    4. 遍历原图片的颜色值,匹配到选中颜色之后,做对应颜色的替换即可

    核心api: getImageData putImageData

    //获取data
    const data = this.imageData.data || [];
    //遍历并替换
    for (let i = 0; i < data.length; i += 4) {
            const similar = this.isSimilar(_fromColor, data.slice(i, i + 4));
            if (similar) {
              data[i] = _toColor[0];
              data[i + 1] = _toColor[1];
              data[i + 2] = _toColor[2];
              data[i + 3] = _toColor[3] * 255;
            }
     }
     //绘制到目标容器上
     this.transCtx.putImageData(this.imageData, 0, 0);
    

    isSimilar方法用来判断两个颜色是否相似或相等,这个可以通过参数调整(类似于ps的容差概念),容差值越小,匹配越精准。

    颜色值是rgba格式的,即4个数组为一组,数值都在[0,255]之间,由于el-picker返回的rgba,透明度用的是【0,1】表示的,所以要转换到0-255区间

    5. undo&redo

    撤销、前进、后退功能还是很有必要的,重复替换操作,可返回历史操作步骤。
    创建一个队列(这里用数组代替),每次有新的数据变化添加到队列里,用unshift表示入列pop从队列后面删除。可以设置上限10,队列过大会占用较大内存,不建议设置过大。
    维护一个index,理解成指针,表示当前回退的数据在队列中的位置。

    undo() {
      this.index++;
      this.redrawImg();
    },
    redo() {
      this.index--;
      this.redrawImg();
    },
    redrawImg() {
      const preImageData = JSON.parse(this.imgStock[this.index]).data;
      this.imageData = this.transCtx.createImageData(
        this.canWidth,
        this.canHeight
      );
      for (let i = 0; i < this.imageData.data.length; i++) {
        this.imageData.data[i] = preImageData[i];
      }
      this.transCtx.putImageData(this.imageData, 0, 0);
    },
    

    需要注意的细节:当回退到某一条历史记录,比如index=5,这时候再执行手动操作替换,此时就“穿越”了,需要把index = 5之前的历史都移除。(可能描述有点绕~)

    至此,就完成了核心功能了~

    TODO LIST

    1. 增加边缘识别,去除毛边
    2. 增加容差选项
    3. 尝试视频抠图和替换
    4. 。。。

    演示地址
    代码仓库

    欢迎提意见&star!

    四体不勤 五谷不分 文不能测字 武不能防身
  • 相关阅读:
    高仿富途牛牛-组件化(三)-界面美化
    高仿富途牛牛-组件化(二)-磁力吸附
    高仿富途牛牛-组件化(一)-支持页签拖拽、增删、小工具
    Cef3 学习资料
    QCustomplot使用分享(八) 绘制图表-加载cvs文件
    Electron桌面应用:环境搭建
    Qt疑难问题-模态窗口父类被析构
    Qt线程实现分析-moveToThread vs 继承
    基础连接已经关闭: 未能为 SSL/TLS 安全通道建立信任关系。
    asp.net中的ListBox控件添加双击事件
  • 原文地址:https://www.cnblogs.com/amingxiansen/p/15189313.html
Copyright © 2020-2023  润新知