8

我喜欢 Svelte,但我坚持一些基本的东西(尽管只是装饰性的)。下面的代码应该在两个元素之间平滑过渡,但它会“跳跃”——显然是在传入元素到达之前为它腾出空间。

这个问题类似于Rich Harris 几年前指出的问题,但我没有看到解决方案已实施。Svelte 教程站点上的所有示例仅转换一个元素。

这是基本的标记/代码:

{#if div1}
    <div 
      in:fly={{ x: 100, duration: 400, delay: 400 }}
      out:fly={{ x: 100, duration: 400 }}>Div 1</div>
{:else}
    <div 
      in:fly={{ x: 100, duration: 400, delay: 400 }}
      out:fly={{ x: 100, duration: 400 }}>Div 2</div>
{/if}

<button on:click={()=>{ div1 = !div1}}>Switch</button>

Vue 中的等效工作是:

<transition name="fly" mode="out-in">
    <div v-if="div1">Div 1</div>
    <div v-else>Div 2</div>
</transition>

这是一个代码沙箱示例。您可以看到按钮向下跳来为新元素腾出空间。我添加了一个等于 400 持续时间的“in”转换延迟(我知道这是默认设置,但为了清楚起见,我想明确设置它)。延迟应该允许第一个元素在转换下一个元素之前转换出来,如第一个链接中所述(Harris 称之为“延迟的骇人使用”)并在此处建议。

我还尝试将被淘汰的元素显式设置为 position: absolute ,这样它就不会占用空间。这是一个(仍然无法正常工作)示例。似乎有点不雅,即使它正在工作。出于某种原因,转换覆盖了设置位置的类:绝对。

任何帮助是极大的赞赏!

更新:我用这段代码得到了想要的效果。我在这里所做的是复制和修改 Svelte 的飞行过渡以获取一个额外的参数——“位置”,它可以设置为“绝对”或“相对”或任何你想要的。对 CSS 进行了一些调整以确保没有奇怪的副作用(将容器设置为 position: relative,并将每个元素的宽度设置为 100% 以确保它们不会改变大小)。这行得通,但我仍然觉得应该有一种不那么劳动密集型的方法,而无需修改 Svelte 的过渡。

4

2 回答 2

10

我也是从 Vue 过来的,out-in 是我对 Svelte 怀念的一件事。Rich Harris 甚至在 Svelte 3 之前就承认了这一点,但据我所知从未真正实施过修复。

单一条件、仅延迟、out-in 转换方法的问题在于,Svelte 会在条件切换后创建传入元素,尽管在转换中存在延迟。您可以减慢过渡速度并检查开发工具以查看这一点,这两个元素都将存在传入的过渡延迟不会阻止元素具有大小,只是可见性。

解决它的一种方法是用绝对位置做你所做的事情,有点密集并成为样板。另一种方法是为包含正在转换的元素的容器设置绝对高度,将其他所有内容拉出容器(示例中的按钮)并隐藏溢出,如此处所示,非常依赖于 css,并且在某些情况下并不总是很好布局。

我使用的最后一种方法更加圆润,但是由于 Svelte 有一个在动画完成时调度的 outroend 事件,您可以为蓝色或您的第二个条件添加一个变量,并为第二个条件(此处为蓝色)并连接触发器,以便它检查活动变量并将其关闭,然后打开 outroend 事件内的另一个变量,如此处所示,您还可以删除任何延迟,因为持续时间变为延迟。

在转换期间检查 DOM 似乎这是两个元素不同时存在的唯一方法,因为它们依赖于不同的条件,我相信还有更优雅的方法可以实现这一点,但这对我有用。

编辑

还有另一个选项仅适用于支持 CSS 网格规范的浏览器,幸运的是,这在这一点上几乎是通用的。它与绝对定位方法非常相似,还有一个额外的好处是您根本不必担心元素的高度

这背后的想法是,使用 CSS Grid,我们可以通过在 1 col x 1 row 的隐式网格上给两个元素(或超过 2 个)相同的开始和结束列和行来强制 2 个元素占据相同grid-area的空间(网格足够聪明,不会创建我们不会使用的额外列和行)。由于 Svelte 在它的过渡中使用了变换,我们可以让元素来来去去而无需任何布局转换,很好。我们不再需要担心影响元素的绝对位置或延迟,我们可以将过渡时间微调到完美。grid-columngrid-row

这是一个展示简单设置的 REPL,另一个展示如何使用它来获得一些非常漂亮的分层效果的 REPL,哇

于 2020-01-24T08:35:19.170 回答
5

如果您碰巧有两个以上的状态要在它们之间交换,那么将行为抽象到自定义存储非常有用。商店可能看起来像这样:

statefulSwap(initialState) {
    const state = writable(initialState);
    let nextState = initialState;
    
    function transitionTo(newState) {
        if(nextState === newState) return;
        nextState = newState
        state.set(null)
    }
    
    function onOutro() {
        state.set(nextState)
    }
    return {
        state,
        transitionTo,
        onOutro
    }
}

您可以使用条件块在元素之间交换:

{#if $state == "first"}
    <h1 transition:fade on:outroend={onOutro}>
        First
</h1>
{:else if $state == "second"}
    <h1 transition:fade on:outroend={onOutro}>
        Second
</h1>
{/if}

out-in这种技术通过最初将当前状态设置为null,然后在onOutro第一个元素转换出来后应用新状态来模拟Vue 的行为。

这是一个REPL示例。这里的优点是您可以使用不同的动画动作和时间来拥有任意数量的状态,而无需跟踪交换逻辑。else但是,如果您的条件标记中有默认块,这将不起作用。

于 2020-12-06T19:00:17.673 回答