外部模块中的 Svelte 5 反应式 $state(dragListener)

问题描述 投票:0回答:1

我试图通过将拖动元素的位置定义为

.svelte.js
内的反应式
$state()
变量并将其在模块和
+page.svelte
之间来回传递,将dragListener重构为
+page.svelte
模块。

Svelte 5 Docs 提供了一个示例,其中

let position = $state()
在外部模块内定义(返回 getter 函数)。但是如果我需要在
let position = $state()
中定义
+page.svelte
我该怎么办?

下面我提供了一个最小的工作示例,其中可以通过按钮和拖动来更改元素的位置。对于这两者,我在

helpers.svelte.js
文件中提供了句柄。在
+page.svelte
内部,我使用
$inspect(position)
检查状态是否已更新。虽然检查将在单击按钮时触发,但不会在拖动时触发。

但是,我认为按钮事件仅出现是反应性的,因为

position
handleClick()
返回。虽然对于简单的单击操作而言,缺乏真正的反应性是可以接受的,但对于拖动侦听器而言,这是一个问题。

那么,我怎样才能使位置真正具有反应性?

顺便说一句:我也可以使用商店。但是我也不让它们在外部

svelte.js
文件中工作。另外,根据上面链接的 Svelte 5 文档,应该有一种方法可以使用
$state()
符文来解决这个问题。

+page.svelte

<svelte:options runes="{true}" />
<script>
    import { onMount } from "svelte";
    import {handleIncreaseX, handleDrag} from '$lib/helpers.svelte'
    
    let item;
    let position = $state({x:0, y:0});

    $inspect(position); //??? why is it only reacting to the button but not to drag?
    
    let onclick = () => {
        position = handleIncreaseX(item, position)
    }
    
    onMount(() => { 
        position = handleDrag(item, position);
    });
</script>


<h1>Position: {position.x} / {position.y}</h1>
<button {onclick}>Increase X</button>


<div id="draggable" bind:this={item}>Drag Me</div>

<style>
    #draggable {
        background-color: black;
        color: white;
        width: 200px;
    }
</style>

helpers.svelte.js

import interact from 'interactjs';

export function handleIncreaseX(item, position) {
    position.x += 1;
    item.style.webkitTransform =  item.style.transform = `translate(${position.x}px,${position.y}px)`;
    item.setAttribute('data-x', position.x);
    item.setAttribute('data-y', position.y);
    return position;
}


export function handleDrag(item, position) {

    console.log('DRAG INIT')

    interact(item).draggable({
        inertia: false,
        autoScroll: false,
        onstart: (ev) => {
            console.log('ONSTART');
        },
        
        onmove: (ev) => {
            let el = ev.target;

            // store the dragged position inside data-x/data-y attributes
            let x = (parseFloat(el.getAttribute('data-x')) || 0) + ev.dx;
            let y = (parseFloat(el.getAttribute('data-y')) || 0) + ev.dy;
        
            // translate the element and update position attributes
            el.style.webkitTransform =  el.style.transform = `translate(${x}px,${y}px)`;
            el.setAttribute('data-x', x);
            el.setAttribute('data-y', y);
            position = {x:x, y:y};

            console.log('ONMOVE', position);
        },

        onend: (ev)=>{
            console.log('ONEND');
        }
    });
    return position;
}

注意: 只要我在

+page.svelte
中定义拖动侦听器,反应性就可以完美地工作。然而,这对我来说不是一个选择。

+page.svelte

<svelte:options runes="{true}" />
<script>
    import { onMount } from "svelte";
    import interact from 'interactjs';

    import {handleIncreaseX} from '$lib/helpers.svelte'
    
    let item;
    let position = $state({x:0, y:0});
    
    $inspect(position);
    
    let onclick = () => {
        position = handleIncreaseX(item, position)
    }
    
    onMount(() => { 
        console.log('DRAG INIT')

        interact(item).draggable({
            inertia: false,
            autoScroll: false,
            onstart: (ev) => {
                console.log('ONSTART');
            },
            
            onmove: (ev) => {
                let el = ev.target;

                // store the dragged position inside data-x/data-y attributes
                let x = (parseFloat(el.getAttribute('data-x')) || 0) + ev.dx;
                let y = (parseFloat(el.getAttribute('data-y')) || 0) + ev.dy;
            
                // translate the element and update position attributes
                el.style.webkitTransform =  el.style.transform = `translate(${x}px,${y}px)`;
                el.setAttribute('data-x', x);
                el.setAttribute('data-y', y);
                position = {x:x, y:y};

                console.log('ONMOVE', position);
            },

            onend: (ev)=>{
                console.log('ONEND');
            }
        });
    });
</script>


<h1>Position: {position.x} / {position.y}</h1>
<button {onclick}>Increase X</button>


<div id="draggable" bind:this={item}>Drag Me</div>

<style>
    #draggable {
        background-color: black;
        color: white;
        width: 200px;
    }
</style>
state svelte interact.js svelte-5
1个回答
0
投票

在拖动函数中,您重新分配函数参数,这永远不会对外部产生任何影响。您只需修改位置即可。

// instead of:
position = {x:x, y:y};

// do
position.x = x;
position.y = y;

在这种情况下也没有真正的理由让函数返回任何内容。您只需传入状态并在拖动时修改它即可。

预览 REPL1

还有其他方法,例如状态可以由函数创建并返回。


1 链接稍后可能会中断并需要更新。

© www.soinside.com 2019 - 2024. All rights reserved.