基于react、svg和d3绘制流程图

2021-01-12  本文已影响0人  ChicAboo

前言

      因业务关系,新增绘制流程图需求,最开始想使用第三方库,调研后总有部分功能不能满足,考虑后期需求变更的情况,决定自己研发。经多番调研,确认使用svg和d3绘制流程图,并封装为npm包。本文主要讲解封装库的前期规划、部分实现方式以及里面涉及到的一些好的idea。这是封装好库react-data-flow,并在不断更新优化中,目前的功能按照公司的需求定制的,后期新增自定义功能。

rdf.gif

前期规划

首先确认需求,目前需要支持的需求有:

  1. 背景画布,canvas;
  2. 绘制节点;
  3. 节点间拖拽绘制边;
  4. 节点和节点间的连线规则;
  5. 节点选中效果;
  6. 节点选中后边的动画效果;
  7. 边上的显示文本及相关事件;
  8. 图谱居中、缩放功能;

暂时梳理的需求有上面8项,分析需求,除了节点拖拽绘制边及图谱的居中和缩放功能,使用svg比较容易就能实现。需要考虑的是节点的事件、边的事件以及图谱上的拖拽已经缩放事件。d3已经有了成熟的方法能实现,引入d3,包的体积比较大,很多功能不能使用,考虑上面的需求,只需引入d3-zoom和d3-selection就可满足。

到此可以确认使用的技术:

React、Canvas、SVG、d3-selection、d3-zoom

绘制画布

1610024667967.jpg

分解需求:

  1. 横线和纵线(点线),线与间隔[3, 3];
  2. 线与线间的间距为20;
  3. 绘制多条横线和纵线即可;
    需求已很明确,绘制多条横线和纵线组合起来,背景就完成了。下面的绘制背景的方法是使用Canvas绘制,不清楚Canvas绘制线条语法,点我

绘制线条封装:

   // canvas的ref
   const girdRef = useRef<HTMLCanvasElement>(null);
   const canvasWidth = 1920 * 2;
   const canvasHeight = 1080 * 2;
   const grid = {
        strokeColor: '#E2E2F0',
        strokeWidth: 1,
        distance: 20,
        isLineDash: true,
        lineDash: [3, 3],
      }

  const drawGridLine = (x1: number, y1: number, x2: number, y2: number) => {
      if (girdRef && girdRef.current) {
        const gridCanvas: any = girdRef.current.getContext('2d');
        const { strokeWidth, strokeColor, lineDash } = grid;

        gridCanvas.beginPath();
        gridCanvas.moveTo(x1, y1);
        gridCanvas.lineTo(x2, y2);
        gridCanvas.setLineDash(lineDash);
        gridCanvas.lineWidth = strokeWidth;
        gridCanvas.strokeStyle = strokeColor;
        gridCanvas.stroke();
      }
    }

线条绘制方法已经绘制好,只需要传入起点和终点左边即可;

接下来的工作,需要绘制多条线条。方法如下:

  const drawGrid = () => {
    const distance = grid.distance;
    const rowNumber = Math.ceil(canvasHeight / distance);
    const colNumber = Math.ceil(canvasWidth / distance);

    for (let i = 0; i < rowNumber; i++) {
      drawGridLine(0, i * distance, canvasWidth, i * distance);
    }

    for (let j = 0; j < colNumber; j++) {
      drawGridLine(j * distance, 0, j * distance, canvasHeight);
    }
  }

获取当前容器的宽高,除以间距,得到条数。到此,背景已绘制完成。

svg绘制节点

不清楚svg语法的点我,节点是一个矩形,需要故需要绘制矩形,svg绘制矩形根据左上角点的坐标和宽高绘制的,所以只需确定坐标即可。

代码如下:

    const position = {
      x: 300, y: 300
    }
   const rect = {
    strokeWidth: 1,
    strokeColor: '#2994FF',
    fill: '#FAFBFC',
    width: 180,
    height: 50,
    distance: 10,
    radius: 4,
    hover: {
      fill: '#E9F3FC',
    },
    delRadius: 10,
  }

   <g>
      <rect
        className="rectNode"
        x={position.x - width / 2}
        y={position.y - height / 2}
        rx={radius}
        ry={radius}
        width={width}
        height={height}
        stroke={strokeColor}
        fill={rectFill}
      />
      <text
        className="rectTextNode"
        id={`text_id_${node.id}`}
        x={position.x}
        y={position.y + rectText.marginTop}
        fill={rectText.fill}
        style={{ textAnchor: 'middle', fontSize: rectText.fontSize, userSelect: 'none' }}>
        {title}
      </text>
    </g>

只需要知道api,绘制图形挺简单,多数的操作都是细节的调整。

svg绘制边

语法:

命令 参数 说明
M m x y 移动画笔到制定坐标
L l x y 绘制一条到给定坐标的线
H h x 绘制一条到给定x坐标的横线
V v y 绘制一条到给定y坐标的垂线
A a rx ry x-axis-rotation large-arc sweep x y 圆弧曲线命令有7个参数,依次表示x方向半径、y方向半径、旋转角度、大圆标识、顺逆时针标识、目标点x、目标点y。大圆标识和顺逆时针以0和1表示。0表示小圆、逆时针
Q q x1 y1 x y 绘制一条从当前点到x,y控制点为x1,y1的二次贝塞尔曲线
T t x y 绘制一条从当前点到x,y的光滑二次贝塞尔曲线,控制点为前一个Q命令的控制点的中心对称点,如果没有前一条则已当前点为控制点。
C c x1 y1 x2 y2 x y 绘制一条从当前点到x,y控制点为x1,y1 x2,y2的三次贝塞尔曲线
S s x2 y2 x y 绘制一条从当前点到x,y的光滑三次贝塞尔曲线。第一个控制点为前一个C命令的第二个控制点的中心对称点,如果没有前一条曲线,则第一个控制点为当前的点。

代码:

<path
        d={` M 275, 231
                  L 315, 231
                  L 341.5, 231
                  L 341.5, 231
                  L 368, 231
                  L 408, 231`}
        strokeWidth="1"
        stroke="#ccc"
        fill="none"
        markerEnd="url(#arrow)"
      />

多节点和多边结合及相关事件的思路

上面简单介绍了绘制点和绘制线条的方式,点与边的结合确定坐标即可。

对于每个节点的事件而言,有两种方法,一种是通过d3-selection获取元素demo,从而操作demo,如下:

d3.select(svgElement).on('click', function(){})

第二种方法,React的方式,在元素上绑定onClick事件。如下:

<g onClick={handleClick}>
  <rect />
  <text />
</g>

对于拖拽事件而言,同样事两种方法,一种事d3提供的方法,推荐这种,如下:

d3.select(svgElement).call(d3.drag().on('drag', function() {}))

另外一种方式,在react元素上绑定拖拽事件,需要借助第三方库,如react-dndreact-draggable等库。

总结

本文只是简单介绍了如何通过canvas、svg、d3绘制简单的图形,如果详细讲解上面封装的库,内容太多,不知从和开始介绍。若有疑问或建议欢迎评论区留言。

参考文献

Canvas
SVG
d3

上一篇下一篇

猜你喜欢

热点阅读