重新思考 drag 和 drop

3,824 阅读8分钟
原文链接: www.zcfy.cc

原作者: Alex Reardon

我们今天来讲一些基础的,但是很漂亮的东西。

先介绍一下 react-beautiful-dnd

拖拽是一个直观地移动和重排方式。我们已经在 Atlassian 的官方仓库上发布了 react-beautiful-dnd,它能够使表单在在网页中的拖放变得更加漂亮、自然和容易。

更有物理感

react-beautiful-dnd的核心设计思想是“物理感”,我们想让用户在拖放的时候感觉在移动身边的物体。对比是最好的说明方式——我们来探讨一些标准拖放行为以及我们如何做的更好。

jquery-sortable

这个例子使用了 jquery-sortable ,非常棒。它的拖放机制是相当标准的,是一个很好的参照物。

移动

实时移动是标准

当拖动元素时,其他元素将根据需要消失并重新出现。并且当你放下元素时,它会立即出现在新的位置。

一种更自然的移动方式

在拖拽的时候我们给需要移出自己原先位置的元素添加了动画,以便=便更清楚地显示拖动效果,这是一种更自然的移动方式。我们也给元素的 drop 添加了动画,这样在移动到新位置时候也会有动效了。在任何时候一个元素的移动行为都不应该是瞬间的。

知道何时移动

拖放交互基于用户始于用户触发拖动的位置是显而易见的。

基于选择点的移动

在上面的例子中,用户按住了第一个元素的右上角。在第二个元素移动到新位置之前,用户需要正确地往下拖拽拖拽,因为计算是基于用户最初选择的位置。

基于重力中心的移动

react-beautiful-dnd 中,正在拖动中的元素对其他元素的影响是基于重力中心的,不管用户点击何处开始的拖拽。拖动中元素对其他元素的影响遵循一套类似尺度的规则。下面是一些遵循的规则考虑到自然拖动的一些经验,甚至考虑到元素灵活的高度。

  • 列表是拖拽经过的状态,当正在拖动的元素的中心点经过列表的边缘的时候。

  • 当正在拖动的元素的中心位置越过静止元素的边缘时,静止元素将移到拖动元素的路径外。换句话说:一旦一个元素 A 的中心位置越过另一个元素 B 的边缘,B就离开了。

易用性

传统的拖放交互完全是鼠标或触摸交互。react-beautiful-dnd 支持键盘即一切党拖拽交互。这使得一些高端用户能够随心所欲地使用他们操作键盘的经验,以及向以前被排除在外的用户开放这些经验。

尊重浏览器

除了支持键盘,我们还审查了键盘快捷键如何与标准浏览器键盘交互。当用户没有拖拽元素时,他们可以像平时那样使用键盘。拖动时,我们覆盖和禁用浏览器的某些快捷键盘(如“tab”键),以确保用户的流畅体验。

细致的动画设计

因为元素动来动去,用户很容易被这些动画分心。我们已经调整了各种动画,以确保提示、性能和交互性达到正确的平衡。

最大限度地提高交互性

react-beautiful-dnd 在尽力避免很多地方的非交互性下了很多功夫。用户应该感觉他们在控制界面,而不是等待动画完成才能继续与界面交互。

放置(dropping)

当你放置一个拖拽的元素的时候,它的动作是符合物理的(感谢 react-motion )。

从路径上移出

元素从正在拖拽的元素路径中移出使用 CSS 动画而不是物理变化,通过允许 GPU 处理运动来使性能最大化。设计 CSS 动画曲线是为了计算非拖拽元素从正在拖拽元素的移动路径上移出

这个动画是这样组成的:

  1. 一个预热期模仿自然的响应时间

  2. 一个小的阶段可以快速从路径上移开

  3. 一个长长的结束动画,这样人们就可以在动画的后半部分阅读任何正在被动画化的文字。

当移出路径时使用动画曲线

其他的也考虑得很好

我们已经做了很多工作来确保在提供灵活性的同时直观地工作。

慢点击和点击阻塞

当用户在某个元素上点击鼠标时,我们无法确定用户是否正在点击还是拖拽。而且,有时候当用户点击的时候他们可以非常轻地移动光标 ——这被称为慢点击。因此,只有鼠标被按下而且移动到一定距离(拖拽阈值)后,我们才开始拖拽事件,而不是用户只要一开始移动鼠标,有可能用户只是做了一次慢点击。如果拖拽没有超过阈值,那么用户的交互行为就被视为一次普通的点击。如果拖拽超过了阈值,那么交互将被视为拖拽,标准的单击事件不会发生。

这可以让用户把交互的元素包起来,比如锚点,让它不仅仅是一个标准的锚点,也是一个可拖拽的元素。

焦点管理

react-beautiful-dnd 在确保不会影响正常的 tab 文档流方面也下了很多功夫。比如,如果你包了一个锚点标签,然后用户将仍然能够通过 tab 键定位到锚点,而不是定位到包裹着锚点的元素。我们添加了一个tab 索引 给拖拽元素,确保即使没有用常见的交互式的东西(比如“div”)包裹,用户仍然可以通过键盘来拖动它。

不是所有人都能用

现在外面有很多 React 拖放交互的库,其中最值得注意的是优秀的 react-dnd 。它提供了一组非常好的拖放原子操作,尤其是与 wildly inconsistent HTML5 的拖放特性配合得非常好,真是难以置信的工作。react-beautiful-dnd 是专为垂直和水平列表构建的高级抽象。在功能的子集内,react-beautiful-dnd 提供了强大,自然和漂亮的拖放体验。然而,它并不像 react-dnd 提供了很多的功能。所以这个库可能不适合你,这取决于你的用例是什么。

工程化

干净、强大的 API

在这个库发布很久以前,人们就期望能有一个声明式的、干净的、强大的 API。它应该很容易上手,并在整个拖放体验上提供正确的控制水准。它是基于对其他库的大量研究以及在构建拖放产品方面的博采众长。

性能

react-beautiful-dnd 被设计成非常高效的,这是它的基因。它建立在对 React 性能的事先调查之上,你可以阅读 这篇文章 and 另一篇文章。它被设计成执行每个任务所需的渲染次数最少。

高亮

  • 使用带有记忆功能的连接组件来确保只有有需要渲染的组件才会被渲染,这要感谢 react-reduxreselectmemoize-one
  • 所有的拖拽动作都被 requestAnimationFrame 截获,感谢 raf-schd
  • 记忆功能被广泛使用,感谢 memoize-one
  • 有条件地禁止当拖拽正在进行时,其他所有可拖放元素的 鼠标事件 ,这样可以组织浏览器来做无用功。你可以阅读这篇技术文章 来了解更多。
  • 非主要的动画都在 GPU 上完成

最少的 React 更新次数

最少的浏览器绘图

可测试

react-beautiful-dnd 使用许多不同的测试策略,包括单元测试、集成测试和性能测试。测试系统的各个方面有助于提高系统的质量和稳定性。

代码覆盖率是 不保证代码健康的,这是一个很好的指标。这个代码库目前在至少有95% 的覆盖率

类型化

这个代码使用了 flowtype 类型检查器来进行类型化,这样可以促进更大的内部一致性和更具弹性的代码。它还提供了一个更庞大的开发人员文档故事,因为整个 API 都是类型化的。

最后的话

我们觉得 Web 将是一个更加美丽和容易的地方,因为有了 react-beautiful-dnd 。查看更多信息和示例可以访问我们的仓库

非常感谢 Atlassian 的每个人让这一切变得可能。

万岁!

更新 #1

react-beautiful-dnd 的支持已经让我们受宠若惊了!非常感谢大家。我们已经准备了很多 新特性 ,例如水平拖拽。

水平拖拽

已经有很多人希望提供对 touch 的支持(这需要 react-beautiful-dnd 能在移动设备和平板电脑上工作)。这个需求已经在我们的计划中了,而且我们正打算 立马着手这项工作!这个库还非常年轻!

感谢 Daniel Kerris