canvas多边形变形拖拽(二)

515 阅读2分钟

前言

  canvas上期我简单描述了一下如何绘制多边形, 假如没有看过的朋友的可以点击传送门先 canvas自定义多边形, 距离上述的文章已经过去了一年多了, 本身也懒不想更新了, 但是最近有粉丝居然在催更, 那没有任何拒绝的理由了, 这篇幅主要是描述多边形各节点的拖拽

整体思路

  首先需要统计canvas画布上所有的多边形的path, 我对多边形描述的对象定义是这样的

let pointArr = [];

// 一个画布中所有的闭合路径
pointArr = [{
  status: 'draw',       // 当前对象状态, 但一个对象操作完status就会转换成display  /draw 连点 /stretch 拉伸/drag 拖动/display 展示
  coordinate: [{x: 100, y: 100}]  // 组成整个闭合区域的坐标点
  path2d: ''        // 由coordinate的坐标点组成的path2D对象, 具体区别画布上哪个闭合区域
  }]

  所有多边形的对象全部存储在pointArr数组中, 然后在onmousemove事件中获取鼠标的x, y坐标遍历pointArr中的path2d来确定具体是哪个多边形, 鼠标点击多边形同时status修改成stretch(拉伸)状态。

if (mode === "select" && pointArr.length > 0) {
    for (let i = 0; i < pointArr.length; i++) {
        const { path2d } = pointArr[i];
          if (ctx.isPointInPath(path2d, x, y)) {
             // console.log(`我是数组中第${i}个图形`);
             stretchIndex = i;
             pointArr[i] = { ...pointArr[i], status: "stretch" };
          }
     }
}

当多边形的status变成stretch, 则这个多边形的的所有角都会出现一个圆点, 当鼠标点击变成激活可拖拽状态 May-07-2023 20-43-20.gif 相关代码如下:

function stretchFunc() {
        const target = pointArr[currentIndex].coordinate;
        pointArr[currentIndex].coordinatePath2d = [];
        for (let k = 0; k < target.length; k++) {
          const path = new Path2D();
          const { x, y } = target[k];
          ctx.fillStyle = "#fff";
          ctx.beginPath();
          ctx.arc(x, y, 10, 0, 2 * Math.PI);   //在被选中的多边形
          path.arc(x, y, 10, 0, 2 * Math.PI);  //存储拖拽点路径
          if(pointTargetIndex === k) {
            ctx.fillStyle = "red";     // 激活状态的拖拽圆点会变成红色
          } else {
            ctx.fillStyle = "white";
          }
          
          ctx.fill();
          ctx.stroke();
          
          pointArr[currentIndex].coordinatePath2d[k] = path;
        }
      }

交互问题

其实canvas并不擅长于交互,我上述的操作其实也存在一定的问题, 包括一个点或者多个点切换拉伸以及拉伸完成的交互怎么设计暂时没有想好, 就比如这次的节点的拉伸, 明显可以看到我是先有一个选择的操作, 代码如下

if (mode === "select" && pointArr.length > 0) {
          for (let i = 0; i < pointArr.length; i++) {
            const { path2d } = pointArr[i];
            if (ctx.isPointInPath(path2d, x, y)) {
              // console.log(`我是数组中第${i}个图形`);
              stretchIndex = i;
              pointArr[i] = { ...pointArr[i], status: "stretch" };
            }
          }
}

在选择模式下先要选中才真正进入拉伸模式, 才能进行节点拉伸动作

if(mode === "select" ) {
     if(stretchIndex > -1){          // 选中图形
        mode = "stretch";            // 切换模式
        currentIndex = stretchIndex  // 选中图形的索引值      
     }
}

为什么这么设计, 选择是一种过渡的模式, 单独把它拎出来没有意义, 但是它是搭配其他操作比如拉伸,拖拽等的前置操作, 因为canvas和document的联系是有但真得挺麻烦, 最好的方式就是svg, canvas负责ui, svg负责交互

后记

其实这个我最初的设想是做一个在线的canvas画板, 在flutter中使用webrtc技术实现一个在线课堂跨端的一个完整系统, 这两期讲得只是其中很小一部分, 不过我之后会持续更新, 直到可以封装成一个库的地步

传送门

源码链接