我喜欢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>
编辑:最初在这篇文章中并在评论中引用的非功能性CodeSandbox链接已被删除。
我也是从 Vue 过来的,外向是我想念 Svelte 的一件事。Rich Harris 甚至在 Svelte 3 之前就承认了这一点,但据我所知,从未真正实施过修复程序。
单一条件、仅延迟、外向转换方法的问题在于,尽管在转换上有延迟,但一旦条件切换,Svelte 就会创建传入元素。您可以放慢过渡速度并检查开发工具以查看这一点,这两个元素都将存在,传入的过渡延迟不会阻止元素具有大小,只是可见性。
解决它的一种方法是做你用绝对位置做过的事情,有点密集,成为样板。另一种方法是为保存正在转换的元素的容器设置绝对高度,将其他所有内容从容器中拉出(示例中的按钮(并隐藏溢出,如下所示,非常依赖于 css,并且并不总是在某些布局中很好地发挥作用。
我使用的最后一种方法有点圆润,但由于 Svelte 有一个在动画完成后调度的 outroend 事件,您可以为 blue 或任何第二个条件添加一个变量,并为第二个条件放入一个 else if 块(此处为蓝色(并连接触发器,以便它检查活动变量并将其关闭, 然后打开 Outroend 事件内的另一个变量,如此处所示,您还可以删除任何延迟,因为持续时间成为延迟。
在过渡期间检查 DOM 似乎这是两个元素同时不存在的唯一方法,因为它们依赖于不同的条件,我相信还有更优雅的方法可以实现这一点,但这对我有用。
编辑:
还有另一个选项仅适用于支持 CSS 网格规范的浏览器,幸运的是,这在这一点上几乎是通用的。它与绝对定位方法非常相似,但还有一个额外的好处,您根本不用担心元素的高度
这背后的想法是,使用 CSS Grid,我们可以强制 2 个元素占用与grid-area
或grid-column
相同的空间,并通过为两个元素(或超过 2 个(提供相同的开始和结束列以及 1 列乘 1 行的隐式网格上的行来grid-row
(grid 足够智能,不会创建我们不会使用的额外列和行(。由于 Svelte 在过渡中使用了变换,我们可以让元素来来去去,没有任何布局偏移,很好。我们不再需要担心影响元素的绝对位置或延迟,我们可以将过渡时间微调到完美。
这是一个 REPL 来展示一个简单的设置,另一个 REPL 来展示如何使用它来获得一些非常甜蜜的分层效果,哇!
如果您碰巧有两个以上的状态要交换,将行为抽象到自定义存储区真的很有帮助。商店可能看起来像这样:
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}
这种技术模拟 Vue 的行为out-in
方法是最初将当前状态设置为null
,然后在第一个元素转换后onOutro
应用新状态。
下面是一个 REPL 示例。这样做的好处是,您可以使用不同的动画操作和计时拥有任意数量的状态,而无需跟踪交换逻辑。但是,如果条件标记中有默认else
块,则这不起作用。
这就是"网格方式"的基本设置
:<script>
import { scale } from "svelte/transition"
let condi = true;
</script>
<div class="container">
{#if condi}
<div class="item" in:scale out:scale />
{:else}
<div class="item" in:scale out:scale />
{/if}
</div>
<style>
.container {
display: grid;
}
.item {
grid-column-start: 1;
grid-column-end: 2;
grid-row-start: 1;
grid-row-end: 2;
}
</style>