我试图通过将拖动元素的位置定义为
.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>
在拖动函数中,您重新分配函数参数,这永远不会对外部产生任何影响。您只需修改位置即可。
// instead of:
position = {x:x, y:y};
// do
position.x = x;
position.y = y;
在这种情况下也没有真正的理由让函数返回任何内容。您只需传入状态并在拖动时修改它即可。
还有其他方法,例如状态可以由函数创建并返回。
1 链接稍后可能会中断并需要更新。