本篇主要是 dnd-kit 官方文档 中 Draggable 的对照翻译
翻译主要使用 DeepL 翻译,再根据自己的理解进行修改,如有错误,欢迎指正。 部分名词不会翻译。
Draggable
使用 useDraggable
将 DOM 节点变成可拖动的源,可以被拾起、移动和拖放在 dropped 容器上。
Usage
useDraggable
对你的应用程序结构没有特殊的要求。
Node ref
但至少,你需要将 useDraggable
返回的 setNodeRef
函数传递给一个 DOM 元素,这样它就可以访问底层的 DOM 节点并保持跟踪,以检测与其他 droppable 元素的碰撞和交叉。
import { useDraggable } from '@dnd-kit/core'
import { CSS } from '@dnd-kit/utilities'
function Draggable() {
const { attributes, listeners, setNodeRef, transform } = useDraggable({
id: 'unique-id',
})
const style = {
transform: CSS.Translate.toString(transform),
}
return (
<button ref={setNodeRef} style={style} {...listeners} {...attributes}>
/* Render whatever you like within */
</button>
)
}
始终尝试在你的应用程序的上下文中使用最有语义semantic的 DOM 元素。请查看我们的无障碍指南Accessibility guide,了解更多关于如何帮助为屏幕阅读器提供更好的体验。
Identifier
id
参数是一个字符串,是一个唯一的标识符,这意味着在一个给定的 DndContext
provider 中不应该有其他 draggable 元素共享该标识符。
Listeners
useDraggable
要求你将 listeners
附加到你想成为开始拖动的激活器的 DOM 节点。
While we could have attached these listeners manually to the node provided to setNodeRef
, there are actually a number of key advantages to forcing the consumer to manually attach the listeners.
我们需要将这些 listeners 手动附加到 setNodeRef
的节点上,这可以提供一些关键的优势。
Flexibility 灵活性
许多拖放库都有 "拖动手柄 "的概念,但使用 useDraggable
创建拖动手柄非常简单,只需手动将 listeners 添加到与 draggable 源 DOM 节点不同的元素上即可。
import { useDraggable } from '@dnd-kit/core'
function Draggable() {
const { attributes, listeners, setNodeRef } = useDraggable({
id: 'unique-id',
})
return (
<div ref={setNodeRef}>
/* Some other content that does not activate dragging */
<button {...listeners} {...attributes}>
Drag handle
</button>
</div>
)
}
当把
listeners
附加到与 draggable 节点不同的元素上时,请确保你也把attributes
附加到存有listeners
的同一个节点上,这样它仍然可以被访问。
如果你需要,你甚至可以在应用中存在有多个拖动手柄
import { useDraggable } from '@dnd-kit/core'
function Draggable() {
const { attributes, listeners, setNodeRef } = useDraggable({
id: 'unique-id',
})
return (
<div ref={setNodeRef}>
<button {...listeners} {...attributes}>
Drag handle 1
</button>
/* Some other content that does not activate dragging */
<button {...listeners} {...attributes}>
Drag handle 2
</button>
</div>
)
}
Performance 性能
这个策略也意味着我们能够使用 React 的合成事件,这比手动附加 listeners 到每个单独节点的性能要高。
为什么呢?因为 React 不需要为每个 draggable 节点附加单独的事件监听器,而是为我们在 document
上监听的每种类型的事件附加一个事件监听器。一旦发生对 draggable 节点的点击,React 在 document
上的 listeners 就派发一个 SyntheticEvent 回给原始处理程序。
Transforms
为了真正看到 draggable 元素在屏幕上移动,你需要用 CSS 来移动它。你可以使用内联样式、CSS 变量,甚至是 CSS-in-JS 库,将 transform
属性作为 CSS 传递给你的 draggable 元素。
出于性能方面的考虑,我们强烈建议您使用
transform
属性在屏幕上移动您的 draggable 元素,因为其他位置属性(如 top、left 或 margin)会导致昂贵的重绘。 了解更多关于 CSS transforms 的信息。
After an item starts being dragged, the transform
property will be populated with the translate coordinates you'll need to move the item on the screen. The transform
object adheres to the following shape: {x: number, y: number, scaleX: number, scaleY: number}
在一个项目被拖动后,transform
属性将被填充为你在屏幕上移动项目的平移坐标。 transform
对象遵守以下类型
{x: number, y: number, scaleX: number, scaleY: number}
The x
and y
coordinates represent the delta from the point of origin of your draggable element since it started being dragged.
x
和 y
坐标代表从你的 draggable 元素的原点开始被拖动时的 delta。
scaleX
和 scaleY
属性表示 draggable 项目与它当前所在的 droppable 容器之间的比例差异。这对于构建 draggable 项目需要适应其当前所在的 droppable 容器的大小的界面非常有用。
CSS
helper 完全是可选的;它是生成 CSS transform
字符串的辅助方法,相当于手动构建这样的字符串。
CSS.Translate.toString(transform) === `translate3d(${translate.x}, ${translate.y}, 0)`
Attributes
useDraggable
为 draggable 项目提供了一组合理的默认属性。我们建议你将这些属性附加到你要附加 listeners 的 draggable 元素上。
我们鼓励你手动附加对你的应用程序有意义的属性,而不是在不考虑是否有意义的情况下全部使用它们。
For example, if the HTML element you are attaching the useDraggable
listeners to is already a semantic button, although it's harmless to do so, there's no need to add the role="button"
attribute, since that is already the default role.
例如,如果您要附加 useDraggable
listeners 的 HTML 元素已经是一个语义按钮,但没有必要添加 role="button"
属性,因为这已经是默认。
In order for your draggable elements to receive keyboard focus, they need to have the tabindex
attribute set to 0
if they are not natively interactive elements (such as the HTML button
element). For this reason, the useDraggable
hook sets the tabindex="0"
attribute by default.
Attribute | Default value | Description |
---|---|---|
role | "button" | 如果可能的话,我们建议您为您计划附加可拖动 listeners 的 DOM 元素使用一个语义<button> 元素。如果这不可能,请确保包含 role="button" 属性,这是默认值。 |
tabIndex | "0" | 为了让你的可拖动元素获得键盘焦点,如果它们不是原生的交互式元素(如 HTML 按钮元素),就需要将 tabindex 属性设置为 0 。出于这个原因,useDraggable 默认设置 tabindex="0" 属性。 |
aria-roledescription | "draggable" | 虽然 draggable 是一个合理的默认值,但我们建议你根据你正在构建的用例来定制这个值。 |
aria-describedby | "DndContext-[uniqueId]" | 每个可拖动的项目都有一个独特的 aria-describedby ID,它指向当可拖动的项目收到焦点时要读出的屏幕阅读器指令。 |
要了解更多关于使可拖动界面无障碍的最佳做法,请阅读完整的无障碍性指南。
Recommendations 建议
touch-action
我们强烈建议你为所有的 draggable 元素指定 touch-action
CSS 属性。
touch-action
CSS 属性 touch-action 用于设置触摸屏用户如何操纵元素的区域 (例如,浏览器内置的缩放功能)。
一般来说,我们建议你将 draggable 元素的 touch-action
属性设置为 none
,以防止移动设备上的滚动。
对于指针事件,没有办法从指针事件 listeners 中阻止浏览器在触摸设备上与可拖动元素交互时的默认行为。使用
touch-action: none;
是可靠地防止指针事件的滚动的唯一方法。此外,使用
touch-action: none;
是目前在 iOS Safari 中防止滚动的唯一可靠方法,适用于 Touch 和 Pointer 事件。
如果您的 draggable 项目是可滚动列表的一部分,我们建议您使用一个拖动手柄,并将拖动手柄的 touch-action
设置为 none
,这样列表的内容仍然可以滚动,但从拖动手柄启动拖动不会滚动页面。
一旦指 pointerdown
或 touchstart
事件被启动,对触摸动作值的任何改变都将被忽略。在指针或触摸事件启动后,以编程方式将元素的触摸动作值从 auto
改为 none
,只要该指针处于活动状态,就不会导致用户代理中止或抑制该事件的任何默认行为(有关详细信息,请参阅指针事件第 2 级规范)。
Drag Overlay 拖拽遮罩层
<DragOverlay>
组件提供了一种渲染 draggable 遮罩层的方法,该遮罩层从正常的文档流中移除,并相对于视口进行定位。
To learn more about how to use drag overlays, read the in-depth guide:
要了解更多关于如何使用拖动覆盖物的信息,请阅读深入指南。