如何编写复杂拖拽组件🐣

7,027

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第13天,点击查看活动详情

大家好,我是速冻鱼🐟,一条水系前端💦,喜欢花里胡哨💐,持续沙雕🌲,是隔壁寒草🌿的好兄弟。 欢迎小伙伴们加我微信:sudongyuer拉你进群,一起讨论,期待与大家共同成长🥂~

阅读本文🦀

1.您将了解到如何让echart做到响应式

2.您将到如何编写复杂的拖拽组件

3.和我一起实现可拖拽组件的增删改查、可编辑、可以拖拽、可排序、可持久化

4.和我一起实现可拖拽组件的删除抖动动画

前言🌵

在业务中得到一个很复杂的需求,需要实现组件中展示ecahrts图表,并且图表可编辑,可排序,大小可调整,还要可持续化,下面就是解决方案啦

正文 🦁

先看效果再一步步实现

技术调研

如何做到可拖拽?自己造轮子?显然不是,当然是站在巨人的肩膀上😁

  1. react-dnd
  2. react-beautiful-dnd
  3. dnd-kit
  4. react-sortable-hoc
  5. react-grid-layout
  • react-dnd
    • 文档齐全
    • github star星数16.4k
    • 维护更新良好,最近一月内有更新维护
    • 学习成本较高
    • 功能中等
    • 移动端兼容情况,良好
    • 示例数量中等
    • 概念较多,使用复杂
    • 组件间能解耦
  • react-beautiful-dnd
    • 文档齐全
    • github star星数24.8k
    • 维护更新良好,最近三月内有更新维护
    • 学习成本较高
    • 使用易度中等
    • 功能丰富
    • 移动端兼容情况,优秀
    • 示例数量丰富
    • 是为垂直和水平列表专门构建的更高级别的抽象,没有提供 react-dnd 提供的广泛功能
    • 外观漂亮,可访问性好,物理感知让人感觉更真实的在移动物体
    • 开发理念上是拖拽,不支持copy/clone
  • dnd-kit
    • 文档齐全
    • github star星数2.8k
    • 维护更新良好,最近一月内有更新维护
    • 学习成本中等
    • 使用易度中等
    • 功能中等
    • 移动端兼容情况,中等
    • 示例数量丰富
    • 未看到copy/clone
  • react-sortable-hoc
    • 文档较少
    • github star星数9.5k
    • 维护更新良好,最近三月内有更新维护
    • 学习成本较低
    • 使用易度较低
    • 功能简单
    • 移动端兼容情况,中等
    • 示例数量中等
    • 不支持拖拽到另一个容器中
    • 未看到copy/clone
    • 主要集中于排序功能,其余拖拽功能不丰富
  • react-grid-layout
    • 文档较少
    • github star 星星15.8k
    • 维护更新比较好,近三个月有更新维护
    • 学习成本比较高
    • 功能复杂
    • 支持拖拽、放大缩小

总结:为了实现我们想要的功能,最终选择react-grid-layout,应为我们想要的就是在网格中实现拖拽、放大缩小、排序等功能

Coding🔥

由于代码量比较大,只讲述一些核心的code

1.先创建基础布局

  • isDraggable 控制是否可拖拽
  • isResizable 控制是否可放大缩小
  • rowHeight控制基础行高
  • layout控制当前gird画布中每个元素的排列顺序
  • onLayoutChange 当布局发生改变后的回调函数
  <ReactGridLayout
      isDraggable={edit}
      isResizable={edit}
      rowHeight={250}
      layout={transformLayouts}
      onLayoutChange={onLayoutChange}
      cols={COLS}
    >
      {layouts && layouts.map((layout, i) => {
        if (!chartList?.some(chartId => chartId === layout.i))
          return null

        return (<div
            key={layout.i}
            data-grid={layout}
            css={css`width: 100%;
              height: 100%`}
          >
            <Chart
              setSpinning={setSpinning}
              updateChartList={updateChartList}
              edit={edit}
              key={layout.i}
              chartList={chartList}
              chartId={Number(layout.i)}
              scenarioId={scenarioId}/>
          </div>
        )
      })}
    </ReactGridLayout>

2.如何让grid中的每个echarts图表随着外层item的放大缩小而改变

    const resizeObserver = new ResizeObserver((entries) => {
      myChart?.resize()//当dom发生大小改变就重置echart大小
    })
    resizeObserver.observe(chartContainer.current)//通过resizeObserver观察echart对应的item实例对象

3.如何实现排序的持久化

//通过一下代码可以实现记录edit变量的前后状态
const [edit, setEdit] = useState(false) 
const prevEdit = useRef(false)
  useEffect(() => {
    prevEdit.current = edit
  })
 //通过将grid中的每个item的排序位置记录为对象,然后对每个属性进行前后的对比,如果没有改变就不进行任何操作,如果发生了改变就可以
//通过网络IO更新grid中item的位置
useEffect(() => {
    if (prevEdit && !edit) {
      // 对比前后的layout做diff 判断是否需要更新位置
      const diffResult = layouts?.every((layout) => {
        const changedLayout = changedLayouts.find((changedLayout) => {
          // eslint-disable-next-line eqeqeq
          return changedLayout.i == layout.i
        })
        return changedLayout?.w === layout.w
          && changedLayout?.h === layout.h
          && changedLayout?.x === layout.x
          && changedLayout?.y === layout.y
      })
      // diffResult为false 证明发生了改变
      if (!diffResult) {
				//这里就可以做图表发生改变后的操作
       //xxxxx
    }
  }, [edit])

4.如何实现编辑时的抖动动画

.wobble-hor-bottom{
    animation:wobble-hor-bottom infinite 1.5s ;
}

@-webkit-keyframes wobble-hor-bottom {
    0%,
    100% {
        -webkit-transform: translateX(0%);
        transform: translateX(0%);
        -webkit-transform-origin: 50% 50%;
        transform-origin: 50% 50%;
    }
    15% {
        -webkit-transform: translateX(-10px) rotate(-1deg);
        transform: translateX(-10px) rotate(-1deg);
    }
    30% {
        -webkit-transform: translateX(5px) rotate(1deg);
        transform: translateX(5px) rotate(1deg);
    }
    45% {
        -webkit-transform: translateX(-5px) rotate(-0.6deg);
        transform: translateX(-5px) rotate(-0.6deg);
    }
    60% {
        -webkit-transform: translateX(3px) rotate(0.4deg);
        transform: translateX(3px) rotate(0.4deg);
    }
    75% {
        -webkit-transform: translateX(-2px) rotate(-0.2deg);
        transform: translateX(-2px) rotate(-0.2deg);
    }
}

总结 🍁

本文大致讲解了下如何使用react-grid-layout如何与echart图表结合使用,来完成复杂的拖拽、排序、等功能,但是这个组件实现细节还有很多,本文只能提供一个大值的思路,还是希望能够帮助到大家,给大家提供一个思路,欢迎留言和我讨论,如果你有什么更好的办法实现类似的功能

结束语 🌞

那么我的如何编写复杂拖拽组件🐣就结束了,文章的目的其实很简单,就是对日常工作的总结和输出,输出一些觉得对大家有用的东西,菜不菜不重要,但是热爱🔥,希望大家能够喜欢我的文章,我真的很用心在写,也希望通过文章认识更多志同道合的朋友,如果你也喜欢折腾,欢迎加我好友,一起沙雕,一起进步

github🤖:sudongyu

个人博客👨‍💻:速冻鱼blog

vx👦:sudongyuer

写在最后

伙伴们,如果喜欢我的口水话给🐟🐟点一个赞👍或者关注➕都是对我最大的支持。