5.1 KiB
5.1 KiB
CSS 动画配方
即开即用的 <ViewTransition> 属性 CSS,可复制到全局样式表中使用。
时间变量
:root {
--duration-exit: 150ms;
--duration-enter: 210ms;
--duration-move: 400ms;
}
共享关键帧
@keyframes fade {
from { filter: blur(3px); opacity: 0; }
to { filter: blur(0); opacity: 1; }
}
@keyframes slide {
from { translate: var(--slide-offset); }
to { translate: 0; }
}
@keyframes slide-y {
from { transform: translateY(var(--slide-y-offset, 10px)); }
to { transform: translateY(0); }
}
淡入淡出
::view-transition-old(.fade-out) {
animation: var(--duration-exit) ease-in fade reverse;
}
::view-transition-new(.fade-in) {
animation: var(--duration-enter) ease-out var(--duration-exit) both fade;
}
用法:<ViewTransition enter="fade-in" exit="fade-out" />
滑动(垂直方向)
::view-transition-old(.slide-down) {
animation:
var(--duration-exit) ease-out both fade reverse,
var(--duration-exit) ease-out both slide-y reverse;
}
::view-transition-new(.slide-up) {
animation:
var(--duration-enter) ease-in var(--duration-exit) both fade,
var(--duration-move) ease-in both slide-y;
}
用法:
<Suspense fallback={<ViewTransition exit="slide-down"><Skeleton /></ViewTransition>}>
<ViewTransition default="none" enter="slide-up"><Content /></ViewTransition>
</Suspense>
方向导航
分离进入/离开类
::view-transition-new(.slide-from-right) {
--slide-offset: 60px;
animation:
var(--duration-enter) ease-out var(--duration-exit) both fade,
var(--duration-move) ease-in-out both slide;
}
::view-transition-old(.slide-to-left) {
--slide-offset: -60px;
animation:
var(--duration-exit) ease-in both fade reverse,
var(--duration-move) ease-in-out both slide reverse;
}
::view-transition-new(.slide-from-left) {
--slide-offset: -60px;
animation:
var(--duration-enter) ease-out var(--duration-exit) both fade,
var(--duration-move) ease-in-out both slide;
}
::view-transition-old(.slide-to-right) {
--slide-offset: 60px;
animation:
var(--duration-exit) ease-in both fade reverse,
var(--duration-move) ease-in-out both slide reverse;
}
单类名方式
::view-transition-old(.nav-forward) {
--slide-offset: -60px;
animation:
var(--duration-exit) ease-in both fade reverse,
var(--duration-move) ease-in-out both slide reverse;
}
::view-transition-new(.nav-forward) {
--slide-offset: 60px;
animation:
var(--duration-enter) ease-out var(--duration-exit) both fade,
var(--duration-move) ease-in-out both slide;
}
::view-transition-old(.nav-back) {
--slide-offset: 60px;
animation:
var(--duration-exit) ease-in both fade reverse,
var(--duration-move) ease-in-out both slide reverse;
}
::view-transition-new(.nav-back) {
--slide-offset: -60px;
animation:
var(--duration-enter) ease-out var(--duration-exit) both fade,
var(--duration-move) ease-in-out both slide;
}
共享元素形变
::view-transition-group(.morph) {
animation-duration: var(--duration-move);
}
::view-transition-image-pair(.morph) {
animation-name: via-blur;
}
@keyframes via-blur {
30% { filter: blur(3px); }
}
用法:<ViewTransition name={product-${id}} share="morph" />
注意: 共享元素过渡会截取光栅快照。对于尺寸差异较大的文本(例如 <h3> → <h1>),旧快照会被放大,产生可见的鬼影伪影。文本共享元素请使用 text-morph。
文本形变
通过隐藏旧快照并以全分辨率显示新文本,避免文本的光栅缩放伪影:
::view-transition-group(.text-morph) {
animation-duration: var(--duration-move);
}
::view-transition-old(.text-morph) {
display: none;
}
::view-transition-new(.text-morph) {
animation: none;
object-fit: none;
object-position: left top;
}
用法:<ViewTransition name={title-${id}} share="text-morph" />
缩放
::view-transition-old(.scale-out) {
animation: var(--duration-exit) ease-in scale-down;
}
::view-transition-new(.scale-in) {
animation: var(--duration-enter) ease-out var(--duration-exit) both scale-up;
}
@keyframes scale-down {
from { transform: scale(1); opacity: 1; }
to { transform: scale(0.85); opacity: 0; }
}
@keyframes scale-up {
from { transform: scale(0.85); opacity: 0; }
to { transform: scale(1); opacity: 1; }
}
用法:<ViewTransition enter="scale-in" exit="scale-out" />
持久元素隔离
::view-transition-group(persistent-nav) {
animation: none;
z-index: 100;
}
背景模糊变通方案
对于使用了 backdrop-filter 的元素,隐藏旧快照以避免闪烁:
::view-transition-old(persistent-nav) {
display: none;
}
::view-transition-new(persistent-nav) {
animation: none;
}
减少动效
@media (prefers-reduced-motion: reduce) {
::view-transition-old(*),
::view-transition-new(*),
::view-transition-group(*) {
animation-duration: 0s !important;
animation-delay: 0s !important;
}
}