如何结合antd开发一个svg伸缩甘特图Gantt(一)超详细!!

5,271 阅读5分钟

前言

团队需要基于Jira Issue做一些甘特图,方便进行工作排期,工作统计等。上网搜了一波感觉都不太符合预期,因此决定结合antd自己开发一个gantt。

废话不多说,先来看看效果:

total.gif

甘特图有以下几个优点:

  1. 右侧甘特图是根据时间用svg绘画,没有依赖其他工具,可以自定义更加丰富的功能;
  2. 依赖antd,可以用antd 组件自定义更丰富功能,如气泡组件,form表单,tree组件;
  3. 用原生css,支持左右缩放

总的来说,因为主要功能没有依赖其他工具,可塑性强~可以用js实现更加丰富的功能~

背景

正式开始之前先介绍一下frappe-gantt, frappe-gantt是纯js绘制甘特图的开源项目。本次的甘特图绘制便是参考了该开源项目进行开发的,感兴趣的同学可以继续往下看,阅读完这遍文章除了可以从零学习开发甘特图,还可以对css有更深刻的了解。

布局

布局分析

1.png 甘特图分成左右两块,左侧展示数据,右侧排期可视化

左右两边的内部又分别分成上下两块,现需要:

  1. 【A】,【B】需要公用一个纵向滚动条
  2. 【a1】,【b1】各自吸顶不随滚动条移动
  3. 【B】拥有一个横向滚动条,【b1】,【b2】随横向滚动条向左右移动 效果如下:

2.gif

代码实现

首先1

【A】,【B】需要公用一个纵向滚动条,第一反应是flex布局,【A】固定宽度,【B】flex: 1

.App {
    display: flex;
    height: 800px;
    overflow: hidden;
    overflow-y: scroll;
}
.A {
    width: 300px;
    border-right: 1px solid #000;
}
.B {
    overflow-x: scroll;
    width: 0; // 注意!!这里必须设置width0,否则当B横向超出时,A无法正常展示宽度
    flex: 1;
}

接着来看看2

【a1】,【b1】各自吸顶不随滚动条移动,这里建议使用 postion:sticky

Sticky positioning is a hybrid of relative and fixed positioning. The element is treated as relative positioned until it crosses a specified threshold, at which point it is treated as fixed positioned.

设置了sticky的元素,在屏幕范围时该元素的位置并不受到定位影响(设置是top、left等属性无效),当该元素的位置将要移出偏移范围时,定位又会变成fixed,根据设置的left、top等属性成固定位置的效果。

因为后面b1要随横向滚动条滚动,假如使用position: absolute,则无法实现随滚动条滚动~来看看代码:

.a1,
.b1 {
    height: 60px;
    border-bottom: 1px solid #000;
    position: sticky;
    background-color: #fff;
    top: 0;
}

最后最重要的3

【B】拥有一个横向滚动条,【b1】,【b2】随横向滚动条向左右移动, 认真看完前面两点的同学可能说,直接给B增加overflow-x: scroll,不就可以了么,天真的我也是这样以为的😭😭

当给B加上overflow-x: scroll时,发现b1不吸顶🌚🌚🌚,再认真看看文档

父级元素不能有任何overflow:visible以外的overflow设置,否则没有粘滞效果。因为改变了滚动容器(即使没有出现滚动条)。

因此,为了能够实现这个布局,只能加入js的辅助【假如有更好的方法可以通过评论告诉我~毕竟能够通过css解决的尽量用css解决😭】

去掉之前的.container overflow-y: scroll,由js控制A,B的滚动。

  const leftRef = useRef<HTMLElement>()
  const rightRef = useRef<HTMLElement>()

  const scrollLock = useRef({
    isRightScroll: false,
    isLeftScroll: false,
  })
  
  useEffect(() => {
    // 监听右侧滚动,右侧滚动,左侧跟随滚动
    rightRef.current?.addEventListener('scroll', function (e) {
    // 当左侧不在滚动时
      if (!scrollLock.current.isLeftScroll) {
        scrollLock.current = {
          ...scrollLock.current,
          isRightScroll: true,
        }
        leftRef.current?.scroll({
          top: e.target?.scrollTop,
        })
      }
      scrollLock.current = {
        ...scrollLock.current,
        isLeftScroll: false,
      }
    })
    // 监听左侧滚动,左侧滚动右侧跟随滚动
    leftRef.current?.addEventListener('scroll', function (e: any) {
    // 当右侧不在滚动时
      if (!scrollLock.current.isRightScroll) {
        scrollLock.current = {
          ...scrollLock.current,
          isLeftScroll: true,
        }
        rightRef.current?.scroll({
          top: e.target?.scrollTop,
        })
      }
      scrollLock.current = {
        ...scrollLock.current,
        isRightScroll: false,
      }
    })
  }, [])

最终代码

CSS实现双屏拖拽

如何纯CSS实现双屏拖拽呢?先来看看效果

3.gif

css实现拖拽效果,是通过resize: horizontal;属性, horizontal

如果一个block元素的 overflow 属性被设置成了visible,那么resize属性对该元素无效。

但是由于a2区域不需要滚动条,所以需要设置一个撑开a2且具有 overflow: scroll 的resize元素,真正的内容则放在一个position: absolute的元素中。

<div className="a2">
    <div className="resize" style={{ width: "500px" }}></div>
    <div className="realContent">a2</div>
</div>

.resize::-webkit-scrollbar {
    width: 10px;
    height: inherit;
}

.resize {
    position: sticky;
    top: 0;
    min-width: 280px;
    max-width: 900px;
    height: 900px;
    overflow: scroll;
    background: red;
    opacity: 0;
    resize: horizontal;
}

.realContent {
    position: absolute;
    top: 0;
    left: 0;
    width: calc(100% - 10px);
    margin: 60px 10px 0 0;
    z-index: -1;
}

.realContent::-webkit-scrollbar {
    width: 0 !important;
}

最终代码

大费周章的写完了布局如何实现,希望小伙伴能够都耐心的看完~毕竟在开发中为了研究如何用纯css实现布局,确实花了许多时间,~

左侧antd tree 实现按需加载

实现了上述的布局之后,后面除了甘特图的实现,其余的都可以使用antd组件自由发挥了

右侧的数据展示并没有使用antd table,因为想使用antd tree的展开异步加载 onLoadData,所以通过栅格布局将antd tree做成table的样式

图

假如没有异步加载子节点数据需求的同学,也可以直接用antd table组件实现该功能~

好奇如何将antd tree变成antd table请留言告诉我吧~

右侧svg绘画gantt将在下篇分享~

链接

antd-design

如何结合antd开发一个svg伸缩甘特图Gantt(二)超详细!!