17

为什么拖拽的 api 都很难用? - taowen - SegmentFault 思否

 4 years ago
source link: https://segmentfault.com/a/1190000023427610?
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

先来看达到了什么样的效果 https://github.com/mulcloud/s...

undo 操作

undo 操作

拖放到区域外自动回滚

drop outside

Framer Motion 的缺点

拖拽和动画是由 Framer Motion 实现的。Framer Motion 在表达动画方面毫无疑问相当牛逼,一点点的代码,就可以实现非常丰富的效果。比 react-spring 还要强!

Framer Motion 在拖拽方面要差点意思。这个是官方给的例子 https://codesandbox.io/s/framer-motion-2-drag-to-reorder-fc4rt 。 主要有两大缺点。

第一个缺点是缺少 drag over 事件。HTML5 的 Drag/drop api 是有两部分的,一部分是被 drag 的元素,一部分是 dragging over 的元素。而 framer motion 仅仅提供了被 drag 元素的 dragStart 和 dragEnd 两个事件。这个就很不符合直觉了,那我被 drag over 了,怎么显示我这里是可以被 drop 的呢?

第二个缺点是实现写得比较绕。第1步先用 ref 拿到了 DOM 元素。第2步,在 useEffect 里采集每个 DOM 元素的位置,复制到另外一个名字叫 positions 的 useRef 上。第3步,假定一个 DOM 元素的数组就是界面上要排序的元素,进行元素位置互换。这相当于是把一部分的 DOM 状态做了一个完整的拷贝,复制到了 react state 里。

为什么 react 拖拽库的 api 都不好用呢?

react dnd 也很难用 https://react-dnd.github.io/react-dnd/about

为啥这些 api 都不做得好用一点呢?我分析有以下原因

  • react 的理念是 ui 由 state 渲染而来。但是拖拽的时候是找不到对应的 react state 的。
  • 直接操作 dom 的拖拽库,例如 https://github.com/SortableJS/Sortable 直接把 DOM 当 state 操作。但是这个和 react 渲染的界面又会冲突。需要做一些 hack 的工作才能让这样模式的拖拽库和 react 配合起来。
  • 跨列表拖拽等需求经常需要在多个组件之间移动元素。这个时候是需要全局有 store 的,而不是每个组件各管各的状态。react 社区没有统一的全局 store,所以各个拖拽库只好让你来自己做适配了。
  • 拖拽过程的 placeholder/drop zone/辅助线 等各种交互效果都是有很重的业务逻辑的。这些业务逻辑严重依赖于 xy 坐标进行计算。但是 react 的代码里比较难拿到 DOM 元素,以及 xy 坐标。需要 ref 和 useEffect 这样做一份复制。

直接访问 DOM 就好了嘛

不能理解为啥用了 react,就一定要啥都用 react。毕竟 Web DOM 的 api 还在那里啊,为啥不用呢?拖拽过程中计算元素位置,最方便最直观的,当然是直接用 DOM 的 api 取得鼠标下面的元素,然后访问 offsetLeft / offsetHeight 等元素属性进行计算嘛。Framer Motion 的例子里复制一份有什么必要呢。如果有多个列表要拖拽,难道每个列表都要平行维护一份 DOM 的副本状态在 react state 中么?这也太原教旨主义了。

完全托管状态

这个想法就是用一个“状态管理”工具把所有的组件状态都管理起来。这样就能很方便的实现 undo/redo 了。当然你会说,redux 就是这样的状态管理工具。

而且我们希望是多 store 的状态管理,对应到每个可被 draggable 和 droppable 的 DOM 都有一个 store。这个用 redux 就不那么直接了。为什么有多 store 的需求呢?因为前面直接访问 DOM 做了排版上的计算,然后第二步就是要更新 state 触发重渲染了。我们需要从 DOM 元素找到对应的 store,dispatch action 更新这个 store。一个直观的做法就是在 DOM 元素上加上 data-model-class 和 data-model-id 的属性,拿这两个属性去找到对应的 store。

最终实现的代码在这里 https://github.com/mulcloud/state-management-demo/tree/master/src/Scenario6

工作地点:北京 / 杭州

非常有创新性和成长的工作内容 https://www.zhipin.com/job_detail/?query=%E4%B9%98%E6%B3%95%E4%BA%91&city=101010100&industry=&position=


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK