fabric.js 组件 图片上传裁剪并进行自定义区域标记

news2024/11/17 15:45:30

目录

0. 前言

1. 安装fabric与引入

2. fabric组件的使用

3. 属性相关设置

4. 初始化加载

4. 方法

5. 全代码


0. 前言

利用fabric组件,实现图片上传、图片”裁剪“、自定义的区域标记一系列操作

先放一张效果图吧👇

1. 安装fabric与引入

npm i fabric -S

我用的是全局引入方式,视情况调整 

import fabric from 'fabric';
Vue.use(fabric);

先放一个fabric.js API地址☞Api | Fabric中文文档 (gitee.io) 

2. fabric组件的使用

定义容器id=canvas,注意宽高

  <div class="maintenancePlanAdd">
    <div class="panel-body">
      <div class="demo">
        <canvas id="canvas" :width="width" :height="height" />
        <div class="draw-btn-group" v-show="!readstate">
          <div>
            <el-button class="el-icon-upload" size="mini" type="primary"
                       style="width: 80px !important;" @click="uploadImgConfirm"
            >
              图片上传
            </el-button>
            <el-button size="mini" type="danger" icon="el-icon-delete" @click="clean">
              清除
            </el-button>
          </div>
          <div>
            <el-button
              v-show="bgImgSrc !== ''"
              v-for="(item, index) in alarmLevel"
              :key="index"
              :style="{background:colorGrounp[index]}"
              size="mini"
              @click="drawPolygon(index)"
            >{{ item }}
            </el-button>
          </div>
        </div>
      </div>
    </div>
    <img id="expImg" :src="bgImgSrc">
    <img id="img" :src="bgImgSrc">
    <input v-show="false" type="file" @change="uploadImgChange" id="imgInput" accept="image/*">
    <p class="tip-title" v-show="bgImgSrc === ''">上传的图片可以进行拖拽调整大小和方向</p>
  </div>

3. 属性相关设置

累了,不想写了

 data() {
    return {
      bgImgFlag: true,
      bgImgSrc: '',
      imgFile: {},
      width: 800,
      height: 400,
      alarmLevel: ['一级风险', '二级风险', '三级风险', '四级风险'],
      colorGrounp: ['rgba(51, 164, 255, 1)', 'rgba(255, 200, 89, 1)', 'rgba(255, 160, 89, 1)', 'rgba(196,43, 1, 1)'],
      colorGrounpFill: ['rgba(51, 164, 255, 0.3)', 'rgba(255, 200, 89, 0.3)', 'rgba(255, 160, 89, 0.3)', 'rgba(196,43, 1, 0.3)'],
      canvas: {},
      mouseFrom: {},
      mouseTo: {},
      drawType: '', // 当前绘制图像ROI
      drawWidth: 2, // 笔触宽度
      drawingObject: null, // 当前绘制对象
      moveCount: 1, // 绘制移动计数器
      doDrawing: false, // 绘制状态
      // polygon 相关参数
      polygonMode: false,
      pointArray: [],
      lineArray: [],
      savePointsGroup: [],
      activeShape: false,
      activeLine: '',
      line: {},
      deleteIconURL: require('@/assets/screen/icon-close.png') // 区域标记取消的x号图标
    };
  },

4. 初始化加载

this.canvas = new fabric.Canvas('canvas', {
      skipTargetFind: false, // 当为真时,跳过目标检测。目标检测将返回始终未定义。点击选择将无效
      selectable: false, // 为false时,不能选择对象进行修改
      selection: false // 是否可以多个对象为一组
    });
    this.canvas.selectionColor = 'rgba(0,0,0,0.05)';
    this.canvas.on('mouse:down', this.mousedown);
    this.canvas.on('mouse:move', this.mousemove);
    document.onkeydown = e => {
      // 键盘 delect删除所选元素
      if (e.keyCode == 46) {
        this.deleteObj();
      }
      // ctrl+z 删除最近添加的元素
      if (e.keyCode == 90 && e.ctrlKey) {
        this.canvas.remove(
          this.canvas.getObjects()[this.canvas.getObjects().length - 1]
        );
      }
    };
    this.$nextTick(() => {
      this.loadDraw(); // 回显之前标注过的内容,底图和区域标记内容
    });

4. 方法

methods: {
    // 保存当前画布为png图片
    save() {
      var canvas = document.getElementById('canvas');
      var imgData = canvas.toDataURL('png');
      imgData = imgData.replace('image/png', 'image/octet-stream');
      // 下载后的问题名,可自由指定
      var filename = 'drawingboard_' + (new Date()).getTime() + '.' + 'png';
      this.saveFile(imgData, filename);
    },
    saveFile(data, filename) {
      var save_link = document.createElement('a');
      save_link.href = data;
      save_link.download = filename;
      var event = document.createEvent('MouseEvents');
      event.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
      save_link.dispatchEvent(event);
    },
    // 提交绘制内容
    submitDraw() {
      const params = {
        pointInfo: [],
        imgInfo: '',
        img: ''
      };
      this.canvas.toJSON(['alarmLevel', 'isBgImg']).objects.forEach(item => {
        const element = {
          alarmLevel: item.alarmLevel,
          pointInfo: ''
        };
        if (item?.points) {
          element.pointInfo = item.points;
          params.pointInfo.push(element);
        }
        if (item?.isBgImg) {
          params.imgInfo = item;
          params.img = item.src;
          delete params.imgInfo.src;
        }
      });
      this.$emit('saveDraw', params);
    },
    // 清除画布
    clean() {
      this.$confirm('是否清除图片和标记?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        this.canvas.clear();
        this.bgImgSrc = '';
        this.bgImgFlag = true;
      });
    },
    // 从已渲染的DOM元素加载图片至canvas
    loadExpImg() {
      const imgElement = document.getElementById('expImg');
      imgElement.onload = () => {
        // eslint-disable-next-line new-cap
        new fabric.Image.fromURL(
          imgElement.src,
          img => {
            img.scale(0.3);
            img.set({
              originX: 'center',
              originY: 'center'
            }, { crossOrigin: 'anonymous' });
            img.on('scaling', e => { // 拉伸事件
              const h = img.scaleY;
              const w = img.scaleX;
              if (h !== w || w == h) { // 判断缩放值相等或不相等后执行图片等比缩放
                if (e.e.movementY == -1 || e.e.movementY == 1) {
                  img.scale(h);// 缩放
                } else {
                  img.scale(w);
                }
              }
            });
            img.setCoords();
            img.centeredScaling = true;
            img.centerTransform = true;
            this.canvas.add(img);
            this.canvas.centerObject(img);
            this.canvas.renderAll();
          }, {
            selectable: true,
            hasControls: true,
            centeredScaling: false,
            zIndex: -99,
            isBgImg: true
          });
      };
    },
    // 上传确认
    uploadImgConfirm() {
      if (this.bgImgSrc !== '') {
        this.$confirm('是否重新上传标记?', '提示', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning'
        }).then(() => {
          this.canvas.clear();
          this.bgImgFlag = true;
          document.getElementById('imgInput').click();
        });
      } else {
        document.getElementById('imgInput').click();
      }
    },
    // 从文件加载图片至canvas
    uploadImgChange() {
      // 获取文件
      var eleImportInput = document.getElementById('imgInput');
      this.imgFile = eleImportInput.files[0];
      var imgTitle = '';
      // 从reader中获取选择文件的src
      if (/\.(jpe?g|png|gif)$/i.test(this.imgFile.name)) {
        var reader = new FileReader();
        var _this = this;
        reader.addEventListener(
          'load',
          function() {
            imgTitle = _this.imgFile.name;
            _this.bgImgSrc = this.result;
          },
          false
        );
        reader.readAsDataURL(this.imgFile);
      }
      this.loadExpImg();
    },
    // 鼠标按下时触发
    mousedown(e) {
      if (undefined === e) return;
      // 记录鼠标按下时的坐标
      var xy = e.pointer || this.transformMouse(e.e.offsetX, e.e.offsetY);
      this.mouseFrom.x = xy.x;
      this.mouseFrom.y = xy.y;
      this.doDrawing = true;

      this.canvas.skipTargetFind = false;
      try {
        // 此段为判断是否闭合多边形,点击红点时闭合多边形
        if (this.pointArray.length > 1) {
          // e.target.id == this.pointArray[0].id 表示点击了初始红点
          if (e.target && e.target.id == this.pointArray[0].id) {
            this.generatePolygon();
            return;
          }
        }
        // 未点击红点则继续作画
        if (this.polygonMode && this.pointArray.length < 4) {
          this.addPoint(e);
        } else if (this.polygonMode && this.pointArray.length > 0) {
          this.$message.warning('最多设置四个点');
        }
      } catch (error) {
        console.log(error);
      }
    },
    // 鼠标松开执行
    mouseup(e) {
      if (undefined === e) return;
      var xy = e.pointer || this.transformMouse(e.e.offsetX, e.e.offsetY);
      this.mouseTo.x = xy.x;
      this.mouseTo.y = xy.y;
      this.drawingObject = null;
      this.moveCount = 1;
    },
    // 鼠标移动过程中已经完成了绘制
    mousemove(e) {
      if (undefined === e) return;
      if (this.moveCount % 2 && !this.doDrawing) {
        // 减少绘制频率
        return;
      }
      this.moveCount++;
      var xy = e.pointer || this.transformMouse(e.e.offsetX, e.e.offsetY);
      this.mouseTo.x = xy.x;
      this.mouseTo.y = xy.y;
      if (this.activeLine && this.activeLine.class == 'line') {
        var pointer = this.canvas.getPointer(e.e);
        this.activeLine.set({
          x2: pointer.x,
          y2: pointer.y
        });
        var points = this.activeShape.get('points');
        points[this.pointArray.length] = {
          x: pointer.x,
          y: pointer.y,
          zIndex: 1
        };
        this.activeShape.set({
          points: points
        });
        this.canvas.renderAll();
      }
      this.canvas.renderAll();
    },
    deleteObj() {
      this.canvas.getActiveObjects().map(item => {
        this.canvas.remove(item);
      });
    },
    transformMouse(mouseX, mouseY) {
      return {
        x: mouseX / 1,
        y: mouseY / 1
      };
    },
    // 绘制多边形开始
    drawPolygon(data) {
      if (this.bgImgFlag) {
        this.canvas.getObjects().forEach(obj => {
          if (obj.isBgImg) {
            obj.hasControls = false;
            obj.selectable = false;
            obj.evented = false;
          }
        });
        this.bgImgFlag = false;
        this.canvas.renderAll();
      }
      this.drawType = data;
      this.polygonMode = true;
      this.pointArray = []; // 顶点集合
      this.lineArray = []; // 线集合
      this.canvas.isDrawingMode = false;
    },
    addPoint(e) {
      var random = Math.floor(Math.random() * 10000);
      var id = new Date().getTime() + random;
      var circle = new fabric.Circle({
        radius: 5,
        fill: '#ffffff',
        stroke: '#333333',
        strokeWidth: 0.5,
        left: (e.pointer.x || e.e.layerX) / this.canvas.getZoom(),
        top: (e.pointer.y || e.e.layerY) / this.canvas.getZoom(),
        selectable: false,
        hasBorders: false,
        hasControls: false,
        originX: 'center',
        originY: 'center',
        id: id,
        objectCaching: false
      });
      if (this.pointArray.length == 0) {
        circle.set({
          fill: this.colorGrounp[this.drawType]
        });
      }
      var points = [
        (e.pointer.x || e.e.layerX) / this.canvas.getZoom(),
        (e.pointer.y || e.e.layerY) / this.canvas.getZoom(),
        (e.pointer.x || e.e.layerX) / this.canvas.getZoom(),
        (e.pointer.y || e.e.layerY) / this.canvas.getZoom()
      ];
      this.line = new fabric.Line(points, {
        strokeWidth: 2,
        fill: this.colorGrounp[this.drawType],
        stroke: this.colorGrounp[this.drawType],
        class: 'line',
        originX: 'center',
        originY: 'center',
        selectable: false,
        hasBorders: false,
        hasControls: false,
        evented: false,
        objectCaching: false
      });
      if (this.activeShape) {
        var pos = this.canvas.getPointer(e.e);
        var points = this.activeShape.get('points');
        points.push({
          x: pos.x,
          y: pos.y
        });
        var polygon = new fabric.Polygon(points, {
          stroke: '#333333',
          strokeWidth: 1,
          fill: this.colorGrounpFill[this.drawType],
          opacity: 0.3,
          selectable: false,
          hasBorders: false,
          hasControls: false,
          evented: false,
          objectCaching: false
        });
        this.canvas.remove(this.activeShape);
        this.canvas.add(polygon);
        this.activeShape = polygon;
        this.canvas.renderAll();
      } else {
        var polyPoint = [
          {
            x: (e.pointer.x || e.e.layerX) / this.canvas.getZoom(),
            y: (e.pointer.y || e.e.layerY) / this.canvas.getZoom()
          }
        ];
        var polygon = new fabric.Polygon(polyPoint, {
          stroke: '#333333',
          strokeWidth: 1,
          fill: '#cccccc',
          opacity: 0.3,
          selectable: false,
          hasBorders: false,
          hasControls: false,
          evented: false,
          objectCaching: false
        });
        this.activeShape = polygon;
        this.canvas.add(polygon);
      }
      this.activeLine = this.line;
      this.pointArray.push(circle);
      this.lineArray.push(this.line);
      this.canvas.add(this.line);
      this.canvas.add(circle);
    },
    generatePolygon() {
      var points = [];
      this.pointArray.map((point, index) => {
        points.push({
          x: point.left,
          y: point.top
        });
        this.canvas.remove(point);
      });
      this.lineArray.map((line, index) => {
        this.canvas.remove(line);
      });
      this.canvas.remove(this.activeShape).remove(this.activeLine);
      var polygon = new fabric.Polygon(points, {
        stroke: this.colorGrounp[this.drawType],
        strokeWidth: this.drawWidth,
        fill: this.colorGrounpFill[this.drawType],
        opacity: 1,
        selectable: false,
        hasBorders: false,
        hasControls: false,
        alarmLevel: this.drawType
      });
      let max = 0;
      for (let i = 1; i < this.canvas._objects.length; i++) {
        if (this.canvas._objects[i].index > max) max = this.canvas._objects[i].index;
      }
      polygon.index = max + 1;
      this.canvas.add(polygon);
      this.activeLine = null;
      this.activeShape = null;
      this.polygonMode = false;
      this.doDrawing = false;
      // this.drawType = null;
      fabric.Image.fromURL(this.deleteIconURL, this.deletecallback);
    },
    // 从画布中删除当前选中的对象
    deleteObject() {
      const activeObject = this.canvas.getActiveObject();
      if (activeObject) {
        this.canvas._objects.forEach(item => {
          if (item.index === activeObject.index) {
            this.canvas.remove(item);
          }
        });
        this.canvas.remove(activeObject);
        this.canvas.renderAll();
      }
    },
    // 渲染删除按钮
    async deletecallback(img) {
      const self = this;
      let max = 0;
      for (let i = 1; i < this.canvas._objects.length; i++) {
        if (this.canvas._objects[i].index > max) max = this.canvas._objects[i].index;
      }
      img.index = max;
      const oImg = await img.set({
        left: this.pointArray[0].left - 20,
        top: this.pointArray[0].top - 20,
        width: 40,
        height: 40,
        angle: 0
      }).scale(0.8);
      this.canvas.add(oImg).renderAll();
      this.canvas.setActiveObject(oImg);
      oImg.on('mousedown', function() {
        self.deleteObject();
      });
    },
    // 回显详情信息
    loadDraw() {
      const self = this;
      if (self.drawinfo.id === '') return;
      const pointGroup = JSON.parse(self.drawinfo.pointInfo);
      const imgInfo = JSON.parse(self.drawinfo.imgInfo);
      self.bgImgSrc = self.drawinfo.img;
      imgInfo.src = self.drawinfo.img;
      // 1、加载底图
      fabric.util.enlivenObjects([imgInfo], objects => {
        objects.forEach(o => {
          o.selectable = false;
          o.hasControls = false;
          o.centeredScaling = false;
          this.canvas.add(o);
        });
        // 2、处理多边形绘制回显操作
        pointGroup.forEach(async (item, index) => {
          if (item.pointInfo !== '') {
            const polygon = new fabric.Polygon(item.pointInfo, {
              stroke: self.colorGrounp[item.alarmLevel],
              strokeWidth: self.drawWidth,
              fill: self.colorGrounpFill[item.alarmLevel],
              opacity: 1,
              selectable: false,
              hasBorders: false,
              hasControls: false,
              alarmLevel: item.alarmLevel
            });
            polygon.index = index;
            self.canvas.add(polygon);
            self.activeLine = null;
            self.activeShape = null;
            self.polygonMode = false;
            self.doDrawing = false;
            if (!self.readstate) {
              fabric.Image.fromURL(self.deleteIconURL, async img => {
                const _self = this;
                img.index = index;
                const oImg = await img.set({
                  left: item.pointInfo[0].x - 20,
                  top: item.pointInfo[0].y - 20,
                  width: 40,
                  height: 40,
                  angle: 0
                }).scale(0.8);
                this.canvas.add(oImg);
                oImg.on('mousedown', function() {
                  _self.deleteObject();
                });
              });
            }
          }
        });
      });
      self.canvas.renderAll();
    }
  }

5. 全代码

累了累了,开始摆烂,以后再调整,直接放全代码吧

<template>
  <div class="maintenancePlanAdd">
    <div class="panel-body">
      <div class="demo">
        <canvas id="canvas" :width="width" :height="height" />
        <div class="draw-btn-group" v-show="!readstate">
          <div>
            <el-button class="el-icon-upload" size="mini" type="primary"
                       style="width: 80px !important;" @click="uploadImgConfirm"
            >
              图片上传
            </el-button>
            <el-button size="mini" type="danger" icon="el-icon-delete" @click="clean">
              清除
            </el-button>
          </div>
          <div>
            <el-button
              v-show="bgImgSrc !== ''"
              v-for="(item, index) in alarmLevel"
              :key="index"
              :style="{background:colorGrounp[index]}"
              size="mini"
              @click="drawPolygon(index)"
            >{{ item }}
            </el-button>
          </div>
        </div>
      </div>
    </div>
    <img id="expImg" :src="bgImgSrc">
    <img id="img" :src="bgImgSrc">
    <input v-show="false" type="file" @change="uploadImgChange" id="imgInput" accept="image/*">
    <p class="tip-title" v-show="bgImgSrc === ''">上传的图片可以进行拖拽调整大小和方向</p>
  </div>
</template>
<script>
export default {
  props: ['readstate', 'drawinfo'],
  data() {
    return {
      bgImgFlag: true,
      bgImgSrc: '',
      imgFile: {},
      width: 800,
      height: 400,
      alarmLevel: ['一级风险', '二级风险', '三级风险', '四级风险'],
      colorGrounp: ['rgba(51, 164, 255, 1)', 'rgba(255, 200, 89, 1)', 'rgba(255, 160, 89, 1)', 'rgba(196,43, 1, 1)'],
      colorGrounpFill: ['rgba(51, 164, 255, 0.3)', 'rgba(255, 200, 89, 0.3)', 'rgba(255, 160, 89, 0.3)', 'rgba(196,43, 1, 0.3)'],
      canvas: {},
      mouseFrom: {},
      mouseTo: {},
      drawType: '', // 当前绘制图像ROI
      drawWidth: 2, // 笔触宽度
      drawingObject: null, // 当前绘制对象
      moveCount: 1, // 绘制移动计数器
      doDrawing: false, // 绘制状态
      // polygon 相关参数
      polygonMode: false,
      pointArray: [],
      lineArray: [],
      savePointsGroup: [],
      activeShape: false,
      activeLine: '',
      line: {},
      deleteIconURL: require('@/assets/screen/icon-close.png')
    };
  },
  watch: {
    drawinfo: {
      handler(n) {
        this.drawinfo = n;
        this.$nextTick(() => {
          this.loadDraw();
        });
      },
      deep: true
    },
    readstate: {
      handler(n) {
        this.readstate = n;
      },
      deep: true
    },
    width() {
      this.canvas.setWidth(this.width);
    },
    height() {
      this.canvas.setHeight(this.height);
    }
  },
  mounted() {
    this.canvas = new fabric.Canvas('canvas', {
      skipTargetFind: false, // 当为真时,跳过目标检测。目标检测将返回始终未定义。点击选择将无效
      selectable: false, // 为false时,不能选择对象进行修改
      selection: false // 是否可以多个对象为一组
    });
    this.canvas.selectionColor = 'rgba(0,0,0,0.05)';
    this.canvas.on('mouse:down', this.mousedown);
    this.canvas.on('mouse:move', this.mousemove);
    document.onkeydown = e => {
      // 键盘 delect删除所选元素
      if (e.keyCode == 46) {
        this.deleteObj();
      }
      // ctrl+z 删除最近添加的元素
      if (e.keyCode == 90 && e.ctrlKey) {
        this.canvas.remove(
          this.canvas.getObjects()[this.canvas.getObjects().length - 1]
        );
      }
    };
    this.$nextTick(() => {
      this.loadDraw();
    });
  },
  methods: {
    // 保存当前画布为png图片
    save() {
      var canvas = document.getElementById('canvas');
      var imgData = canvas.toDataURL('png');
      imgData = imgData.replace('image/png', 'image/octet-stream');
      // 下载后的问题名,可自由指定
      var filename = 'drawingboard_' + (new Date()).getTime() + '.' + 'png';
      this.saveFile(imgData, filename);
    },
    saveFile(data, filename) {
      var save_link = document.createElement('a');
      save_link.href = data;
      save_link.download = filename;
      var event = document.createEvent('MouseEvents');
      event.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
      save_link.dispatchEvent(event);
    },
    // 提交绘制内容
    submitDraw() {
      const params = {
        pointInfo: [],
        imgInfo: '',
        img: ''
      };
      this.canvas.toJSON(['alarmLevel', 'isBgImg']).objects.forEach(item => {
        const element = {
          alarmLevel: item.alarmLevel,
          pointInfo: ''
        };
        if (item?.points) {
          element.pointInfo = item.points;
          params.pointInfo.push(element);
        }
        if (item?.isBgImg) {
          params.imgInfo = item;
          params.img = item.src;
          delete params.imgInfo.src;
        }
      });
      this.$emit('saveDraw', params);
    },
    // 清除画布
    clean() {
      this.$confirm('是否清除图片和标记?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        this.canvas.clear();
        this.bgImgSrc = '';
        this.bgImgFlag = true;
      });
    },
    // 从已渲染的DOM元素加载图片至canvas
    loadExpImg() {
      const imgElement = document.getElementById('expImg');
      imgElement.onload = () => {
        // eslint-disable-next-line new-cap
        new fabric.Image.fromURL(
          imgElement.src,
          img => {
            img.scale(0.3);
            img.set({
              originX: 'center',
              originY: 'center'
            }, { crossOrigin: 'anonymous' });
            img.on('scaling', e => { // 拉伸事件
              const h = img.scaleY;
              const w = img.scaleX;
              if (h !== w || w == h) { // 判断缩放值相等或不相等后执行图片等比缩放
                if (e.e.movementY == -1 || e.e.movementY == 1) {
                  img.scale(h);// 缩放
                } else {
                  img.scale(w);
                }
              }
            });
            img.setCoords();
            img.centeredScaling = true;
            img.centerTransform = true;
            this.canvas.add(img);
            this.canvas.centerObject(img);
            this.canvas.renderAll();
          }, {
            selectable: true,
            hasControls: true,
            centeredScaling: false,
            zIndex: -99,
            isBgImg: true
          });
      };
    },
    // 上传确认
    uploadImgConfirm() {
      if (this.bgImgSrc !== '') {
        this.$confirm('是否重新上传标记?', '提示', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning'
        }).then(() => {
          this.canvas.clear();
          this.bgImgFlag = true;
          document.getElementById('imgInput').click();
        });
      } else {
        document.getElementById('imgInput').click();
      }
    },
    // 从文件加载图片至canvas
    uploadImgChange() {
      // 获取文件
      var eleImportInput = document.getElementById('imgInput');
      this.imgFile = eleImportInput.files[0];
      var imgTitle = '';
      // 从reader中获取选择文件的src
      if (/\.(jpe?g|png|gif)$/i.test(this.imgFile.name)) {
        var reader = new FileReader();
        var _this = this;
        reader.addEventListener(
          'load',
          function() {
            imgTitle = _this.imgFile.name;
            _this.bgImgSrc = this.result;
          },
          false
        );
        reader.readAsDataURL(this.imgFile);
      }
      this.loadExpImg();
    },
    // 鼠标按下时触发
    mousedown(e) {
      if (undefined === e) return;
      // 记录鼠标按下时的坐标
      var xy = e.pointer || this.transformMouse(e.e.offsetX, e.e.offsetY);
      this.mouseFrom.x = xy.x;
      this.mouseFrom.y = xy.y;
      this.doDrawing = true;

      this.canvas.skipTargetFind = false;
      try {
        // 此段为判断是否闭合多边形,点击红点时闭合多边形
        if (this.pointArray.length > 1) {
          // e.target.id == this.pointArray[0].id 表示点击了初始红点
          if (e.target && e.target.id == this.pointArray[0].id) {
            this.generatePolygon();
            return;
          }
        }
        // 未点击红点则继续作画
        if (this.polygonMode && this.pointArray.length < 4) {
          this.addPoint(e);
        } else if (this.polygonMode && this.pointArray.length > 0) {
          this.$message.warning('最多设置四个点');
        }
      } catch (error) {
        console.log(error);
      }
    },
    // 鼠标松开执行
    mouseup(e) {
      if (undefined === e) return;
      var xy = e.pointer || this.transformMouse(e.e.offsetX, e.e.offsetY);
      this.mouseTo.x = xy.x;
      this.mouseTo.y = xy.y;
      this.drawingObject = null;
      this.moveCount = 1;
    },
    // 鼠标移动过程中已经完成了绘制
    mousemove(e) {
      if (undefined === e) return;
      if (this.moveCount % 2 && !this.doDrawing) {
        // 减少绘制频率
        return;
      }
      this.moveCount++;
      var xy = e.pointer || this.transformMouse(e.e.offsetX, e.e.offsetY);
      this.mouseTo.x = xy.x;
      this.mouseTo.y = xy.y;
      if (this.activeLine && this.activeLine.class == 'line') {
        var pointer = this.canvas.getPointer(e.e);
        this.activeLine.set({
          x2: pointer.x,
          y2: pointer.y
        });
        var points = this.activeShape.get('points');
        points[this.pointArray.length] = {
          x: pointer.x,
          y: pointer.y,
          zIndex: 1
        };
        this.activeShape.set({
          points: points
        });
        this.canvas.renderAll();
      }
      this.canvas.renderAll();
    },
    deleteObj() {
      this.canvas.getActiveObjects().map(item => {
        this.canvas.remove(item);
      });
    },
    transformMouse(mouseX, mouseY) {
      return {
        x: mouseX / 1,
        y: mouseY / 1
      };
    },
    // 绘制多边形开始
    drawPolygon(data) {
      if (this.bgImgFlag) {
        this.canvas.getObjects().forEach(obj => {
          if (obj.isBgImg) {
            obj.hasControls = false;
            obj.selectable = false;
            obj.evented = false;
          }
        });
        this.bgImgFlag = false;
        this.canvas.renderAll();
      }
      this.drawType = data;
      this.polygonMode = true;
      this.pointArray = []; // 顶点集合
      this.lineArray = []; // 线集合
      this.canvas.isDrawingMode = false;
    },
    addPoint(e) {
      var random = Math.floor(Math.random() * 10000);
      var id = new Date().getTime() + random;
      var circle = new fabric.Circle({
        radius: 5,
        fill: '#ffffff',
        stroke: '#333333',
        strokeWidth: 0.5,
        left: (e.pointer.x || e.e.layerX) / this.canvas.getZoom(),
        top: (e.pointer.y || e.e.layerY) / this.canvas.getZoom(),
        selectable: false,
        hasBorders: false,
        hasControls: false,
        originX: 'center',
        originY: 'center',
        id: id,
        objectCaching: false
      });
      if (this.pointArray.length == 0) {
        circle.set({
          fill: this.colorGrounp[this.drawType]
        });
      }
      var points = [
        (e.pointer.x || e.e.layerX) / this.canvas.getZoom(),
        (e.pointer.y || e.e.layerY) / this.canvas.getZoom(),
        (e.pointer.x || e.e.layerX) / this.canvas.getZoom(),
        (e.pointer.y || e.e.layerY) / this.canvas.getZoom()
      ];
      this.line = new fabric.Line(points, {
        strokeWidth: 2,
        fill: this.colorGrounp[this.drawType],
        stroke: this.colorGrounp[this.drawType],
        class: 'line',
        originX: 'center',
        originY: 'center',
        selectable: false,
        hasBorders: false,
        hasControls: false,
        evented: false,
        objectCaching: false
      });
      if (this.activeShape) {
        var pos = this.canvas.getPointer(e.e);
        var points = this.activeShape.get('points');
        points.push({
          x: pos.x,
          y: pos.y
        });
        var polygon = new fabric.Polygon(points, {
          stroke: '#333333',
          strokeWidth: 1,
          fill: this.colorGrounpFill[this.drawType],
          opacity: 0.3,
          selectable: false,
          hasBorders: false,
          hasControls: false,
          evented: false,
          objectCaching: false
        });
        this.canvas.remove(this.activeShape);
        this.canvas.add(polygon);
        this.activeShape = polygon;
        this.canvas.renderAll();
      } else {
        var polyPoint = [
          {
            x: (e.pointer.x || e.e.layerX) / this.canvas.getZoom(),
            y: (e.pointer.y || e.e.layerY) / this.canvas.getZoom()
          }
        ];
        var polygon = new fabric.Polygon(polyPoint, {
          stroke: '#333333',
          strokeWidth: 1,
          fill: '#cccccc',
          opacity: 0.3,
          selectable: false,
          hasBorders: false,
          hasControls: false,
          evented: false,
          objectCaching: false
        });
        this.activeShape = polygon;
        this.canvas.add(polygon);
      }
      this.activeLine = this.line;
      this.pointArray.push(circle);
      this.lineArray.push(this.line);
      this.canvas.add(this.line);
      this.canvas.add(circle);
    },
    generatePolygon() {
      var points = [];
      this.pointArray.map((point, index) => {
        points.push({
          x: point.left,
          y: point.top
        });
        this.canvas.remove(point);
      });
      this.lineArray.map((line, index) => {
        this.canvas.remove(line);
      });
      this.canvas.remove(this.activeShape).remove(this.activeLine);
      var polygon = new fabric.Polygon(points, {
        stroke: this.colorGrounp[this.drawType],
        strokeWidth: this.drawWidth,
        fill: this.colorGrounpFill[this.drawType],
        opacity: 1,
        selectable: false,
        hasBorders: false,
        hasControls: false,
        alarmLevel: this.drawType
      });
      let max = 0;
      for (let i = 1; i < this.canvas._objects.length; i++) {
        if (this.canvas._objects[i].index > max) max = this.canvas._objects[i].index;
      }
      polygon.index = max + 1;
      this.canvas.add(polygon);
      this.activeLine = null;
      this.activeShape = null;
      this.polygonMode = false;
      this.doDrawing = false;
      // this.drawType = null;
      fabric.Image.fromURL(this.deleteIconURL, this.deletecallback);
    },
    // 从画布中删除当前选中的对象
    deleteObject() {
      const activeObject = this.canvas.getActiveObject();
      if (activeObject) {
        this.canvas._objects.forEach(item => {
          if (item.index === activeObject.index) {
            this.canvas.remove(item);
          }
        });
        this.canvas.remove(activeObject);
        this.canvas.renderAll();
      }
    },
    // 渲染删除按钮
    async deletecallback(img) {
      const self = this;
      let max = 0;
      for (let i = 1; i < this.canvas._objects.length; i++) {
        if (this.canvas._objects[i].index > max) max = this.canvas._objects[i].index;
      }
      img.index = max;
      const oImg = await img.set({
        left: this.pointArray[0].left - 20,
        top: this.pointArray[0].top - 20,
        width: 40,
        height: 40,
        angle: 0
      }).scale(0.8);
      this.canvas.add(oImg).renderAll();
      this.canvas.setActiveObject(oImg);
      oImg.on('mousedown', function() {
        self.deleteObject();
      });
    },
    // 回显详情信息
    loadDraw() {
      const self = this;
      if (self.drawinfo.id === '') return;
      const pointGroup = JSON.parse(self.drawinfo.pointInfo);
      const imgInfo = JSON.parse(self.drawinfo.imgInfo);
      self.bgImgSrc = self.drawinfo.img;
      imgInfo.src = self.drawinfo.img;
      // 1、加载底图
      fabric.util.enlivenObjects([imgInfo], objects => {
        objects.forEach(o => {
          o.selectable = false;
          o.hasControls = false;
          o.centeredScaling = false;
          this.canvas.add(o);
        });
        // 2、处理多边形绘制回显操作
        pointGroup.forEach(async (item, index) => {
          if (item.pointInfo !== '') {
            const polygon = new fabric.Polygon(item.pointInfo, {
              stroke: self.colorGrounp[item.alarmLevel],
              strokeWidth: self.drawWidth,
              fill: self.colorGrounpFill[item.alarmLevel],
              opacity: 1,
              selectable: false,
              hasBorders: false,
              hasControls: false,
              alarmLevel: item.alarmLevel
            });
            polygon.index = index;
            self.canvas.add(polygon);
            self.activeLine = null;
            self.activeShape = null;
            self.polygonMode = false;
            self.doDrawing = false;
            if (!self.readstate) {
              fabric.Image.fromURL(self.deleteIconURL, async img => {
                const _self = this;
                img.index = index;
                const oImg = await img.set({
                  left: item.pointInfo[0].x - 20,
                  top: item.pointInfo[0].y - 20,
                  width: 40,
                  height: 40,
                  angle: 0
                }).scale(0.8);
                this.canvas.add(oImg);
                oImg.on('mousedown', function() {
                  _self.deleteObject();
                });
              });
            }
          }
        });
      });
      self.canvas.renderAll();
    }
  }
};
</script>

<style lang="scss" scoped>
.el-container {
  flex-direction: column;
}

img {
  display: none;
}

.demo {
  display: flex;
  flex-direction: column;
  align-items: center;
}

canvas {
  border: 1px dashed #2695F9;
}

.draw-btn-group {
  width: 100%;
  margin-top: 10px;
  display: flex;
  align-items: center;
  justify-content: space-between;

  .el-button {
    color: #ffffff;
  }
}

.tip-title {
  top: 40%;
  left: 33%;
  position: absolute;
  font-size: 16px;
  color: #C2C7CC;
  margin: 0;
}
</style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1396735.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Pytorch从零开始实战17

Pytorch从零开始实战——生成对抗网络入门 本系列来源于365天深度学习训练营 原作者K同学 文章目录 Pytorch从零开始实战——生成对抗网络入门环境准备模型定义开始训练总结 环境准备 本文基于Jupyter notebook&#xff0c;使用Python3.8&#xff0c;Pytorch1.8cpu&#xf…

【MIdjourney】一些材质相关的关键词

1.多维剪纸(Multidimensional papercut) "Multidimensional papercut"&#xff08;多维剪纸&#xff09;是一种剪纸艺术形式&#xff0c;通过多层次的剪纸技巧和设计来创造出立体感和深度感。这种艺术形式通常涉及在不同的纸层上剪裁不同的图案&#xff0c;并将它们…

Node.js基础知识点(四)

本节介绍一下最简单的http服务 一.http 可以使用Node 非常轻松的构建一个web服务器&#xff0c;在 Node 中专门提供了一个核心模块&#xff1a;http http 这个模块的就可以帮你创建编写服务器。 1. 加载 http 核心模块 var http require(http) 2. 使用 http.createServe…

Java学习(二十一)--JDBC/数据库连接池

为什么需要 传统JDBC数据库连接&#xff0c;使用DriverManager来获取&#xff1b; 每次向数据库建立连接时都要将Connection加载到内存中&#xff0c;再验证IP地址、用户名和密码&#xff08;0.05s~1s)时间。 需要数据库连接时候&#xff0c;就向数据库要求一个&#xf…

卷积神经网络简介-AI快速进阶系列

1. 概述 在本教程中&#xff0c;我们将研究卷积神经网络背后的理论及其架构。 我们将首先讨论通常使用卷积神经网络 &#xff08;CNN&#xff09; 执行的任务和特征提取问题。然后&#xff0c;我们将讨论为什么需要CNN&#xff0c;以及为什么传统的前馈神经网络是不够的。 然…

Redis实战之-分布式锁

一、基本原理和实现方式对比 分布式锁&#xff1a;满足分布式系统或集群模式下多进程可见并且互斥的锁。 分布式锁的核心思想就是让大家都使用同一把锁&#xff0c;只要大家使用的是同一把锁&#xff0c;那么我们就能锁住线程&#xff0c;不让线程进行&#xff0c;让程序串行…

20230117-yolov5训练环境搭建

文章目录 1.参考资料2.服务器环境3.安装过程4.问题与解决5.补充6.其它技巧 1.参考资料 https://blog.csdn.net/qq_43573527/article/details/132963466 long错误解决方案 https://pytorch.org/get-started/previous-versions/ pytorch下载的位置 2.服务器环境 conda环境&…

RabbitMQ入门精讲

1. 什么是消息队列 消息指的是两个应用间传递的数据。数据的类型有很多种形式&#xff0c;可能只包含文本字符串&#xff0c;也可能包含嵌入对象。 “消息队列(Message Queue)”是在消息的传输过程中保存消息的容器。在消息队列中&#xff0c;通常有生产者和消费者两个角色。…

NAT实验

一&#xff1a;实验要求 二&#xff1a;实验分析 拓扑图 三&#xff1a;实验配置 1&#xff1a;路由器配置 R1配置IP R2配置IP 2&#xff1a;缺省路由 查看路由表 3&#xff1a;端口映射 4&#xff1a;pc、HTTP配置 5:DNS、client配置 四&#xff1a;实验结果 pc可以ping…

响应式Web开发项目教程(HTML5+CSS3+Bootstrap)第2版 例4-5 select

代码 <!doctype html> <html> <head> <meta charset"utf-8"> <title>select</title> </head><body> <!--单选下拉菜单可设置默认选中项--> 所在城市&#xff08;单选&#xff09;:<br> <select>…

如何使用Portainer部署web站点并实现无公网ip远程访问

文章目录 前言1. 安装Portainer1.1 访问Portainer Web界面 2. 使用Portainer创建Nginx容器3. 将Web静态站点实现公网访问4. 配置Web站点公网访问地址4.1公网访问Web站点 5. 固定Web静态站点公网地址6. 固定公网地址访问Web静态站点 前言 Portainer是一个开源的Docker轻量级可视…

第十一章 请求响应

第十一章 请求响应 1.概述2.请求-postman工具3.请求-简单参数&实体参数4.请求-数组集合参数5.请求-日期参数&JSON参数6.请求-路径参数7.响应-ResponseBody&统一响应结果8.响应-案例 1.概述 将前端发送的请求封装为HttpServletRequest对象 在通过HttpServletRespo…

JVM工作原理与实战(十六):运行时数据区-Java虚拟机栈

专栏导航 JVM工作原理与实战 RabbitMQ入门指南 从零开始了解大数据 目录 专栏导航 前言 一、运行时数据区 二、Java虚拟机栈 1.栈帧的组成 2.局部变量表 3.操作数栈 4.帧数据 总结 前言 JVM作为Java程序的运行环境&#xff0c;其负责解释和执行字节码&#xff0c;管理…

深入解析 Java 方法引用:Lambda 表达式的进化之路

前言 方法引用是 Java 8 提供的一种新特性&#xff0c;它允许我们更简洁地传递现有方法作为参数。这项特性实际上是对 Lambda 表达式的一种补充&#xff0c;通过方法引用&#xff0c;我们可以直接引用现有方法&#xff0c;而无需编写完整的Lambda表达式。最近在使用方法引用的…

ElasticSearch扫盲概念篇[ES系列] - 第500篇

历史文章&#xff08;文章累计500&#xff09; 《国内最全的Spring Boot系列之一》 《国内最全的Spring Boot系列之二》 《国内最全的Spring Boot系列之三》 《国内最全的Spring Boot系列之四》 《国内最全的Spring Boot系列之五》 《国内最全的Spring Boot系列之六》 E…

每日一题——LeetCode1252.奇数值单元格的数目

进阶&#xff1a;你可以设计一个时间复杂度为 O(n m indices.length) 且仅用 O(n m) 额外空间的算法来解决此问题吗&#xff1f; 方法一 直接模拟&#xff1a; 创建一个n x m的矩阵&#xff0c;初始化所有元素为0&#xff0c;对于indices中的每一对[ri,ci]&#xff0c;将矩…

5W紫外激光打标机优势特点

紫外激光打标机在当今市场上备受关注&#xff0c;而5W紫外激光打标机更是其中的佼佼者。作为一种高精度、高效率的激光加工设备&#xff0c;5W紫外激光打标机在各个领域都有着广泛的应用。 首先&#xff0c;让我们来了解一下5W紫外激光打标机的基本原理。紫外激光打标机利用高能…

Springboot日志框架logback与log4j2

目录 Springboot日志使用 Logback日志 日志格式 自定义日志格式 日志文件输出 Springboot启用log4j2日志框架 Springboot日志使用 Springboot底层是使用slf4jlogback的方式进行日志记录 Logback日志 trace&#xff1a;级别最低 debug&#xff1a;调试级别的&#xff0c…

Google play 应用批量下架的可能原因及应对指南

想必大多数上架马甲包或矩阵式上架的开发者们&#xff0c;都遭遇过应用包批量被下架、账号被封的情况。这很令人苦恼&#xff0c;那造成这种情况的可能原因有哪些呢&#xff1f;以及如何降低这种情况发生&#xff1f; 1、代码问题 通常上架成功后被下架的应用&#xff0c;很可…

基于局部信息提取的人脸标志检测算法matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1 人脸检测 4.2 局部区域选择 4.3 特征提取 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab2022a 3.部分核心程序 .........................................…