Svelte 过渡引擎的一个特别强大的功能是能够推迟过渡,以便它们可以在多个元素之间协调。
¥A particularly powerful feature of Svelte’s transition engine is the ability to defer transitions, so that they can be coordinated between multiple elements.
以这对待办事项列表为例,切换待办事项会将其发送到相反的列表。在现实世界中,对象的行为并非如此 - 它们不会消失并重新出现在另一个地方,而是会经过一系列中间位置。使用动作可以大大帮助用户了解应用中发生的事情。
¥Take this pair of todo lists, in which toggling a todo sends it to the opposite list. In the real world, objects don’t behave like that — instead of disappearing and reappearing in another place, they move through a series of intermediate positions. Using motion can go a long way towards helping users understand what’s happening in your app.
我们可以使用 crossfade
函数实现此效果,如 transition.js
中所示,它创建了一对称为 send
和 receive
的转换。当一个元素是 ‘sent’ 时,它会查找对应的 ‘received’ 元素,并生成一个转换,将该元素转换到其对应元素的位置并将其淡出。当元素为 ‘received’ 时,情况相反。如果没有对应项,则使用 fallback
转换。
¥We can achieve this effect using the crossfade
function, as seen in transition.js
, which creates a pair of transitions called send
and receive
. When an element is ‘sent’, it looks for a corresponding element being ‘received’, and generates a transition that transforms the element to its counterpart’s position and fades it out. When an element is ‘received’, the reverse happens. If there is no counterpart, the fallback
transition is used.
打开 TodoList.svelte
。首先,从 transition.js 导入 send
和 receive
转换:
¥Open TodoList.svelte
. First, import the send
and receive
transitions from transition.js:
<script>
import { send, receive } from './transition.js';
let { todos, remove } = $props();
</script>
<script lang="ts">
import { send, receive } from './transition.js';
let { todos, remove } = $props();
</script>
然后,将它们添加到 <li>
元素中,使用 todo.id
属性作为匹配元素的键:
¥Then, add them to the <li>
element, using the todo.id
property as a key to match the elements:
<li
class={{ done: todo.done }}
in:receive={{ key: todo.id }}
out:send={{ key: todo.id }}
>
现在,当你切换项目时,它们会顺利移动到新位置。非过渡项目仍然会笨拙地跳来跳去 - 我们可以在下一个练习中解决这个问题。
¥Now, when you toggle items, they move smoothly to their new location. The non-transitioning items still jump around awkwardly — we can fix that in the next exercise.
<script>
import TodoList from './TodoList.svelte';
const todos = $state([
{ id: 1, done: false, description: 'write some docs' },
{ id: 2, done: false, description: 'start writing blog post' },
{ id: 3, done: true, description: 'buy some milk' },
{ id: 4, done: false, description: 'mow the lawn' },
{ id: 5, done: false, description: 'feed the turtle' },
{ id: 6, done: false, description: 'fix some bugs' }
]);
let uid = todos.length + 1;
function remove(todo) {
const index = todos.indexOf(todo);
todos.splice(index, 1);
}
</script>
<div class="board">
<input
placeholder="what needs to be done?"
onkeydown={(e) => {
if (e.key !== 'Enter') return;
todos.push({
id: uid++,
done: false,
description: e.currentTarget.value
});
e.currentTarget.value = '';
}}
/>
<div class="todo">
<h2>todo</h2>
<TodoList todos={todos.filter((t) => !t.done)} {remove} />
</div>
<div class="done">
<h2>done</h2>
<TodoList todos={todos.filter((t) => t.done)} {remove} />
</div>
</div>
<style>
.board {
display: grid;
grid-template-columns: 1fr 1fr;
grid-column-gap: 1em;
max-width: 36em;
margin: 0 auto;
}
.board > input {
font-size: 1.4em;
grid-column: 1/3;
padding: 0.5em;
margin: 0 0 1rem 0;
}
h2 {
font-size: 2em;
font-weight: 200;
}
</style>