vue+electron实现对图片的截选,拖拽等功能。

1,380 阅读9分钟

熟悉vue-electron项目

创建一个vue使用electron桌面应用:

  1. 首先使用vue-cli创建一个vue的项目,创建命令:vue create vue-electron:

    选择自我配置:

    image-20220402095617531.png

    选择项目中要使用到的技术点:

    image-20220402095736902.png 选择vue的版本(2.0):

    image-20220402095816138.png

    是否选择history模式作为路由模式(否):

    image-20220402095934351.png

    选择css预处理器:

    image-20220402095956204.png

    如babel、ESlint等插件是否要独立创建一个文件(是):

    image-20220402100131552.png

    是否要将此保存为未来项目的预设(否):

    image-20220402100258364.png

    等待安装完成:

    image-20220402100330472.png

  2. 进入vue-electron为vue项目安装electron插件:

    安装命令:vue add electron-builder :

    image-20220402102120661.png

    选择electron版本:

    image-20220402102042734.png

    image-20220402102223629.png

    使用 cd vue-electron 命令 进入 vue-electron使用npm run electron:serve / yarn electron:serve启动项目:

  3. 启动项目的时候会很慢,出现这个问题的主要原因是项目在下载 electron-devtools-installer这个插件,但是又由于这个插件是国外的,国内访问非常慢,有可能还会有下载失败的情况出现

    下载失败:

    image-20220402103510340.png

    解决办法:我们打开项目中的background.js文件,找到 import installExtension, { VUEJS_DEVTOOLS } from 'electron-devtools-installer' 这一行,将它注释掉,这样每次启动项目的时候,就不会在下载这个插件了,也就避免了这个问题:

    image-20220402103745530.png 运行效果:

    image-20220402103814039.png

项目一:拖拽、平移、缩放、截选等

项目需求:使用svg.js初始化一个svg画布,给画布初始化一个图片;在页面上渲染四个按钮,分别是:绘制,拖拽、删除、所、绘制十字线。默认情况下,可以在鼠标按下时拖拽图片移动,鼠标滑轮滚动时可以实现图片的缩放;当选择绘制时候,停止图片的平移、缩放功能,可以绘制一个矩形,用来选择一个某个局部细节,当绘制完成时,恢复图片的平移、缩放功能;当按下拖拽按钮时,可以将绘制的所有的矩形选中,进行拖拽放大缩小;当按下删除按钮时,可以将选中的矩形删除掉;当按下绘制十字线按钮时,在鼠标点击的时候绘制一个十字线。

  • 首先我们将vue项目中的不用的vue组件删掉,如HelloWorld.vue和AboutView.vue组件,在 views/HomeView.vue 组件中,进行项目开发:

    1. 安装svg.js插件,安装命令:npm install @svgdotjs/svg.js / yarn add @svgdotjs/svg.js :

      image-20220402105754983.png

    2. 初始化一个svg画布,给svg初始化一个图片,实现图片的缩放平移:

      在script中通过 import { SVG } from '@svgdotjs/svg.js' 引入 svg.js;

      在template模板中定义一个存放svg元素的父元素:

      <template>
        <div class="home">
          <!-- 存放svg的父元素 -->
          <div class="svgMain"></div>
        </div>
      </template>
      

      在script中的data中初始化一个svg,在methods中定义初始化svg的方法initSvg(), 并在mounted()调用:

      <script>
      import { SVG } from '@svgdotjs/svg.js'
      export default {
        name: 'HomeView',
        data() {
          return {
            svg: SVG()
          }
        },
        methods: {
          // 初始化SVG
          initSvg () {
            this.svg.addTo('.svgMain').size("100%", "100%");
          }
        },
        mounted() {
          this.initSvg()
        }
      }
      </script>
      

      此时我们可以在控制台看到我们插件的svg元素:

      image-20220402110320565.png

      初始化图片:在methods中定义initImg()方法用来初始化图片,并在initSvg方法中调用:

      <script>
      import { SVG } from '@svgdotjs/svg.js'
      export default {
        name: 'HomeView',
        data() {
          return {
            svg: SVG(), //定义一个svg方法
            imgMsg: { //用来存放图片的宽高
              w: 0,
              h: 0
            }
          }
        },
        methods: {
          // 初始化SVG
          initSvg () {
            this.svg.addTo('.svgMain').size("100%", "100%");
            this.initImg()
          },
          // 初始化图片
          initImg() {
            this.svg.image(require("@/assets/xibao.jpg"), (e) => {
              this.imgMsg.w = e.target.naturalWidth;  //获取图片宽度
              this.imgMsg.h = e.target.naturalHeight;  //获取图片高度
      
              // 初始化viewbox,它的起点时(0, 0),宽高和图片的快高一样
              this.svg.viewbox(0, 0, this.imgMsg.w, this.imgMsg.h)
            })
          }
        },
        mounted() {
          this.initSvg()
        }
      
      }
      </script>
      

      此时我们再启动项目,会看见我们初始化的图片,而且图片是因窗口而自适应大小的:

      image-20220402112023881.png

    3. 实现让鼠标控制svg元素平移、缩放:在methods方法中,定义svgConfig方法,并在initImg方法中调用:

      因为控制svg元素平移缩放,需要使用 panzoom.js ,通过 npm install @svgdotjs/svg.js @svgdotjs/svg.panzoom.js进行安装:

      image-20220402113050729.png

      安装完成之后,我们在script中引入svg.panzoom.js:

      import '@svgdotjs/svg.panzoom.js'
      

      此时,我们使用svg.panzoom方法,控制svg元素的缩放平移:

      initImg() {
          this.svg.image(require("@/assets/xibao.jpg"), (e) => {
              this.imgMsg.w = e.target.naturalWidth;  //获取图片宽度
              this.imgMsg.h = e.target.naturalHeight;  //获取图片高度
      
              // 初始化viewbox,它的起点时(0, 0),宽高和图片的快高一样
              this.svg.viewbox(0, 0, this.imgMsg.w, this.imgMsg.h)
              // 调用svgConfig方法,让svg原始可以缩放平移
              this.svgConfig(true) 
          })
      },
          
      // 让鼠标控制svg元素平移缩放
      svgConfig(flag) {
          this.svg.panZoom({
              zoomMin: 0.2, //最小缩放级别
              zoomMax: 20,  //最大缩放级别
              zoomFactor: 0.15,   // 鼠标滚轮放大缩小倍数
              wheelZoom: flag //启用滚轮缩放
          })
      }
      
    4. 我们绘制四个控制按钮,这里使用的element-ui的按钮,可以自己书写:

      <template>
        <div class="home">
          <!-- 定义四个按钮,用来控制svg操作 -->
          <div class="control-btn">
            <div @click="drawARectangle($event)">绘制矩形</div>
            <div @click="dragTheRectangle($event)">拖拽矩形</div>
            <div @click="removeTheRectangle($event)">删除矩形</div>
            <div @click="drawACross($event)">绘制十字</div>
          </div>
          <!-- 存放svg的父元素 -->
          <div class="svgMain"></div>
        </div>
      </template>
      

      image-20220402153841401.png

  • 绘制矩形:点击绘制矩形之后,取消svg因素的平移和缩放。用户可以在图片上绘制矩形。绘制矩形需要导入svg.draw.js。

    1. 导入svg.draw.js:import "@/js/svg.draw.js";

    2. 在点击后,取消svg因素的平移和缩放,在鼠标按下时候,开始绘制:

      // 绘制矩形
      drawARectangle() {
          // 取消svg元素的平移和缩放
          this.svgConfig(false);
          // 鼠标按下时,开始绘制,鼠标抬起时候,结束绘制
          this.svg.on("mousedown", (e) => {
              this.rect = this.svg
                  .rect() //绘制矩形
                  .attr({
                  // 设置矩形的属性
                  fill: "transparent", // 绘制背景色
                  stroke: "red", // 绘制边框颜色
                  "stroke-width": 3, // 绘制边框宽度
              });
              this.rect.draw(e); // 开始绘制
          });
      },
      
    3. 鼠标抬起的时候,停止绘制,恢复svg元素的缩放平移:

      // 绘制矩形
      drawARectangle() {
          // 取消svg元素的平移和缩放
          this.svgConfig(false);
          // 鼠标按下时,开始绘制,鼠标抬起时候,结束绘制
          this.svg.on("mousedown", (e) => {
              this.rect = this.svg
                  .rect() //绘制矩形
                  .attr({
                  // 设置矩形的属性
                  fill: "transparent", // 绘制背景色
                  stroke: "red", // 绘制边框颜色
                  "stroke-width": 3, // 绘制边框宽度
              });
              this.rect.draw(e); // 开始绘制
          });
          // 鼠标抬起时,矩形绘制完成,恢复svg元素的平移和缩放
          this.svg.on("mouseup", (e) => {
              // 使矩形完成绘制
              this.rect.draw(e);
              // 恢复svg元素的平移和缩放
              this.svgConfig(true);
          });
      },
      

      问题1:当我们单击按钮以后,按钮被选中,但是此时我们又不想绘制矩形,我们会再次单击按钮,会发现此时按钮的选中效果并没有失效,并且还可以继续进行绘制矩形。解决办法,每次单击之后,都进行一次判断,判断按钮是否被选中,如果选中,我们就移除选中事件;如果没有选中,我们就添加选中事件。这里我们是用jQuery的hasClass方法,判断此时按钮是否处于选中状态。

      安装jQuery命令:npm install jquery

      HomeView.vue组件的script中导入jQuery:

      // 绘制矩形
      drawARectangle(e) {
          // 获取单击按钮
          let el = $(e.target);
          if (!el.hasClass("current")) {
              // 如果没有选中,我们添加选中事件
              el.addClass("current");
              
              ····
              
              // 鼠标抬起时,矩形绘制完成,恢复svg元素的平移和缩放
              this.svg.on("mouseup", (e) => {
                  // 使矩形完成绘制
                  this.rect.draw(e);
                  // 移除选中样式
                  el.removeClass('current')
                  // 恢复svg元素的平移和缩放
                  this.svgConfig(true);
              });
          } else {
              // 如果被选中,我们移除选中事件
              el.removeClass("current");
              this.svgConfig(true);
          }
      },
      

      问题2:我们每次都有鼠标按下和抬起事件,都有绑定,却没有解绑,这就可能会导致,绘制完成后,我们在拖拽图片的时候,又进行一次绘制。解决版本,我们可以声明一个方法initOnOff,用来解绑这些事件:

      // 绘制矩形
      drawARectangle(e) {
          // 在每次操作前,进行初始化,解绑之前可能没有解绑的事件
          this.initOnOff()
          // 获取单击按钮
          let el = $(e.target);
          if (!el.hasClass("current")) {
              // 如果没有选中,我们添加选中事件
              el.addClass("current");
              // 取消svg元素的平移和缩放
              this.svgConfig(false);
              // 鼠标按下时,开始绘制,鼠标抬起时候,结束绘制
              this.svg.on("mousedown", (e) => {
                  this.rect = this.svg
                      .rect() //绘制矩形
                      .attr({
                      // 设置矩形的属性
                      fill: "transparent", // 绘制背景色
                      stroke: "red", // 绘制边框颜色
                      "stroke-width": 3, // 绘制边框宽度
                  });
                  this.rect.draw(e); // 开始绘制
              });
              // 鼠标抬起时,矩形绘制完成,恢复svg元素的平移和缩放
              this.svg.on("mouseup", (e) => {
                  // 使矩形完成绘制
                  this.rect.draw(e);
                  // 解绑mousedown和mouseup事件
                  this.initOnOff()
                  // 恢复svg元素的平移和缩放
                  this.svgConfig(true);
      
              });
          } else {
              // 调用initOnOff方法,移除选中事件
              this.initOnOff()
              // 恢复平移缩放
              this.svgConfig(true);
          }
      },
      
      // 解绑操作
      initOnOff() {
          // 解绑mousedown和mouseup方法
          this.svg.off('mousedown')
          this.svg.off('mouseup')
          $(".control-btn>div").removeClass("current"); //移除所有按钮的选中事件
      },
      

      问题3:当我们鼠标移除视口/屏幕外时,会失去对dom的监听,示例:

      image-20220402162125930.png 解决办法:

      将鼠标抬起事件修改为:

      // 当我们鼠标移除视口/屏幕外时,会失去对dom的监听
      document.addEventListener("mouseup", this.stopDraw, true);
      
      // 绘制完成
      stopDraw(e) {
          // 使矩形完成绘制
          this.rect.draw(e);
          // 解绑mousedown和mouseup事件
          this.initOnOff();
          // 恢复svg元素的平移和缩放
          this.svgConfig(true);
      },
      
    4. 优化绘制矩形过程1(超出图片裁剪回来):

      我们应该让用户在截选细节时,矩形框不应该超出图片。但是此时我们可以在图片外面绘制,这显然是不正确的,错误示范:

      image-20220402154535212.png

      解决办法:

      右下角:根据下图分析可知,我们需要获取 实际画框的W就是用图片的宽度-画框的宽;实际画框的H就是图片的高度-画框的y;

    image-20220402155751936.png

    // 将用户画的超出图片位置裁剪掉
    rectCalculation(e) {
        // 右下角操作
        // 我们需要获取 实际画框的W就是用图片的宽度-画框的宽;实际画框的H就是图片的高度-画框的y;
        let x = e.attr("x");  // 用户画的画框的起始点横坐标
        let y = e.attr("y");  // 用户画的画框的起始点纵坐标
        let rightX = x + e.attr("width"); // 用户画的画框的终点横坐标
        let rightY = y + e.attr("height");  // 用户画的画框的终点纵坐标
    
        // 设置画框的宽高
        if(rightX > this.imgMsg.w) {
            e.attr('width', this.imgMsg.w - x)
        }
        if (rightY > this.imgMsg.h) {
            e.attr('height', this.imgMsg.h - y)
        }
        // 左上角操作
    },
    

    左上角:根据下图分析可知,我们要获取画框的实际W就是用画框的宽-画框的横坐标的绝对值,获取画框实际的高就是用画框的高-画框的纵坐标的绝对值。

    image-20220402171806735.png

    // 将用户画的超出图片位置裁剪掉
    rectCalculation(e) {
        // 右下角操作
        ····
        
        // 左上角操作
        if (x < 0) {
            e.attr("width", e.attr("width") - Math.abs(x));
        }
        if (y < 0) {
            e.attr("height", e.attr("height") - Math.abs(y));
        }
    },
    

    坑:如果我们直接设置如向上设置画框的宽高的话,我们就会出现:画框的宽高裁剪为我们想要的宽高,但是它依然没有在指定位置上,是因为我们画框的起始坐标在上面,所以此时,我们将画框的起始坐标设置为(0,0)就可以解决这个问题。

    // 左上角操作
    if (x < 0) {
        e.attr({x: 0,"width": e.attr("width") - Math.abs(x)});
    }
    if (y < 0) {
        e.attr({y:0, "height": e.attr("height") - Math.abs(y)});
    }
    
    1. 优化绘制矩形过程1(不能让用户画矩形时,超出图片):

      // 鼠标按下时,开始绘制,鼠标抬起时候,结束绘制
      this.svg.on("mousedown", (e) => {
      	···
      
          // 判断用户在绘制矩形的过程中,如果超出图片,则进行限制
          this.rect.on("drawupdate", () => {
              this.rectCalculation(this.rect);
          });
      });
      
    2. 优化绘制矩形过程1(当用户在图片外开始绘制时,取消绘制):

      // 鼠标按下时,开始绘制,鼠标抬起时候,结束绘制
      this.svg.on("mousedown", (e) => {
          
          ···
      
          // 当用户在图片外开始绘制时,取消绘制
          if (
              this.rect.attr("x") < 0 ||
              this.rect.attr("y") < 0 ||
              this.rect.attr("x") > this.imgMsg.w ||
              this.rect.attr("y") > this.imgMsg.h
          ) {
              this.rect.remove();
          }
      });
      
    3. 绘制完成,可以重新启动项目,查看运行效果:

      image-20220406092237425.png

  • 拖拽矩形: 点击拖拽矩形按钮后,可以将绘制的矩形框进行拖拽、放大缩小。但拖拽范围不能超出图片。

    1. 当用户点击拖拽矩形按钮后,进行判断,当前是否处于选中状态,如果处于选中状态,则取消选中事件,恢复svg元素可以平移和缩放功能;如果处于非选中状态,则添加选中事件,取消svg元素的平移和缩放功能,给绘制的所有矩形添加选中事件。当用户选中某个矩形后,可以对矩形进行拖拽放大放小。

      为了获取绘制的矩形,我们可以在绘制矩形()的时候,添加一个class为lable的属性。

      // 鼠标按下时,开始绘制,鼠标抬起时候,结束绘制
      this.svg.on("mousedown", (e) => {
          this.rect = this.svg
              .rect() //绘制矩形
              .attr({
              // 设置矩形的属性
              fill: "transparent", // 绘制背景色
              stroke: "red", // 绘制边框颜色
              "stroke-width": 3, // 绘制边框宽度
          }).addClass('lable');
          this.rect.draw(e); // 开始绘制
      
          // 判断用户在绘制矩形的过程中,如果超出图片,则进行限制
          ···
      
          // 当用户在图片外开始绘制时,取消绘制
          ···
      });