我想在加载 SvelteKit 应用程序的登陆页面后对标题进行动画处理,导致标题的每个字母一个接一个地出现,但我为此找到的所有解决方案都需要一些感觉很糟糕的东西并使 TypeScript 混乱或导致布局变化。
<h1>
<span>C</span><span>A</span><span>T</span>
</h1>
我希望看看是否有一种更“Svelte-y”的方式来达到效果而不产生负面副作用。
在 vanilla JS 中,我只需使用 css 将
<h1>
元素的不透明度设置为零,并为其不透明度指定一个过渡值。然后,我将定义一个将不透明度设置为 1 的类。在我的脚本中,我将在页面加载时设置一个时间间隔,以增量方式向标题内的 <span>
元素添加一个类。这样,每个字母都会连续出现并淡入视图,并且不会发生布局变化。
将上述普通 JS 方法转移到 Svelte 会引入一些奇怪的事情:
root
)才能查询其中的内容(看起来很老套,所以一定有一些我不明白的东西;通常我只会使用document.querySelectorAll()
,但这显然不是在 Svelte 中做到这一点的方法)。root
变量很生气,但我不知道要为其分配什么类型。
<script>
import { onMount } from "svelte";
let root;
const lettersWithClasses = { C: 'red', A: 'green', T: 'orange' };
const animateHeading = function(letters) {
letters.forEach((span, i) => {
setTimeout(() => {
span.classList.add("viz")
}, i * 400 + 1000);
});
}
onMount(() => {
let headingLetters = root.querySelectorAll("#heading > span");
animateHeading(headingLetters);
});
</script>
<div bind:this={root}>
<h1 id="heading" class="h1 text-center text-[12rem] font-extrabold">
{#each Object.entries(lettersWithClasses) as [key, value], i}
<span
class="{value}"
>{key}</span>
{/each}
</h1>
</div>
<style>
h1 > span {
opacity: 0;
transition: opacity 1s ease-in;
}
.viz {
opacity: 1 !important;
}
.red { color: red; }
.green { color: green; }
.orange { color: orange; }
</style>
这似乎是实现我的目标的一种更 Svelte-y 的方式(请参阅使用
fade
Svelte 过渡),但它会导致布局转变:(
此外,我通过
ready
更改此 onMount
变量的值的方式看起来确实很老套,但为了在页面加载后运行转换,这是必要的(也许不应该将其视为“ hacky”虽然因为 Rich Harris 提出了这种方法)。
可以肯定的是布局偏移 = 不好
<script>
import { onMount } from "svelte";
import { fade } from "svelte/transition";
const lettersWithClasses = { C: 'red', A: 'green', T: 'orange' };
let ready = false;
onMount(() => ready = true);
</script>
<div>
{#if}
<h1 id="heading" class="h1 text-center text-[12rem] font-extrabold">
{#each Object.entries(lettersWithClasses) as [key, value], i}
<span
in:fade|global={{ delay: 1000 + i * 400, duration: 1000 }}
class="{value}"
>{key}</span>
{/each}
</h1>
{/if}
</div>
<style>
.red {
color: #f70702;
}
.green {
color: #398c31;
}
.orange {
color: #f27202;
}
</style>
我考虑过在结束
<span>
标签后添加一个硬编码的 {/each}
并给它一个类,使其具有“隐藏”的可见性,但这对我来说也显得很老套。
仅使用 CSS 关键帧来完成这一切会更好吗?我所说的“更好”是指对我作为开发人员来说不那么复杂,对客户来说计算成本也更低。或者是否有一种更适合 Svelte 的“更好”方法?
只需为每个
<span>
分配一个不同的类,然后@关键帧和动画来运行过渡。
<script>
const lettersWithClasses = { C: 'red', A: 'green', T: 'orange' };
</script>
<div>
<h1 id="heading" class="h1 text-center text-[12rem] font-extrabold">
{#each Object.entries(lettersWithClasses) as [key, value], i}
<span
class="{value}"
>{key}</span>
{/each}
</h1>
</div>
<style>
@keyframes fadeinto-red {
from { opacity: 0; color: #white; }
to { opacity: 1; color: red; }
}
@keyframes fadeinto-green {
from { opacity: 0; color: white; }
to { opacity: 1; color: green; }
}
@keyframes fadeinto-orange {
from { opacity: 0; color: white; }
to { opacity: 1; color: orange; }
}
span.red{
animation: fadeinto-red 1.8s ease-in-out both;
}
span.green{
animation: fadeinto-green 1.8s ease-in-out 0.4s both;
}
span.orange{
animation: fadeinto-orange 1.8s ease-in-out 0.8s both;
}
</style>
这将是对第一个示例的修改
<script>
const lettersWithClasses = { C: 'red', A: 'green', T: 'orange' };
function fadeIn(spanElement, index) {
setTimeout(() => {
spanElement.style.opacity = 1
}, index * 400 + 1000);
}
</script>
<div>
<h1 id="heading" class="h1 text-center text-[12rem] font-extrabold">
{#each Object.entries(lettersWithClasses) as [letter, color], index}
<span
style:color={color}
use:fadeIn={index}
>
{letter}
</span>
{/each}
</h1>
</div>
<style>
h1 > span {
opacity: 0;
transition: opacity 1s ease-in;
}
</style>