跳转到内容

动效

动效和系统的其他部分一样安静。变的是颜色和透明度,东西很少移动,也不会缩放、弹跳或回弹。

四种时长:--motion-fast 用于控件的颜色变化,--motion-base 用于底色变化,--motion-slow 用于形状变化(例如一段内容折叠),--motion-entrance 只属于对话框出现和切换主题,别处不用。三条曲线:--ease-enter 用于出现,--ease-exit 用于消失,--ease-move 用于原地改变大小。

motion.css 把它们组合成四种过渡:--transition-tint、-surface、-fade 和 -collapse。先用这几个,再考虑手写时长。

Token值说明
--motion-fastmotion.fast120ms控件的颜色变化:按下、切换标签、链接。
--motion-basemotion.base160ms表面变化:导航栏变实、关闭、列表项进入。
--motion-slowmotion.slow240ms几何变化:高度折叠、侧栏重排。
--motion-entrancemotion.entrance350ms只用于对话框出现和切换主题时的颜色过渡。
--motion-shiftmotion.shift8px系统里唯一的位移距离。
--ease-enterease.entercubic-bezier(0, 0, 0.2, 1)进入或停稳。
--ease-exitease.exitcubic-bezier(0.4, 0, 1, 1)离开。
--ease-moveease.movecubic-bezier(0.4, 0, 0.2, 1)原地改变大小或位置。

所有会变的列表都用三个类:.ds-reveal-in(从下方 8px 淡入)、.ds-fade-out(只改透明度)和 .ds-collapse(改高度)。一个列表有增有减时按固定顺序:要走的先淡出,剩下的移动位置,新来的淡入。--motion-shift 是唯一的位移距离,只在淡入时用。

没有滚动触发的出现、没有视差,也没有骨架屏闪光。换页时的淡入淡出见页面切换,由网站自己引入。唯一循环的动画是不确定进度条,它总带着文字说明。

主题是每页一个的 store:@rottensea/ds-personal-website/scripts/theme 里的 themeStore(),只有它能改主题。它存着读者的选择(浅色、深色、跟随系统)和实际用的主题,由它写 data-theme、把选择存进 localStorage、加渐变。选择是跟随系统时它跟着系统变,网站的别的标签页改了选择时它也跟着变。ThemeToggle 和 SettingsMenu 用 set() 提出切换;SettingsMenu、Mermaid 和 SharePoster 用 subscribe() 订阅,所以面板上显示的总是当前的选择,主题真的变了图才重画,每次只画一次。

import { themeStore } from "@rottensea/ds-personal-website/scripts/theme";
const theme = themeStore();
theme.set("dark");
theme.subscribe((state, previous) => {
if (state.theme !== previous.theme) redraw();
});

每次切换,页面上所有颜色都一起过渡,用 --motion-entrance 和 --ease-move。store 在 <html> 上标记 data-theme-fade,持续这么久。标记在的时候,base.css 让每个元素都用同一个颜色过渡,替换它自己的,所以按钮悬停用的颜色过渡不会按自己的节奏走。脚本画出来的东西不是 CSS 颜色:Mermaid 图和 SharePoster 会按新主题重画,再用同一个模块里的 fadeIn 让新图在旧图上淡入。

脚本自己直接写 data-theme 时,store 照样当成一次切换,加渐变、通知订阅者,开发模式下在控制台提示。如果那段脚本写完马上读样式,渐变会来不及,所以正路是 set()。

两种情况下动效都缩短到 1ms:系统设置里的 prefers-reduced-motion,以及网站在 <html> 上设的 data-reduce-motion="true"。任何时候都可能开着其中一个,所以动效只用来确认操作,不单独传达信息。

有两个背景在这两种情况下都照样在动:Starfield 和 DotGlobe。它们漂得又慢又匀,没有闪动和跳动,网站也希望它们一直是活的。