Svelte系列 --- 动画

858 阅读2分钟

动画是交互设计中重要的视觉元素,页面如果缺少了动画,所有的变化都是闪现或闪退的,会显得俨乎其然而变得生硬无趣,少了许多生机。

页面上的交互动画主要都是一些偏线性的过渡效果,在操作与操作之间缓和过渡,以避免生硬冒失,唐突而出。

Svelte中的交互动画可以大致分为三类: 运动效果(motion)、过渡效果(transition)和动画(animate)

所有Svelte中支持的动效,可以在Svelte的对应包中进行查看

motion

补间动画(Tweened)

tweened最常用于频繁变化的值

import { tweened } from 'svelte/motion';
const $progress = tweened(0); // tweened(0)返回的是一个store值
<script>
  import { tweened } from 'svelte/motion';
  // svelte支持的渐进执行效果 --> https://unpkg.com/svelte@3.38.3/easing/index.mjs
  import { cubicOut } from 'svelte/easing';
  
  // 可以给tweened方法提供第二个参数 为补间动画的配置项
  const progress = tweened(0, {
    delay: 300, // 单位 ms
    duration: 400, //  执行时间  单位 ms  一般推荐在 300 ~ 400ms为佳
    easing: cubicOut //  渐进执行效果
  });
</script>

弹簧动画 (Spring)

<script>
  import { spring } from 'svelte/motion';

  let $size = spring(10); // spring返回的也是一个store
  
  // spring有第二个参数对spring的动画效果进行配置,如果没有那么就采用默认值
  let coords = spring({ x: 50, y: 50 }, {
    stiffness: 0.1, // 刚度 [0, 1]  值越小,移动越困难
    damping: 0.25 // 阻尼系数 [0, 1] 值越小,弹簧效果越明显
  });
</script>

transition

transition 指令 一般用于元素如何如何优雅的进入或离开舞台

过渡效果是可逆的,如果在过渡效果进行期间,切换了复选框,它将从当前位置开始过渡而不是起点或终点位置过渡

fade

<script>
  import { fade } from 'svelte/transition';
  let visible = true;
</script>

<p transition:fade>淡入淡出效果展示</p>

fly

<script>
  import { fly } from 'svelte/transition';
  let visible = true;
</script>

<!-- fly可以认为添加了参数的fade动效 -->
<p transition:fly={{ y: 200, duration: 2000 }}> 飞入效果展示 </p>

in 和 out

<script>
	import { fade, fly } from 'svelte/transition';
</script>

<!-- 进入或离开的时候,执行不同的动效 -->
<!-- 如果进入的动效和离开的动效分开,过渡效果就是 不可逆 的了 -->
<p in:fly={{ y: 200, duration: 2000 }} out:fade>
  飞入、淡出效果展示
</p>

自定义 CSS 过渡

// 这是内置fade函数的实现代码
function fade(node, { delay = 0, duration = 400 }) {
  const o = +getComputedStyle(node).opacity;

  // t 是一个进度 值为 [0, 1]
  return { delay, duration, css: t => `opacity: ${t * o}` };
}

Svelte '模拟' 这个过渡效果并构建一个 CSS 动画,接着使其运行起来。

比方说,fade 过渡效果会生成类似以下 CSS 动画:

0% { opacity: 0 }
10% { opacity: 0.1 }
20% { opacity: 0.2 }
/* ... */
100% { opacity: 1 }

我们可以使用这个特征来自定义一些css动画

<script>
  // 这个函数是一个callback
  // 参数1: 添加动画的dom节点
 	// 参数2: 配置项
	function spin(node, { duration }) {
    // 返回一个过渡效果的对象
    // 这个对象可以有 delay, duration, css
		return {
			duration,
			css: t => { // 一个 `(t, u) => cs代码` 的函数,其中 u === 1 - t
        // 让t的值不在是简单的从0到1,而是根据不同的补间动画函数,产生不同的进度范围和进度值,以产生不同的效果
				const eased = elasticOut(t); 
        
				// 返回css代码
				return `transform: scale(${eased});`
			}
		};
</script>

过渡效果事件

事件名功能
introstart进入效果开始
introend进入效果结束
outrostart退出效果开始
outroend退出效果结束
<p
  transition:fly="{{ y: 200, duration: 2000 }}"
  on:introstart="{() => status = 'intro started'}" 
  on:outrostart="{() => status = 'outro started'}"
  on:introend="{() => status = 'intro ended'}"
  on:outroend="{() => status = 'outro ended'}"
>
  Flies in and out
</p>

局部过渡(Local transitions)

在进行元素切换的时候,如果切换的父元素的子元素中存在过度效果,

那么在切换父元素的时候,子元素的过渡效果也会依次被执行

有时候我们仅希望添加或删除子元素时, 才开始执行过渡效果。

<script>
  import { slide } from 'svelte/transition';

  let showItems = true;
  let i = 5;
  let items = [
    'one', 'two', 'three', 'four', 'five', 
    'six', 'seven', 'eight', 'nine', 'ten'
  ];
</script>

<style>
  div { padding: 0.5em 0; border-top: 1px solid #eee; }
</style>

<label>
  <input type="checkbox" bind:checked={showItems}>
  show list
</label>

<label>
  <input type="range" bind:value={i} max=10>
</label>

{#if showItems}
  {#each items.slice(0, i) as item}
		<!-- 
			可以通过 local 过渡来实现,这个 local 指示过渡效果仅在添加或删除父元素时才会执行
			也就是在添加item的时候,动效才会执行,直接切换整个列表的显示和隐藏是不会触发item的动效的
		-->

		<!-- 可以尝试通过移除或添加local属性来查看区别 -->
    <div transition:slide|local>{item}</div>
  {/each}
{/if}

Svelte 无法为各种各样的动画提供预设支持,因此如果某些效果特别引人入胜,你可以通过自定义 CSS 过渡效果来包装它们,有些还需要配合 JS 创建的效果,Svelte 也是支持的,具体可以查看对应的文档说明