前言

vue+konva.js(未使用vue-konva)实现数据标注矩形和多边形功能
矩形和多边形都可以移动调整,未编写删除功能。注释写的还需详细吧


一、矩形和多边形绘画

在这里插入图片描述


二、矩形和多边形重新调整

在这里插入图片描述


三、代码

<template>
  <div>
    <el-menu class="el-menu-demo" mode="horizontal" @select="handleSelect">
      <template v-for="(item, i) in tools">
        <el-menu-item :index="i.toString()">
          <i class="el-icon-location"></i>
          <span slot="title">{{ item.nameCN }}</span>
        </el-menu-item>
      </template>
    </el-menu>
    <div id="map" ref="map"></div>
  </div>
</template>

<style>
#map {
  background: #ddd;
  overflow: hidden;
  width: 100%;
  aspect-ratio: 16/9;
}
</style>

<script>
import Konva from "konva";
export default {
  name: "MyKonva",
  data() {
    return {
      stage: null,
      layer: null,
      shape: null,
      image: { src: require("@/assets/dog.jpeg") },
      tools: [
        { id: 1, nameEN: "rect", nameCN: "矩形" },
        { id: 2, nameEN: "poly", nameCN: "多边形" },
      ],
      currentTool: "",
      drawing: false, //一开始不能绘画
      currentDrawingShape: null, //现在绘画的图形
      pointStart: [], //记录鼠标按下的起始坐标
      polygonPoints: [], //存储绘画多边形各个顶点的数组
    };
  },
  mounted() {
    this.initKonvaStage();
  },
  methods: {
    /**
     *初始化konva舞台
     */
    initKonvaStage() {
      //1实例化stage层
      this.stage = new Konva.Stage({
        container: "map",
        width: this.$refs.map.clientWidth,
        height: this.$refs.map.clientHeight,
      });
      this.stage.container().style.cursor = "crosshair";
      //2实例化layer层
      this.layer = new Konva.Layer();
      var imageObj = new Image();
      //imageObj的this是imagedom对象,不是vc
      var vc_this = this;
      imageObj.onload = function () {
        //3实例化shape层
        vc_this.shape = new Konva.Image({
          x: 0,
          y: 0,
          width: vc_this.stage.width(),
          height: vc_this.stage.height(),
          image: imageObj,
        });
        //4将layer层添加到stage层
        vc_this.stage.add(vc_this.layer);
        // 5将shape层添加到layer层
        vc_this.layer.add(vc_this.shape);
      };
      imageObj.src = this.image.src;
      //给***舞台***绑定事件
      //鼠标按下
      this.stage.on("mousedown", (e) => {
        //图形起始点只能在图片层上,移除变形框
        if (e.target === this.shape) {
          // 如果有,就移除舞台上唯一一个的变形框
          if (this.stage.find("Transformer").length != 0) {
            this.stage.find("Transformer")[0].destroy();
          }
          //如果不在绘画且舞台上的多边形被选中
          if (!this.drawing && this.stage.find("Circle").length != 0) {
            var circlePoints = this.stage.find("Circle");
            for (var i = 0; i < circlePoints.length; i++) {
              if (circlePoints[i].visible()) {
                //隐藏顶点
                this.stage.find("Circle").forEach((element) => {
                  element.hide();
                });
                return;
              }
            }
          }
          this.layer.draw();
          //开始初始绘画
          this.stageMousedown(this.currentTool, e);
          return;
        }
        //允许后续点绘画在其他图形上
        if (this.drawing) {
          this.stageMousedown(this.currentTool, e);
          return;
        }
      });
      //鼠标移动
      this.stage.on("mousemove", (e) => {
        if (this.currentTool && this.drawing) {
          //绘画中
          this.stageMousemove(this.currentTool, e);
        }
      });
      //鼠标放开
      this.stage.on("mouseup", (e) => {
        this.stageMouseup(this.currentTool, e);
      });
    },
    /**
     * 圆形
     * @param //x x坐标
     * @param //y y坐标
     */
    drawCircle(x, y) {
      const circle = new Konva.Circle({
        name: "circle",
        x: x,
        y: y,
        radius: 5,
        visible: true, //是否显示
        fill: "#ffffff",
        stroke: "#333333",
        draggable: false,
        strokeWidth: 0.5,
      });
      var vc_this = this;
      var xChange, yChange;
      this.layer.add(circle);
      this.layer.draw();
      circle.on("dragstart", (e) => {
        var polyPoints = vc_this.currentDrawingShape
          .getChildren((element) => {
            return element.getClassName() === "Line";
          })[0]
          .points();
        //查找拖拽了多边形的哪个点
        for (var i = 0; i < polyPoints.length; i += 2) {
          if (
            circle.getAttr("x") == polyPoints[i] &&
            circle.getAttr("y") == polyPoints[i + 1]
          ) {
            xChange = i;
            yChange = i + 1;
            break;
          }
        }
      });
      circle.on("dragmove", (e) => {
        //更改拖拽多边形点的位置
        var polyPoints = vc_this.currentDrawingShape
          .getChildren((element) => {
            return element.getClassName() === "Line";
          })[0]
          .points();
        /*   e.evt.offsetX - vc_this.currentDrawingShape.getAttr('x') ---> 抵消拖动组的xy影响  */
        polyPoints[xChange] =
          e.evt.offsetX - vc_this.currentDrawingShape.getAttr("x");
        polyPoints[yChange] =
          e.evt.offsetY - vc_this.currentDrawingShape.getAttr("y");
        vc_this.currentDrawingShape
          .getChildren((element) => {
            return element.getClassName() === "Line";
          })[0]
          .points(polyPoints);
      });
      return circle;
    },
    /**
     * 矩形
     * @param //x x坐标
     * @param //y y坐标
     * @param //w 宽
     * @param //h 高
     * @param //c 颜色
     * @param //sw 该值大于0-表示空心矩形(描边宽),等于0-表示实心矩形*/
    drawRect(x, y, w, h) {
      const rect = new Konva.Rect({
        name: "rect",
        x: x,
        y: y,
        width: w,
        height: h,
        fill: "green",
        stroke: "green",
        strokeWidth: 1,
        opacity: 0.3,
        draggable: true,
        strokeScaleEnabled: false,
      });
      this.currentDrawingShape = rect;
      this.layer.add(rect);
      this.layer.draw();
      var vc_this = this;
      rect.on("mouseenter", function () {
        vc_this.stage.container().style.cursor = "move";
      });
      rect.on("mouseleave", function () {
        vc_this.stage.container().style.cursor = "crosshair";
      });
      rect.on("mousedown", (e) => {
        //如果不是正在绘画图形时,可以添加变形框
        if (!vc_this.drawing) {
          // 如果有,就移除舞台上唯一一个的transformer
          if (vc_this.stage.find("Transformer").length != 0) {
            vc_this.stage.find("Transformer")[0].destroy();
          }
          var tr = new Konva.Transformer({
            anchorCornerRadius: 5,
            rotateEnabled: false, //关闭旋转
            node: rect,
            keepRatio: false, //不等比缩放
            enabledAnchors: [
              "top-left",
              "top-right",
              "bottom-left",
              "bottom-right",
            ],
            borderDash: [3, 3],
          });
          vc_this.layer.add(tr);
          tr.attachTo(e.target);
          vc_this.layer.draw();
          return;
        }
        //绘画图形时,不能被拖动
        if (vc_this.drawing) {
          rect.setAttr("draggable", false);
          return;
        }
      });
    },
    /**
     *多边形
     * @param points 多边形绘画的各个顶点,类型数组
     */
    drawPloygon(points) {
      const poly = new Konva.Line({
        name: "poly",
        points: points,
        fill: "red",
        stroke: "red",
        strokeWidth: 1,
        draggable: false,
        opacity: 0.3,
        lineCap: "round",
        lineJoin: "round",
        closed: true,
        strokeScaleEnabled: false,
      });
      this.currentDrawingShape = poly;
      this.layer.add(poly);
      this.layer.draw();
      var vc_this = this;
      poly.on("mouseenter", (e) => {
        vc_this.stage.container().style.cursor = "move";
      });
      poly.on("mouseleave", (e) => {
        vc_this.stage.container().style.cursor = "crosshair";
      });
      poly.on("mousedown", (e) => {
        //如果不是正在绘画图形时,可以显示顶点
        if (!vc_this.drawing) {
          vc_this.stage.container().style.cursor = "move";
          console.log("mousedown");
          //设置现在绘画节点的对象为该多边形和顶点的组
          vc_this.currentDrawingShape = poly.getParent();
          //先隐藏全部顶点
          vc_this.stage.find("Circle").forEach((element) => {
            element.hide();
            //解绑第一个红色顶点的事件
            element.off("mousedown");
          });
          //显示现在操作多边形的原来的顶点
          vc_this.currentDrawingShape
            .getChildren((element) => {
              return element.getClassName() === "Circle";
            })
            .forEach((element) => {
              element.show();
              element.setAttr("draggable", true);
            });
          // 如果要让顶点和多边形一起拖拽,必须设置,多边形不能被拖拽
          poly.setAttr("draggable", false);
          poly.getParent().setAttr("draggable", true);
          //使所有顶点在顶层显示
          vc_this.stage.find("Circle").forEach((element) => {
            element.moveToTop();
          });
          vc_this.layer.draw();
        } else {
          //绘画时,鼠标移入多边形,设置组不可以拖动
          vc_this.stage.container().style.cursor = "crosshair";
          poly.getParent().setAttr("draggable", false);
        }
      });

      poly.getParent().on("dragend", (e) => {
        vc_this.stage.container().style.cursor = "crosshair";
        poly.getParent().setAttr("draggable", false);
      });
      return poly;
    },
    /**
     * 组件el-menu点击事件
     * @param key 索引值
     * @param keyPath
     */
    handleSelect(key, keyPath) {
      //设置当前工具
      this.currentTool = this.tools[key].nameEN;
    },
    /**
     * 在舞台上鼠标点下发生的事件
     * @param currentTool 当前选择的工具
     * @param e 传入的event对象
     */
    stageMousedown(currentTool, e) {
      switch (currentTool) {
        case "rect":
          //初始化矩形
          var x = e.evt.offsetX,
            y = e.evt.offsetY;
          this.pointStart = [x, y];
          this.drawRect(x, y, 0, 0);
          break;
        case "poly":
          //如果数组长度小于2,初始化多边形和顶点,是它们成为一组,否则什么都不做
          if (this.polygonPoints.length < 2) {
            var x = e.evt.offsetX,
              y = e.evt.offsetY;
            //拖拽组
            var group = new Konva.Group({
              x: 0,
              y: 0,
              name: "pointsAndPoly",
              draggable: false,
            });
            //添加多边形的点
            group.add(this.addPoint(e));
            //绘画多边形
            this.polygonPoints = [x, y];
            group.add(this.drawPloygon(this.polygonPoints));
            //使所有顶点在顶层显示
            this.stage.find("Circle").forEach((element) => {
              element.moveToTop();
            });
            this.layer.add(group);
            this.stage.draw();
          } //多边形增加顶点
          else {
            var x = e.evt.offsetX,
              y = e.evt.offsetY;
            //group继续添加多边形的点
            this.currentDrawingShape.getParent().add(this.addPoint(e));
            this.polygonPoints.push(x);
            this.polygonPoints.push(y);
            //绘画多边形
            this.currentDrawingShape.setAttr("points", this.polygonPoints);
            //使所有顶点在顶层显示
            this.stage.find("Circle").forEach((element) => {
              element.moveToTop();
            });
            this.stage.draw();
          }
          break;
        default:
          break;
      }
      this.drawing = true;
    },
    /**
     * 鼠标在舞台上移动事件
     * @param currentTool 当前选择的工具
     * @param e 传入的event对象
     */
    stageMousemove(currentTool, e) {
      switch (currentTool) {
        case "rect":
          //绘画矩形中
          this.currentDrawingShape.setAttrs({
            width: e.evt.offsetX - this.pointStart[0],
            height: e.evt.offsetY - this.pointStart[1],
          });
          break;
        case "poly":
          //多边形初始化后,如果数组长度大于2,鼠标移动时,实时更新下一个点
          if (this.polygonPoints.length >= 2) {
            var x = e.evt.offsetX,
              y = e.evt.offsetY;
            var tempPoints = this.polygonPoints.concat();
            tempPoints.push(x);
            tempPoints.push(y);
            this.currentDrawingShape.setAttr("points", tempPoints);
          }
          break;
        default:
          break;
      }
      this.layer.draw();
    },
    /**
     * 鼠标在舞台上移动事件
     * @param currentTool 当前选择的工具
     * @param e 传入的event对象
     */
    stageMouseup(currentTool, e) {
      switch (currentTool) {
        case "rect":
          this.drawing = false;
          break;
        default:
          break;
      }
      this.layer.draw();
    },
    /**
     * 增加多边形顶点
     * @param e 传入的event对象
     */
    addPoint(e) {
      if (this.polygonPoints.length == 0) {
        var vc_this = this;
        //将第一个点标红,并显示
        return this.drawCircle(e.evt.offsetX, e.evt.offsetY)
          .setAttrs({
            fill: "red",
          })
          .show()
          .on("mousedown", (e) => {
            //点击第一个红点,绘画多边形结束
            //绘画多边形
            this.currentDrawingShape.setAttr("points", this.polygonPoints);
            //结束绘画多边形封闭
            //  this.currentDrawingShape.setAttr('closed', true);
            vc_this.drawing = false;
            vc_this.polygonPoints = [];
            //隐藏所有顶点
            vc_this.stage.find("Circle").forEach((element) => {
              element.hide();
            });
            //所有顶点变为白色
            vc_this.stage.find("Circle").forEach((element) => {
              element.setAttrs({
                fill: "#ffffff",
              });
            });
            //把现在的绘画对象更改为点和多边形合成的组
            this.currentDrawingShape = this.currentDrawingShape.getParent();
          });
      } else {
        //绘画点并显示
        return this.drawCircle(e.evt.offsetX, e.evt.offsetY).show();
      }
    },
  },
};
</script>


四,添加了其他功能和完善了代码。konva标注多边形矩形—demo2.0

https://blog.csdn.net/u011472784/article/details/126320562

源码下载

https://download.csdn.net/download/u011472784/85932065

Logo

前往低代码交流专区

更多推荐