@angular/cdk/drag-drop
模块为你提供了一种方便易用、声明性地创建可拖放界面的方式,它支持自由拖动、在列表中排序、在列表之间转移条目、动画、触摸设备、自定义拖动把手、预览和占位符,以及水平列表和轴向锁定。
首先,将 DragDropModule
导入到要使用拖放功能的 NgModule
中。这样你就可以把 cdkDrag
指令添加到元素中,让它们变得可拖动了。当没有 cdkDropList
元素的时候,可拖动元素可以在页面中自由移动。你可以添加 cdkDropList
元素来约束元素可以被扔在哪里。
在一组 cdkDrag
元素外添加一个 cdkDropList
包装可以把这些可拖曳元素分组成一个可重新排序的集合。当元素移动时,这些条目会自动重新排序。注意,这不会更新你的数据模型,你可以监听 cdkDropListDropped
事件,以便在用户完成拖放操作后更新数据模型。
cdkDropList
指令支持在相互连接的拖放区之间转移要拖动的项。你可以把一个或多个 cdkDropList
实例连接起来,方法是设置 cdkDropListConnectedTo
属性,或把这些元素包含在带有 cdkDropListGroup
属性的元素中。
注意,cdkDropListConnectedTo
既可以直接引用其它拖放容器(cdkDropList
)的实例,也可以引用其它拖放容器的 id
:
<!-- This is valid -->
<div cdkDropList #listOne="cdkDropList" [cdkDropListConnectedTo]="[listTwo]"></div>
<div cdkDropList #listTwo="cdkDropList" [cdkDropListConnectedTo]="[listOne]"></div>
<!-- This is valid as well -->
<div cdkDropList id="list-one" [cdkDropListConnectedTo]="['list-two']"></div>
<div cdkDropList id="list-two" [cdkDropListConnectedTo]="['list-one']"></div>
如果有一个未知数量的连接拖放列表,你也可以使用 cdkDropListGroup
指令来自动建立连接。注意,添加到组下的所有新 cdkDropList
都会自动和其它的列表连接起来。
<div cdkDropListGroup>
<!-- All lists in here will be connected. -->
@for (list of lists; track list) {
<div cdkDropList></div>
}
</div>
你可以把通过设置 cdkDragData
或 cdkDropListData
来分别让 cdkDrag
和 cdkDropList
与任意数据关联起来。这两个指令触发的事件都包含这些数据,可以让你轻松地识别出拖放的来源。
@for (list of lists; track list) {
<div cdkDropList [cdkDropListData]="list" (cdkDropListDropped)="drop($event)">
@for (item of list; track item) {
<div cdkDrag [cdkDragData]="item"></div>
}
</div>
}
cdkDrag
和 cdkDropList
指令只包含使用此功能时必需的那些样式。然后,该应用可以通过那些由指令添加的 CSS 类来定制这些元素:
选择器 | 描述 |
---|---|
.cdk-drop-list |
对应于
cdkDropList 容器。 |
.cdk-drag |
对应于
cdkDrag 实例。 |
.cdk-drag-disabled |
添加到已禁用的
cdkDrag 的类。 |
.cdk-drag-handle |
添加到
cdkDragHandle 指令的宿主元素的类。 |
.cdk-drag-preview |
此元素将在用户拖动可排序列表中的条目时,渲染在用户光标旁边。默认情况下,此元素与正在拖动的元素完全相同。 |
.cdk-drag-placeholder |
此元素将在
cdkDropList 中拖动时,显示为真实元素的替代品。默认情况下,此元素将与正在排序的元素完全相同。 |
.cdk-drop-list-dragging |
当用户拖动条目时,添加到
cdkDropList 的类。 |
.cdk-drop-list-disabled |
当
cdkDropList 被禁用时,添加到
cdkDropList 的类。 |
.cdk-drop-list-receiving |
当
cdkDropList 可以接收正在连接的拖放列表中拖动的条目时,添加到
cdkDropList 的类。 |
拖放模块既支持对列表中的元素进行排序时的动画,也支持用户将其拖放到列表中最终位置时的动画。要设置动画,你就必须定义一个以包含 transform
属性的 transition
。动画中可以使用下列如下 CSS 类:
.cdk-drag
- 如果你把 transition
添加到该类中,当用户在列表中进行排序时,它就会播放动画。.cdk-drag-animating
- 当用户停止拖动时,该类就会添加到 cdkDrag
上。如果你给它添加了一个 transition
,那么 CDK 就会把该元素从它的拖放位置动画到其在 cdkDropList
容器内的最终位置。动画示例:
/* Animate items as they're being sorted. */
.cdk-drop-list-dragging .cdk-drag {
transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
}
/* Animate an item that has been dropped. */
.cdk-drag-animating {
transition: transform 300ms cubic-bezier(0, 0, 0.2, 1);
}
默认情况下,用户都可以拖动整个 cdkDrag
元素来移动它。如果要限制用户只能使用某个拖动把手元素,你可以把 cdkDragHandle
指令添加到 cdkDrag
内部的某个元素上。注意,你可以有任意多个 cdkDragHandle
元素:
当拾起 cdkDrag
元素时,它会在拖动过程中创建一个可见的预览元素。默认情况下,这将是位于用户光标旁边的原始元素的克隆体。但是,通过由 *cdkDragPreview
提供的自定义模板,可以自定义此预览。
使用此默认配置时,自定义预览不会匹配原始被拖动元素的大小,因为 CDK 不能对该元素的内容做任何假设。如果你希望匹配其大小,可以给输入参数 matchSize
传入 true
。
注意,克隆元素时会删除它的 id
属性,以免在页面中拥有多个具有相同 id
的元素。这会导致任何以此 id
为目标的 CSS 都不能应用在这个预览元素上。
默认情况下,cdkDrag
的预览器将插入到页面的 <body>
中,以避免出现 z-index
和 overflow: hidden
相关的问题。在某些情况下,这可能是无效的,因为此预览器不会保留其继承的样式。你可以使用 cdkDrag 上的 cdkDragPreviewContainer
输入来控制插入预览器的 cdkDrag
。可能的值为:
值 | 描述 | 优点 | 缺点 |
---|---|---|---|
global |
默认值。预览插入到
<body> 或最接近的影子根中。 |
预览不会受到
z-index 或
overflow: hidden 的影响。它也不会影响
:nth-child 选择器和 flex 布局。 |
不会保留继承的样式。 |
parent |
预览插入到正在拖动的条目的父元素中。 | 预览继承与拖动条目相同的样式。 | 预览可能会被
overflow: hidden 剪裁,或者由于
z-index 而被放置在其他元素下方。此外,它会影响
:nth-child 选择器和一些 flex 布局。 |
ElementRef 或
HTMLElement |
预览将插入到指定的元素中。 | 预览继承自指定容器元素的样式。 | 预览可能会被
overflow: hidden 剪裁,或者由于
z-index 而被放置在其他元素下方。此外,它会影响
:nth-child 选择器和一些 flex 布局。 |
在拖动 cdkDrag
元素的同时,CDK 会创建一个占位符元素,它会显示在要放置的位置上。默认的占位符是被拖元素的克隆体,但你可以使用 *cdkDragPlaceholder
指令来把它替换为自定义的版本:
cdkDropList
指令默认假设列表是垂直的。可以通过将
orientation
属性设置为 `"horizontal"` 来更改此设置。
如果想阻止用户把某个 cdkDrag
元素拖到另一个元素外部,你可以把一个 CSS 选择器传递给 cdkDragBoundary
属性。该属性的工作原理是接受一个选择器并查找该 DOM,直到找到一个与之匹配的元素。如果找到了匹配项,它就会用作该元素无法拖出的边界。当 cdkDrag
位于 cdkDropList
中时,也可以用 cdkDragBoundary
来达到相同效果。
默认情况下,cdkDrag
允许所有方向的自由移动。要想限定只能沿特定的轴移动,可以把 cdkDrag
上的 cdkDragLockAxis
或 cdkDropList
上的 lockAxis
设置为 "x"
或 "y"
。
如果你想要把某个元素做成可拖动的,但却无法直接访问它,你可以借助 cdkDragRootElement
属性。该属性的工作原理是接受一个选择器并查找 DOM,直到它找到一个与该选择器匹配的元素。如果找到了某个元素,它就会成为用户在拖动时所移动的替代元素。这对于让对话框可拖动之类的场景非常有用。
默认情况下,一个容器中的所有 cdkDrag
项都可以移动到另一个相连的容器中。如果你想对可拖放的条目进行更精细的控制,你可以使用 cdkDropListEnterPredicate
,它会在条目即将进入新容器时调用。根据它是返回的是 true
还是 false
,可以允许或不允许该条目进入新容器。
如果要对特定的条目禁用拖曳,你可以在 cdkDrag
条目上设置输入属性 cdkDragDisabled
。你还可以使用 cdkDropList
上的输入属性 cdkDropListDisabled
来禁用整个列表或 cdkDragHandle
上的 cdkDragHandleDisabled
来进行特定的拖动手柄。
某些情况下,可拖动的条目可以从一个列表拖到另一个列表中,但用户不应该在源列表中对它们进行排序。对于这些情况,你可以设置 cdkDropListSortingDisabled
输入,它会阻止 cdkDropList
保留被拖动条目在源列表中的初始位置(如果用户决定退回该条目)。
默认情况下,只要用户把指针放在 cdkDrag
,就会启动拖动序列。这种对于触摸设备上的全屏可拖动元素等情况可能并不理想,因为用户在滚动页面时可能会意外触发拖动。对于这类情况,你可以使用输入属性 cdkDragStartDelay
来延迟拖动,它会先等待用户按住指定的毫秒数之后才开始移动此元素。
默认情况下,独立的 cdkDrag
元素只有当用户手动移动时,才会从其常规 DOM 位置移开。通过输入属性 cdkDragFreeDragPosition
可以显式设置该元素的位置。例如,应用程序通常会这样做:当用户导航离开后,自动还原可拖动对象的位置,然后返回。
默认情况下,cdkDrag
的条目可以放到 cdkDropList
中的任意位置(序号)上。你可以通过设置 cdkDropListSortPredicate
来改变这种行为。每当一个条目要移到一个新的序号上时,就会调用一个谓词函数。如果该谓词返回 true
,则该项将被移入新序号,否则会保持当前位置。