provider 的 animation(排序时的布局移动)、dropAnimation(浮层落位)和 animateChanges(非拖拽引起的数据变化)接受 false、选项,或你自己的函数——用于 GSAP 或 motion。列表和树可以覆盖 animation 和 animateChanges;可拖拽元素、项和列表可以带上自己的 dropAnimation,放置目标则可以带上 overDropAnimation。参见动画。
<DnDProvider :animation="{ duration: 180, easing: 'ease-out' }" :drop-animation="false" />
下面的每条泳道都是一个独立的 provider,它的 animation、dropAnimation 和 animateChanges(后者在 move 中接受函数)都使用同一种取值形式。
正在加载演示…
<script setup lang="ts">
import { ref } from 'vue';
import { DnDProvider, type AutoAnimation, type DropAnimation, type LayoutAnimation } from '@vue-dnd-kit/core';
import AnimLane from './AnimLane.vue';
import { tiltDrop, tiltMove } from './tilt';
const SLOW = { duration: 700, easing: 'ease-in-out' };
// One shape per lane, for all three options: sorting, the drop, and changes no drag made.
const lanes: { label: string; animation: LayoutAnimation; dropAnimation: DropAnimation; animateChanges: AutoAnimation }[] = [
{ label: 'true', animation: true, dropAnimation: true, animateChanges: true },
{ label: 'false', animation: false, dropAnimation: false, animateChanges: false },
{ label: '{ duration: 700 }', animation: SLOW, dropAnimation: SLOW, animateChanges: SLOW },
// `animateChanges` takes no function itself: its parts (`move`, `enter`, `leave`) do.
{ label: 'ƒ tilt', animation: tiltMove, dropAnimation: tiltDrop, animateChanges: { move: tiltMove } },
];
const letters = ['A', 'B', 'C', 'D'];
const rows = ref(lanes.map(() => letters.map((id, i) => ({ id, hue: 150 + i * 40 }))));
/** The same new order in every lane (Fisher–Yates: every order as likely): `animateChanges` plays it four ways. */
const shuffle = () => {
const order = [...letters];
for (let i = order.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[order[i], order[j]] = [order[j]!, order[i]!];
}
rows.value = rows.value.map((lane) => order.map((id) => lane.find((row) => row.id === id)!));
};
</script>
<template>
<div class="demo">
<code><DnDProvider :animation="shape" :drop-animation="shape" :animate-changes="shape"></code>
<div class="lanes">
<!-- A provider per lane: each one has its own animations. -->
<DnDProvider
v-for="(lane, index) in lanes"
:key="lane.label"
:animation="lane.animation"
:drop-animation="lane.dropAnimation"
:animate-changes="lane.animateChanges"
>
<AnimLane v-model="rows[index]!" :label="lane.label" />
</DnDProvider>
</div>
<button type="button" @click="shuffle">Shuffle all</button>
</div>
</template>
<style scoped>
.demo {
display: flex;
flex-direction: column;
align-items: center;
gap: 14px;
}
code {
max-width: 100%;
font: 11px/1.4 ui-monospace, Menlo, Consolas, monospace;
text-align: center;
opacity: 0.7;
}
button {
padding: 6px 12px;
border: 0;
border-radius: 8px;
background: #42b883;
color: #fff;
font: 600 12px/1 system-ui, sans-serif;
cursor: pointer;
}
button:focus-visible {
outline: 2px solid currentColor;
outline-offset: 2px;
}
.lanes {
display: grid;
grid-template-columns: repeat(4, 128px);
gap: 8px;
}
@media (max-width: 560px) {
.lanes {
grid-template-columns: repeat(2, 128px);
}
}
</style>选项
动画的时间设置。省略的部分保留该动画的默认值。
排序
项如何移动到新位置:true(默认时间)、选项、false(直接跳过去),或一个函数——它为一个项执行从 from 到 to 的动画,并返回它的 Animation、一个在动画结束时完成的 Promise,或者不返回任何东西。
type LayoutAnimation = | boolean | AnimationOptions | ((context: LayoutAnimationContext) => Animation | Promise<unknown> | void)布局动画函数收到的参数,针对一个移动到新位置的项。
Key项的 key。
HTMLElement项的元素,已经在新位置上。
Rect变化之前的视觉矩形(正在运行的动画显示该项的位置)。
Rect变化之后的布局矩形。
Required<AnimationOptions>选项给出的时间设置,已填入默认值。
'drag' | 'change'是什么移动了这个项:一次拖拽('drag':排序、放置、取消),或者非拖拽引起的数据变化('change',需开启列表的 animateChanges)。
AbortSignal库中断动画时中止:项在动画结束前再次移动(下一个动画从它当前显示的位置开始),或者动画被关闭。返回的 Animation 会自动帮你取消。其他任何东西都要在这里停止,并立即撤销它设置的样式:紧接着就会测量布局。
boolean设置了 prefers-reduced-motion: reduce。此时库自己的动画不会运行;你的函数总会运行,并据此做出决定(跳过,或保留一个淡入淡出)。
放置
浮层在放置时如何落位、在取消时如何飞回原处:true(默认时间)、选项、false(不飞行:它停在松开的地方),或者一个函数:把 overlay 从 from 动画到 to,并返回它的 Animation、一个在动画结束时完成的 promise,或者什么都不返回。
type DropAnimation = | boolean | AnimationOptions | ((context: DropAnimationContext) => Animation | Promise<unknown> | void)放置动画函数收到的参数:浮层、它现在的位置和它将落到的位置。
HTMLElement浮层盒子;它会落到当前项的位置上。在多项拖拽中,随它一起移动的其他项的克隆(默认浮层中的克隆、自定义浮层中的 DragPreview)会自行落到各自的位置上(如果这是一个自定义函数,则使用默认时间)。
Rect拖拽结束时浮层所在的位置。
Rect | null被放下的项的最终矩形;当它没有被渲染或不可见时为 null(浮层淡出)。
Rect | null放到一个区域(而不是可排序列表)上之后,该项不再显示,例如它被放进了另一个项(文件夹)或回收区:此时为该区域的可见矩形。默认动画会让浮层在淡出时缩进这个区域,而不是在原地淡出。
'drop' | 'cancel''drop':浮层落到放置把项放到的地方。'cancel':它飞回项原来所在的地方。
Active结束的那次拖拽。
Over | null放置落到的目标(列表、放置区);取消时为 null。
Required<AnimationOptions>选项给出的时间设置,已填入默认值。
boolean设置了 prefers-reduced-motion: reduce。此时库自己的动画不会运行;你的函数总会运行,并据此做出决定(跳过,或保留一个淡入淡出)。
数据变化
为非拖拽引起的列表(或树)项的变化添加动画:项被添加、删除、移动(排序、打乱、在同一分组的列表之间移动)或过滤。默认关闭(false);true 会以默认时间为移动、进入和离开的项添加动画。拖拽保留它们自己的动画(animation、dropAnimation)。在 prefers-reduced-motion: reduce 下,默认动画不会运行;你的函数仍然会运行(并收到 reducedMotion: true)。只有可见区域附近的项才会执行动画(虚拟列表中未挂载的行会被跳过)。
type AutoAnimation = boolean | AutoAnimationOptions分部分设置的 animateChanges:duration 和 easing 适用于所有部分;每种变化可以单独设为 true、选项(覆盖共享选项)、false,或你自己的函数。
true、选项、false(项直接出现),或一个为它执行进入动画的函数。
type EnterAnimation = | boolean | AnimationOptions | ((context: EnterAnimationContext) => Animation | Promise<unknown> | void)在开启了 animateChanges 的列表中出现的项:新添加的,或重新显示的(过滤、展开的树节点)。
Key项的 key。
HTMLElement该项的元素,已经位于它的位置上。
Rect它出现的位置(布局矩形)。
Required<AnimationOptions>选项给出的时间设置,已填入默认值。
boolean设置了 prefers-reduced-motion: reduce。此时库自己的动画不会运行;你的函数总会运行,并据此做出决定(跳过,或保留一个淡入淡出)。
true、选项、false(项直接消失),或一个为留在原处的副本执行离开动画的函数:返回它的 Animation 或一个 Promise,副本会在动画结束时被移除(函数什么都不返回时立即移除)。
type LeaveAnimation = | boolean | AnimationOptions | ((context: LeaveAnimationContext) => Animation | Promise<unknown> | void)从开启了 animateChanges 的列表中离开的项:被删除的,或被隐藏的(过滤、折叠的树节点)。
Key项的 key。
HTMLElement该项元素的副本,显示在该项原来的位置(项本身已经不在了):在原来的父元素中绝对定位,忽略指针,并带有 data-dnd-leaving 标记。你的动画结束后,库会移除它。
Rect该项原来所在的位置。
Required<AnimationOptions>选项给出的时间设置,已填入默认值。
boolean设置了 prefers-reduced-motion: reduce。此时库自己的动画不会运行;你的函数总会运行,并据此做出决定(跳过,或保留一个淡入淡出)。