美文网首页
基于react、svg和d3绘制流程图

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

作者: ChicAboo | 来源:发表于2021-01-12 09:21 被阅读0次

    前言

          因业务关系,新增绘制流程图需求,最开始想使用第三方库,调研后总有部分功能不能满足,考虑后期需求变更的情况,决定自己研发。经多番调研,确认使用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

    相关文章

      网友评论

          本文标题:基于react、svg和d3绘制流程图

          本文链接:https://www.haomeiwen.com/subject/rersaktx.html