动画是交互设计中重要的视觉元素,页面如果缺少了动画,所有的变化都是闪现或闪退的,会显得俨乎其然而变得生硬无趣,少了许多生机。
页面上的交互动画主要都是一些偏线性的过渡效果,在操作与操作之间缓和过渡,以避免生硬冒失,唐突而出。
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 也是支持的,具体可以查看对应的文档说明