我有一个带有多个可拖动项目组件的父组件,这些组件将在最终应用程序中动态创建。在每个项目内,我计算当前位置以及派生的偏移值。每次拖动任何 Item 时,我都希望其位置/偏移量与父组件反应式共享。
在带有商店的 Svelte 4 中,这相当容易。然而,我很难在 Svelte 5 中使用符文实现这一点。 Context-API 不知何故不是一个选项,因为我想在它们所属的 Item 组件内定义 pos/offset...不应该吗?
当前代码有两个问题:
这是我的代码的简化版本(REPL):
App.svelte
<svelte:options runes="{true}" />
<script>
import Item from './Item.svelte';
let CURRENT = $state({pos: {x:99, y:99}, offset: 99});
</script>
<h1>Last dragged item: ({CURRENT.pos.x} / {CURRENT.pos.y}) Offset: {CURRENT.offset}</h1>
<Item bind:CURRENT={CURRENT} itemName='ONE' />
<Item bind:CURRENT={CURRENT} itemName='TWO' />
Item.svelte
<svelte:options runes="{true}" />
<script>
import interact from 'interactjs';
let { CURRENT = $bindable(), itemName } = $props();
let item;
let pos = $state({x:0, y:0});
let offset = $derived(Math.floor(Math.sqrt( Math.pow(pos.x,2) + Math.pow(pos.y,2) )));
CURRENT.pos = pos;
CURRENT.offset = offset;
const handleDraggable = (node) => {
interact(node).draggable({
onmove: (ev) => {
let el = ev.target;
let x = (parseFloat(el.getAttribute('data-x')) || 0) + ev.dx;
let y = (parseFloat(el.getAttribute('data-y')) || 0) + ev.dy;
el.style.webkitTransform = el.style.transform = `translate(${x}px,${y}px)`;
el.setAttribute('data-x', x);
el.setAttribute('data-y', y);
pos.x = x;
pos.y = y;
},
});
}
</script>
<div id="draggable" bind:this={item} use:handleDraggable>
<p>{itemName} ({pos.x} / {pos.y}) Offset: {offset}</p>
</div>
正如 @PeppeL-G 在我的问题的评论中所暗示的,可以使用 回调函数将变量从子级传递到父级。
这是更改后的代码和REPL
App.svelte
<svelte:options runes="{true}" />
<script>
import Item from './Item.svelte';
let CURRENT = $state({pos: {x:99, y:99}, offset: 99});
let onDragged = (pos, offset) => {
CURRENT.pos = pos,
CURRENT.offset = offset
}
</script>
<h1>Last dragged item: ({CURRENT.pos.x} / {CURRENT.pos.y}) Offset: {CURRENT.offset}</h1>
<Item itemName='ONE' {onDragged} } />
<Item itemName='TWO' {onDragged} } />
Item.svelte
<svelte:options runes="{true}" />
<script>
import interact from 'interactjs';
//let { CURRENT = $bindable(), itemName } = $props();
let { itemName, onDragged } = $props();
let item;
let pos = $state({x:0, y:0});
let offset = $derived(Math.floor(Math.sqrt( Math.pow(pos.x,2) + Math.pow(pos.y,2) )));
const handleDraggable = (node) => {
interact(node).draggable({
onmove: (ev) => {
let el = ev.target;
let x = (parseFloat(el.getAttribute('data-x')) || 0) + ev.dx;
let y = (parseFloat(el.getAttribute('data-y')) || 0) + ev.dy;
el.style.webkitTransform = el.style.transform = `translate(${x}px,${y}px)`;
el.setAttribute('data-x', x);
el.setAttribute('data-y', y);
pos.x = x;
pos.y = y;
onDragged(pos, offset);
},
});
}
</script>
<div id="draggable" bind:this={item} use:handleDraggable>
<p>{itemName} ({pos.x} / {pos.y}) Offset: {offset}</p>
</div>
<style>
#draggable {
background-color: green;
width: 220px;
}
</style>