如何在页面加载时为 Svelte 组件设置动画而不导致布局移位

问题描述 投票:0回答:1

问题

我想在加载 SvelteKit 应用程序的登陆页面后对标题进行动画处理,导致标题的每个字母一个接一个地出现,但我为此找到的所有解决方案都需要一些感觉很糟糕的东西并使 TypeScript 混乱或导致布局变化。

我想要制作动画的标题的简化结构

    <h1>
        <span>C</span><span>A</span><span>T</span>
    </h1>

目前的解决方案:

  • “翻译”我将如何在 vanilla JS 中做到这一点(看起来很hacky)
  • 简洁的过渡(导致布局变化)
  • 纯CSS(对我来说似乎是最简单、最有效的)

我希望看看是否有一种更“Svelte-y”的方式来达到效果而不产生负面副作用。

我如何在普通 JS 中做到这一点

在 vanilla JS 中,我只需使用 css 将

<h1>
元素的不透明度设置为零,并为其不透明度指定一个过渡值。然后,我将定义一个将不透明度设置为 1 的类。在我的脚本中,我将在页面加载时设置一个时间间隔,以增量方式向标题内的
<span>
元素添加一个类。这样,每个字母都会连续出现并淡入视图,并且不会发生布局变化。

上述方法的简洁“翻译”让 TypeScript 感到不安

将上述普通 JS 方法转移到 Svelte 会引入一些奇怪的事情:

  • 我必须将组件最顶层的元素绑定到一个变量(
    root
    )才能查询其中的内容(看起来很老套,所以一定有一些我不明白的东西;通常我只会使用
    document.querySelectorAll()
    ,但这显然不是在 Svelte 中做到这一点的方法)。
  • TypeScript 对
    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 Transitions(导致布局偏移)

这似乎是实现我的目标的一种更 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

仅使用 CSS 关键帧来完成这一切会更好吗?我所说的“更好”是指对我作为开发人员来说不那么复杂,对客户来说计算成本也更低。或者是否有一种更适合 Svelte 的“更好”方法?

如何使用 CSS 完成这一切的示例

只需为每个

<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>

svelte svelte-transition
1个回答
0
投票

这将是对第一个示例的修改

REPL

<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>
© www.soinside.com 2019 - 2024. All rights reserved.