美文网首页
G6开发树图时,通过虚拟列表自定义滚动表格节点

G6开发树图时,通过虚拟列表自定义滚动表格节点

作者: 凌晨四点打铁匠 | 来源:发表于2022-03-06 21:08 被阅读0次

    背景

    最近用Antv-g6做树图开发时,需要做滚动的表格,查了g6的文档,发现没有滚动,只能自己来做了。主要思路是,监听鼠标滚轮,判断当前鼠标是否在表格节点位置,然后不断截取数组,然后更新当前节点。

    实现

    首先,需要使用g6的自定义复合交互Behavior,去自定义实现wheel事件逻辑,同时在自定义节点中也要配合渲染对应的数据。主要实现代码如下:

    import G6 from '@antv/g6'
    /**
     * 判断坐标是否在对应的节点范围内
     * @param {object} point 需要判断的位置
     * @param {object} bbox 节点的几何信息
     */
    function isInBBox(point, bbox) {
      const { x, y } = point;
      const { minX, minY, maxX, maxY } = bbox;
      return x < maxX && x > minX && y < maxY && y > minY;
    }
    // 画布缩放比例
    let ratio = 1;
    // 行高
    const rowHeight = 50;
    // 一次最多显示的行数
    const rowCount = 10;
    // 自定义交互事件
    G6.registerBehavior('my-table-scroll', {
      getEvents() {
        return {
          // 事件映射
          wheel: 'scroll',
        }
      },
      // 自定义scroll事件,
      scroll: (ev) => {
        // 阻止默认事件,默认会缩放画布,
        // 由于自定义了wheel事件,所以缩放事件下面也需要自己实现
        ev.preventDefault();
        // 将屏幕/页面坐标转换为渲染坐标。
        const point = graph.getPointByClient(e.clientX, e.clientY);
        // 找到当前需要滚动位置的节点
        const nodes = graph.getNodes().filter((node) => {
          const bbox = node.getBBox();
          const group = node.getContainer();
          // 自定义节点时,给需要滚动的节点shape自定义了name='scroll-box'
          const shape = group.findAllByName('scroll-box')[0];
          if (!shape) return false;
          return isInBBox(point, bbox);
        })
        // 优化滚动性能
        requestAnimationFrame(() => {
          // 垂直位置滚动的距离
          const y = ev.deltaY || ev.movementY;
          // 如果在需要滚动的节点上
          if (nodes && nodes.length) {
            const node = nodes[0];
            // 拿到当前节点的数据
            const model = node.getModel();
            // 如果当前的数据较少,不需要滚动,这里的rowCount根据需求自定义
            if ((model.tablesData || []).length <= rowCount) return;
            // 开始截取的位置,获取数据中保存的截取位置,如果还没滚动过取0
            const index = model.startIndex || 0;
            // 计算出开始的索引数
            // y / rowHeight代表偏移行数,如y是2px,表示0.04行,方便自定义节点中使用
            let startIndex = index + y  / rowHeight; 
            if (startIndex < 0) startIndex = 0;
            // 可以取的最大maxIndex值
            const maxIndex = model.tablesData.length - rowCount;
            if (startIndex > maxIndex) startIndex = maxIndex;
            // 更新节点,将新的startIndex值合并到节点中,代替旧的
            graph.update(node, { startIndex });
            return;
          }
          
          // 如果不是在滚动节点,需要缩放画布,每次缩放变化的比例
          if (y > 0) {
            ratio -= 0.05;
          } else {
            ratio += 0.05
          }
          // 控制最大和最小的缩放比例
          if (ratio < 0.25) {
            ratio = 0.25
          } else if (ratio > 1.75) {
            ratio = 1.75;
          }
          // 四舍五入ratio
          ratio = Math.round(ratio * 100) / 100;
          // 将渲染坐标转换为 Canvas 画布坐标。
          const canvasPoint = graph.getCanvasByPoint(point.x, point.y);
          // 根据鼠标所在的canvas画布位置为中心进行缩放
          graph.zoomTo(ratio, canvasPoint)
        })
      }
    });
    
    // 自定义节点部分
    G6.registerNode('my-node-card', {
      draw: (cfg, group) => {
        // 省略其余节点的代码,主要看滚动节点的部分代码
        const { tablesData, startIndex = 0 } = cfg;
        // 需要渲染的数据
        let list = tablesData
        // 滚动偏移值
        let offsetY = 0;
        // 渲染的行数
        let len = tablesData.length;
        if (len > rowCount) {
          len = rowCount
          // 向下取整
          const index = Math.floor(startIndex);
          // 这里多取一行,是为了滚动时看起来比较连续
          // 当第一行还没完全滚出去时,最后面需要多一行
          list = list.slice(index, index + rowCount + 1);
          // 偏移值用索引的小数部分进行计算,同时需要取反
          offsetY = -(startIndex % 1) * rowHeight;
        }
        // 节点高度,这里节点高度需要多两行
        // 因为滚动的上下边界需要有遮挡条,
        const nodeHeight = (len + 2) * rowHeight
        // ...省略后续的节点渲染逻辑,不同的项目千差万别
        // 节点框的渲染位置不变
        // 里面滚动部分的位置根据数据和偏移值来确定,就可以达到滚动效果
      }
    })
    
    // 树图实例,具体配置省略
    const graph = new G6.TreeGraph({
      // 大部分配置省略
      modes: {
        default: ['my-table-scroll']
      },
      // 自定义节点
      defaultNode: {
        type: 'my-node-card'
      }
    })
    

    最后,如果追求完美,可以加上滚动条,这样看起来会更加直观。还可以加上拖动滚动条进行滚动等效果(待完善。。。)

    参考链接

    antv-g6官网

    相关文章

      网友评论

          本文标题:G6开发树图时,通过虚拟列表自定义滚动表格节点

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