发布于 2026-01-06 2 阅读
0

使用 svelte-dnd-action 在 Svelte 中实现拖放功能

使用 svelte-dnd-action 在 Svelte 中实现拖放功能

简而言之:本文将向您展示如何使用svelte-dnd-action为您的 Svelte 应用添加强大的拖放功能。如果您一直想用 Svelte 构建一个类似 Trello 的应用(只是动画效果比 Trello 更美观),那么您来对地方了。

我们来简单聊聊拖放功能吧。

如果你曾经尝试过实现一个具有丰富(甚至基本)拖放交互的应用,你就会知道这有多么困难。当然,浏览器内置了拖放 API。但它存在一个小问题——在视觉效果和交互体验方面表现平平。
不信?svelte-sortable-list就是一个利用浏览器拖放 API 并在此基础上(借助 Svelte)尽可能添加动画效果的库。尽管它确实付出了令人钦佩的努力(我是认真的),但它仍然无法投入生产环境。被拖动的元素以及所有其他元素都会停留在原来的位置,直到发生放置事件。这种感觉非常静态和乏味(你可以自己尝试一下)。正如 Rich Harris 所说:“我们可以做得更好。”

React 开发者们一直很享受功能强大但略显笨重且复杂的react-beautiful-dnd。而 Svelte 开发者(至少我本人)却对此感到失望。

svelte-dnd-action是一个旨在纠正这一问题的新库。

svelte-dnd-action 的工作原理是什么?

顾名思义,该库利用Svelte 的 actions 机制,将任何列表容器转换为拖放 (dnd) 区域。它依赖于宿主(即你的代码)在需要时(通过事件)更新列表数据。它还依赖于宿主,利用Svelte 内置的翻转动画来实现一些动画效果。
让我们来看一个简单的例子;

假设我们有以下组件,用于显示包含 3 个项目的列表:

<style>
    div {
        height: 1.5em;
        width: 10em;
        text-align: center;
        border: 1px solid black;
        margin: 0.2em;
        padding: 0.3em;
    }
</style>
<script>
    let items = [
        {id:1, title: 'I'},
        {id:2, title: 'Am'},
        {id:3, title: 'Yoda'}
    ];
</script>
<section>
    {#each items as item(item.id)}
        <div>
            {item.title}    
        </div>
    {/each}
</section>
Enter fullscreen mode Exit fullscreen mode

现在假设我们想让它可以通过拖放功能重新排序。
让我们把 svelte-dnd-action 添加到其中:

<style>
    div {
        height: 1.5em;
        width: 10em;
        text-align: center;
        border: 1px solid black;
        margin: 0.2em;
        padding: 0.3em;
    }
</style>
<script>
    import {dndzone} from 'svelte-dnd-action';
    function handleSort(e) {
        items = e.detail.items;
    }
    let items = [
        {id:1, title: 'I'},
        {id:2, title: 'Am'},
        {id:3, title: 'Yoda'}
    ];
</script>
<section use:dndzone={{items}} on:consider={handleSort} on:finalize={handleSort}>
    {#each items as item(item.id)}
        <div>
            {item.title}    
        </div>
    {/each}
</section>
Enter fullscreen mode Exit fullscreen mode

在 REPL 中运行这个示例
,很简单吧?
我们将元素传递给dndzoneaction,并在收到 `drop`consider或 `remove`finalize事件时更新列表。两者的区别在于,` drop`consider事件用于处理中间状态(因为元素需要“腾出空间”),而 ` finalizeremove` 事件用于处理被丢弃的元素。区分这两个事件在决定是否将新列表保存到服务器时非常有用。
需要注意的一点是,列表中的每个元素都有一个 ` idvalue` 属性,我们也将其作为键传递给 ` #eachdrop` 块。`drop`svelte-dnd-action依赖于 `value` 属性的存在id,因此请确保它存在。

这看起来不错,但目前还没有动画。为了让所有元素都能流畅地动起来,我们需要添加flip一些动画效果,并将翻转持续时间dndzone作为参数传递给它:

<style>
    div {
        height: 1.5em;
        width: 10em;
        text-align: center;
        border: 1px solid black;
        margin: 0.2em;
        padding: 0.3em;
    }
</style>
<script>
    import {dndzone} from 'svelte-dnd-action';
    import {flip} from 'svelte/animate';
    const flipDurationMs = 200;
    function handleSort(e) {
        items = e.detail.items;
    }
    let items = [
        {id:1, title: 'I'},
        {id:2, title: 'Am'},
        {id:3, title: 'Yoda'}
    ];
</script>
<section use:dndzone={{items, flipDurationMs}} on:consider={handleSort} on:finalize={handleSort}>
    {#each items as item(item.id)}
        <div animate:flip={{duration:flipDurationMs}}>
            {item.title}    
        </div>
    {/each}
</section>
Enter fullscreen mode Exit fullscreen mode

在 REPL Viola 中运行此示例
,它会动起来!

Dnd 区域类型

默认情况下,如果您dnd-zone在多个列表容器中使用此方法,则可以从一个列表中抓取元素并将其拖放到另一个列表中。这非常方便,但有时您需要控制元素的移动位置。
为了满足此需求,该方法svelte-dnd-action接受一个可选type参数。您可以
在 REPL 中查看其实际效果
在本例中,您可以将顶部两个类型为“light”的列表之间的元素移动。您无法将顶部列表与底部类型为“dark”的列表之间的元素移动(幸运的是,尤达和卢克的元素不会受到影响)。您仍然可以像以前一样在每个列表内打乱元素的顺序。

类型用途的一个实用方法是构建嵌套的动态区域。例如,如果您正在构建类似 Trello 的看板,每一列都可以是一个列表dndzone(这样就可以将项目从一列移动到另一列),而容纳这些列的容器也可以是dndzone不同类型的列表。这样,列的顺序就可以独立于它们包含的项目进行重新排序。

它还能做什么?

实际上,这个库的功能远不止这些。
要查看包含水平和垂直列表、看板(如上所述的嵌套区域)以及自动滚动功能的更复杂示例,请查看此 REPL

今天就到这里啦,各位。祝你们拖放愉快!

2020年10月3日更新:图书馆现已全面无障碍通行。您可以在这里了解更多信息。

文章来源:https://dev.to/isaachagoel/drag-and-drop-with-svelte-using-svelte-dnd-action-4554