MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Svelte动画基础:过渡效果的实现

2022-02-142.5k 阅读

1. Svelte 动画基础概念

在深入探讨过渡效果之前,我们先来了解一下 Svelte 中动画的基本概念。Svelte 是一个用于构建用户界面的 JavaScript 框架,它的动画系统设计得简洁而强大。

Svelte 中的动画主要分为两种类型:过渡(transitions)和状态变化动画(state - change animations)。过渡通常用于元素进入或离开 DOM 时产生动画效果,而状态变化动画则在元素的属性或状态发生改变时触发。本文主要聚焦于过渡效果。

1.1 过渡的触发时机

过渡效果通常在以下两种主要情况下触发:

  • 元素进入 DOM:当一个新的元素被添加到 DOM 中时,可以为其定义一个进入过渡效果,让它以一种平滑、美观的方式呈现到用户面前。
  • 元素离开 DOM:当元素从 DOM 中移除时,也可以应用离开过渡效果,使其以一种优雅的方式消失。

2. 简单过渡效果的实现

2.1 使用 fade 过渡

Svelte 提供了一些内置的过渡效果,fade 就是其中之一。它可以实现元素的淡入淡出效果。下面是一个简单的示例:

<script>
    let visible = true;
</script>

<button on:click={() => visible =!visible}>
    Toggle visibility
</button>

{#if visible}
    <div in:fade out:fade>
        This div fades in and out.
    </div>
{/if}

在上述代码中,我们定义了一个布尔变量 visible 来控制 div 元素的显示与隐藏。in:fade 表示元素进入时应用淡入效果,out:fade 表示元素离开时应用淡出效果。当点击按钮时,visible 的值发生变化,div 元素相应地淡入或淡出。

2.2 自定义 fade 过渡的时长和缓动函数

fade 过渡默认有一些参数可以调整,比如过渡的时长和缓动函数。我们可以通过传递参数来自定义这些行为。

<script>
    let visible = true;
</script>

<button on:click={() => visible =!visible}>
    Toggle visibility
</button>

{#if visible}
    <div 
        in:fade="{{ duration: 1000, easing: 'ease - out' }}" 
        out:fade="{{ duration: 1500, easing: 'ease - in' }}">
        This div has custom fade in and out settings.
    </div>
{/if}

这里,我们为 in:fade 设置了 duration 为 1000 毫秒(1 秒),缓动函数为 ease - out,表示淡入时缓慢结束;为 out:fade 设置了 duration 为 1500 毫秒,缓动函数为 ease - in,表示淡出时缓慢开始。

3. 平移过渡(slide

3.1 基本的 slide 过渡

slide 过渡可以让元素在进入或离开 DOM 时沿着某个方向滑动。下面是一个水平滑动的示例:

<script>
    let visible = true;
</script>

<button on:click={() => visible =!visible}>
    Toggle visibility
</button>

{#if visible}
    <div in:slide="{{ x: 200 }}" out:slide="{{ x: -200 }}">
        This div slides horizontally.
    </div>
{/if}

在这个例子中,in:slide="{{ x: 200 }}" 表示元素进入时从右侧 200 像素的位置滑动到其正常位置,out:slide="{{ x: -200 }}" 表示元素离开时向左侧滑动 200 像素后消失。

3.2 垂直 slide 过渡及更多参数

我们也可以实现垂直方向的滑动,并且可以设置更多的参数,如过渡时长和缓动函数。

<script>
    let visible = true;
</script>

<button on:click={() => visible =!visible}>
    Toggle visibility
</button>

{#if visible}
    <div 
        in:slide="{{ y: -100, duration: 800, easing: 'ease - in - out' }}" 
        out:slide="{{ y: 100, duration: 1200, easing: 'linear' }}">
        This div slides vertically with custom settings.
    </div>
{/if}

这里,y 参数控制垂直方向的滑动,duration 设置过渡时长,easing 选择缓动函数。in:slide 时元素从上方 100 像素处滑动进来,out:slide 时向下方滑动 100 像素后消失,且都有各自不同的时长和缓动效果。

4. 缩放过渡(scale

4.1 简单缩放过渡

scale 过渡可以让元素在进入或离开 DOM 时进行缩放。

<script>
    let visible = true;
</script>

<button on:click={() => visible =!visible}>
    Toggle visibility
</button>

{#if visible}
    <div in:scale out:scale>
        This div scales in and out.
    </div>
{/if}

默认情况下,元素进入时从缩放比例 0 逐渐放大到 1,离开时从 1 逐渐缩小到 0。

4.2 自定义缩放过渡的起始和结束比例

我们可以通过传递参数来自定义缩放的起始和结束比例。

<script>
    let visible = true;
</script>

<button on:click={() => visible =!visible}>
    Toggle visibility
</button>

{#if visible}
    <div 
        in:scale="{{ start: 0.5, end: 1.2, duration: 1000 }}" 
        out:scale="{{ start: 1.2, end: 0.5, duration: 1000 }}">
        This div has custom scale in and out settings.
    </div>
{/if}

在这个例子中,in:scale 时元素从 0.5 倍大小开始放大到 1.2 倍大小,out:scale 时从 1.2 倍大小缩小到 0.5 倍大小,并且都设置了 1000 毫秒的过渡时长。

5. 组合过渡效果

在 Svelte 中,我们可以将多个过渡效果组合起来使用,以创造出更加丰富和独特的动画效果。

5.1 fadeslide 组合

<script>
    let visible = true;
</script>

<button on:click={() => visible =!visible}>
    Toggle visibility
</button>

{#if visible}
    <div 
        in:fade="{{ duration: 800 }}" 
        in:slide="{{ x: 200, duration: 800 }}" 
        out:fade="{{ duration: 800 }}" 
        out:slide="{{ x: -200, duration: 800 }}">
        This div fades and slides.
    </div>
{/if}

在这个示例中,元素进入时同时应用淡入和从右侧滑动进来的效果,离开时同时应用淡出和向左侧滑动出去的效果。两个过渡效果的时长都设置为 800 毫秒,以确保它们同步进行。

5.2 scalefade 组合

<script>
    let visible = true;
</script>

<button on:click={() => visible =!visible}>
    Toggle visibility
</button>

{#if visible}
    <div 
        in:scale="{{ start: 0.5, end: 1.2, duration: 1000 }}" 
        in:fade="{{ duration: 1000 }}" 
        out:scale="{{ start: 1.2, end: 0.5, duration: 1000 }}" 
        out:fade="{{ duration: 1000 }}">
        This div scales and fades.
    </div>
{/if}

这里,元素进入时先从 0.5 倍大小缩放至 1.2 倍大小,同时淡入;离开时从 1.2 倍大小缩放至 0.5 倍大小,同时淡出,且所有过渡时长均为 1000 毫秒。

6. 创建自定义过渡效果

除了使用 Svelte 内置的过渡效果,我们还可以创建自己的自定义过渡效果,以满足特定的设计需求。

6.1 基本自定义过渡函数

下面是一个简单的自定义过渡效果,它可以让元素在进入时围绕一个点旋转。

<script>
    function rotateIn(node, params) {
        const style = getComputedStyle(node);
        const transformOrigin = style.transformOrigin;
        node.style.transformOrigin = params.origin || 'center';
        const start = 0;
        const end = 360;
        const duration = params.duration || 1000;
        const easing = params.easing || 'linear';

        return {
            duration,
            easing,
            css: t => `transform: rotate(${start + t * (end - start)}deg); transform - origin: ${node.style.transformOrigin};`
        };
    }

    let visible = true;
</script>

<button on:click={() => visible =!visible}>
    Toggle visibility
</button>

{#if visible}
    <div in:rotateIn="{{ origin: 'top - left', duration: 1500, easing: 'ease - out' }}">
        This div rotates in.
    </div>
{/if}

在这个 rotateIn 函数中,我们首先获取元素的 transformOrigin 属性,然后根据传入的参数设置新的 transformOrigin。接着定义了旋转的起始和结束角度,以及过渡的时长和缓动函数。return 部分返回一个对象,其中 durationeasing 用于设置过渡的时长和缓动效果,css 函数则根据过渡的进度 t(取值范围为 0 到 1)计算出相应的 transform 样式。

6.2 自定义离开过渡效果

我们可以类似地创建一个自定义的离开过渡效果,比如让元素在离开时以一种特殊的方式扭曲消失。

<script>
    function distortOut(node, params) {
        const style = getComputedStyle(node);
        const start = 0;
        const end = 1;
        const duration = params.duration || 1000;
        const easing = params.easing || 'linear';

        return {
            duration,
            easing,
            css: t => {
                const k = t * (end - start);
                return `transform: skewX(${k * 45}deg) skewY(${k * 45}deg);`;
            }
        };
    }

    let visible = true;
</script>

<button on:click={() => visible =!visible}>
    Toggle visibility
</button>

{#if visible}
    <div 
        in:fade 
        out:distortOut="{{ duration: 1500, easing: 'ease - in' }}">
        This div fades in and distorts out.
    </div>
{/if}

distortOut 函数中,我们定义了元素在离开时的扭曲效果。通过 css 函数根据过渡进度 t 计算出 skewXskewY 的值,从而实现扭曲效果。这里元素进入时使用了内置的 fade 过渡,离开时使用自定义的 distortOut 过渡。

7. 过渡效果的事件处理

在过渡效果发生的过程中,我们有时需要对一些事件进行处理,比如过渡开始、结束等。Svelte 为我们提供了相应的事件处理机制。

7.1 过渡开始事件

<script>
    let visible = true;
    function onStart() {
        console.log('Transition started');
    }
</script>

<button on:click={() => visible =!visible}>
    Toggle visibility
</button>

{#if visible}
    <div 
        in:fade="{{ duration: 1000 }}" 
        on:introstart={onStart}>
        This div has a start event handler.
    </div>
{/if}

在上述代码中,我们定义了一个 onStart 函数,当 div 元素的淡入过渡开始时,会在控制台打印出 'Transition started'。这里使用 on:introstart 来绑定过渡开始事件。

7.2 过渡结束事件

<script>
    let visible = true;
    function onEnd() {
        console.log('Transition ended');
    }
</script>

<button on:click={() => visible =!visible}>
    Toggle visibility
</button>

{#if visible}
    <div 
        in:fade="{{ duration: 1000 }}" 
        on:introend={onEnd}>
        This div has an end event handler.
    </div>
{/if}

同样,我们定义了 onEnd 函数,并使用 on:introend 绑定到淡入过渡结束事件上。当淡入过渡结束时,会在控制台打印 'Transition ended'。对于离开过渡,对应的事件是 outrostartoutroend

8. 在组件中使用过渡效果

8.1 组件内的过渡

我们可以在 Svelte 组件内部使用过渡效果,让组件的显示和隐藏更加平滑。

// MyComponent.svelte
<script>
    let visible = true;
</script>

<button on:click={() => visible =!visible}>
    Toggle visibility
</button>

{#if visible}
    <div in:fade out:fade>
        This is content inside the component.
    </div>
{/if}

这里,MyComponent.svelte 组件内部的 div 元素使用了淡入淡出过渡效果。当点击按钮时,组件内的内容会平滑地显示或隐藏。

8.2 父组件控制子组件的过渡

父组件也可以控制子组件的过渡效果。

// Parent.svelte
<script>
    import MyComponent from './MyComponent.svelte';
    let showChild = true;
</script>

<button on:click={() => showChild =!showChild}>
    Toggle child component
</button>

{#if showChild}
    <MyComponent 
        in:fade="{{ duration: 1000 }}" 
        out:fade="{{ duration: 1000 }}">
    </MyComponent>
{/if}

Parent.svelte 组件中,通过 showChild 变量控制 MyComponent 的显示与隐藏,并为其应用了淡入淡出过渡效果。这样,当点击按钮时,MyComponent 会以平滑的过渡效果出现或消失。

9. 优化过渡效果的性能

在使用过渡效果时,性能是一个重要的考虑因素。以下是一些优化过渡效果性能的方法:

9.1 使用硬件加速

通过将过渡效果应用到 transformopacity 属性上,可以利用浏览器的硬件加速功能,提高动画的流畅度。因为 transformopacity 不会触发重排(reflow)和重绘(repaint),而改变其他属性,如 widthheight 等,可能会导致性能问题。

<script>
    let visible = true;
</script>

<button on:click={() => visible =!visible}>
    Toggle visibility
</button>

{#if visible}
    <div 
        in:scale="{{ start: 0.5, end: 1, duration: 800 }}" 
        out:scale="{{ start: 1, end: 0.5, duration: 800 }}">
        This div uses hardware - accelerated scale transition.
    </div>
{/if}

这里的缩放过渡使用 scale,基于 transform 属性,能更好地利用硬件加速。

9.2 控制过渡时长和帧率

避免设置过长的过渡时长,因为过长的过渡可能会让用户感到等待时间过长。同时,合理设置帧率,一般 60fps 是比较理想的,Svelte 的过渡系统在默认情况下尽量会保持较高的帧率,但如果过渡效果过于复杂,可能需要调整。例如,在自定义过渡效果中,可以根据实际情况调整 durationeasing 来优化帧率。

<script>
    function customTransition(node, params) {
        const duration = params.duration || 500;
        const easing = params.easing || 'linear';
        return {
            duration,
            easing,
            css: t => `transform: translateX(${t * 200}px);`
        };
    }

    let visible = true;
</script>

<button on:click={() => visible =!visible}>
    Toggle visibility
</button>

{#if visible}
    <div in:customTransition="{{ duration: 500, easing: 'ease - in - out' }}">
        This div has an optimized custom transition.
    </div>
{/if}

在这个自定义过渡中,将时长设置为 500 毫秒,并选择了合适的缓动函数 ease - in - out,以在保证动画效果的同时优化性能。

9.3 避免过度使用复杂过渡

虽然复杂的过渡效果可能看起来很炫酷,但过多的复杂过渡会增加浏览器的计算负担。尽量保持过渡效果简洁,只在必要的地方使用复杂效果。例如,在一个页面中,如果有多个元素都需要过渡效果,优先考虑简单的淡入淡出、滑动等基本过渡,对于关键元素或特殊场景再使用复杂的自定义过渡。

10. 响应式过渡效果

在不同的屏幕尺寸和设备上,我们可能希望过渡效果有不同的表现,以提供更好的用户体验。

10.1 根据屏幕宽度调整过渡时长

<script>
    import { browser } from '$app/env';
    let visible = true;
    let transitionDuration;

    if (browser) {
        const mediaQuery = window.matchMedia('(min - width: 768px)');
        mediaQuery.addEventListener('change', () => {
            if (mediaQuery.matches) {
                transitionDuration = 1000;
            } else {
                transitionDuration = 500;
            }
        });
        if (mediaQuery.matches) {
            transitionDuration = 1000;
        } else {
            transitionDuration = 500;
        }
    }
</script>

<button on:click={() => visible =!visible}>
    Toggle visibility
</button>

{#if visible}
    <div 
        in:fade="{{ duration: transitionDuration }}" 
        out:fade="{{ duration: transitionDuration }}">
        This div has a responsive fade transition.
    </div>
{/if}

在上述代码中,我们使用 matchMedia 来检测屏幕宽度。如果屏幕宽度大于等于 768 像素,过渡时长设置为 1000 毫秒;否则,设置为 500 毫秒。这样在桌面设备上过渡可能会更平缓,而在移动设备上过渡更快速,以适应不同设备的使用场景。

10.2 不同设备的过渡类型切换

我们还可以根据设备类型切换过渡类型。例如,在桌面设备上使用缩放过渡,在移动设备上使用滑动过渡。

<script>
    import { browser } from '$app/env';
    let visible = true;
    let inTransition;
    let outTransition;

    if (browser) {
        const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
        if (isMobile) {
            inTransition = { name:'slide', params: { y: -100, duration: 500 } };
            outTransition = { name:'slide', params: { y: 100, duration: 500 } };
        } else {
            inTransition = { name:'scale', params: { start: 0.5, end: 1, duration: 800 } };
            outTransition = { name:'scale', params: { start: 1, end: 0.5, duration: 800 } };
        }
    }
</script>

<button on:click={() => visible =!visible}>
    Toggle visibility
</button>

{#if visible}
    <div 
        {#if inTransition.name ==='slide'}
            in:slide={inTransition.params}
        {:else}
            in:scale={inTransition.params}
        {/if}
        {#if outTransition.name ==='slide'}
            out:slide={outTransition.params}
        {:else}
            out:scale={outTransition.params}
        {/if}>
        This div has a device - specific transition.
    </div>
{/if}

在这段代码中,通过检测 navigator.userAgent 判断设备是否为移动设备。如果是移动设备,元素进入和离开时使用垂直滑动过渡;如果是桌面设备,则使用缩放过渡。这样可以根据不同设备的特点提供更合适的过渡效果。

通过以上对 Svelte 过渡效果的深入探讨,我们从基础概念到复杂的自定义和优化,全面了解了如何在 Svelte 项目中实现丰富多样且性能良好的过渡效果,为打造优秀的用户界面奠定了坚实的基础。无论是简单的淡入淡出,还是复杂的组合和自定义过渡,都能根据项目需求灵活运用,提升用户体验。