Windi CSS transition 过渡动画工具类
CSS 过渡的作用是让属性值的变化不要瞬间跳变,而是在一段时间里插值渐变。它由四个属性协作:transition-property 指定哪些属性参与过渡,transition-duration 定时长,transition-timing-function 定速度曲线,transition-delay 定延迟。Windi CSS 把这四个各自拆成一组工具类,按需组合。
关键点是过渡必须声明在元素的常态样式上,而不是变化后的状态上。也就是说 transition-all 要写在元素本身,不能写成 hover:transition-all。原因是浏览器读的是当前状态的过渡配置:鼠标移入时按常态的配置算入场,移出时同样按常态的配置算,配置写在 hover 里的话移出瞬间它就消失了,动画会突然断掉。
还有一点容易忽略:不是所有属性都能过渡。只有可以插值的属性才行,width、opacity、color 这些数值或颜色属性可以,display 从 none 到 block 没有中间态,过渡不生效。想做淡入淡出通常用 opacity 配合 visibility,或者用 max-height 代替 height: auto。
下面是四类工具类和对应 CSS 属性的对照:
| 简写 | 对应属性 |
|---|---|
transition-[all colors opacity shadow transform none] | transition-property |
duration-150 | transition-duration: 150ms |
ease-linear ease-in ease-out ease-in-out | transition-timing-function: linear |
delay-150 | transition-delay: 150ms |
transition-all 最省事但也最粗暴,它让所有可过渡的属性都参与进来,包括你没打算动的那些。副作用是可能出现意料之外的动画(比如布局变化时宽高也跟着渐变),而且浏览器要监听更多属性,性能开销大。生产代码里我更倾向于精确指定:只改颜色用 transition-colors,只做位移缩放用 transition-transform。后者尤其值得用——transform 和 opacity 的变化能走 GPU 合成层,不触发重排重绘,是最流畅的两个动画属性。
duration-* 和 delay-* 的数值单位是毫秒。可选档位在配置里定义,常见的有 75、100、150、200、300、500、700、1000。交互反馈类的动画建议控制在 150 到 300 毫秒之间,短于 100 毫秒人眼几乎察觉不到过渡过程,长于 400 毫秒就开始显得迟钝。
ease-* 控制速度曲线,linear 匀速、ease-in 慢起快收、ease-out 快起慢收、ease-in-out 两头慢中间快。UI 动效里 ease-out 用得最多,元素进场时先快后慢符合物理直觉,感觉上响应更快。linear 一般只用在持续旋转的加载图标上。
注意 duration 和 delay 只写一个数字时作用于所有过渡属性。如果只写了 transition-all 却没写 duration-*,Windi 有个默认时长(150ms),但显式写出来更可控。
示例
下面这个例子把过渡类和 hover: 状态组合起来,鼠标移入时背景色和透明度同时变化:
<template>
<div class="flex justify-around align-middle flex-wrap bg-black">
<div
:class="[
baseClass,
'hover:(bg-blue-500 opacity-50) transition-all delay-1000 duration-1000',
]"
>
transition-all
</div>
<div
:class="[
baseClass,
'hover:(bg-blue-500 text-black) transition-colors duration-1000',
]"
>
transition-colors
</div>
<div
:class="[
baseClass,
'hover:(opacity-50) transition-opacity duration-1000',
]"
>
transition-opacity
</div>
<div
:class="[
baseClass,
'hover:(bg-blue-500 transform scale-50) transition-transform duration-1000',
]"
>
transition-transform
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const baseClass = ref(
'w-150px h-150px leading-150px bg-red-500 m-auto text-white whitespace-nowrap',
);
</script>
四个方块分别演示了不同的 transition-property 取值,对比起来能看出各自的作用范围。
第一个 transition-all 同时过渡背景色和透明度,两个变化都平滑。注意它还加了 delay-1000,鼠标移入后要等一秒才开始动,这个延迟在实际交互中体验很差,这里只是为了把效果放慢便于观察。
第二个 transition-colors 只过渡颜色相关属性(color、background-color、border-color、fill、stroke)。这里 hover 改了背景色和文字色,两者都在覆盖范围内,所以都有过渡。如果 hover 里再加个 opacity-50,透明度会瞬间跳变——不在 colors 的范围里。
第三个 transition-opacity 只管透明度,最窄的范围。
第四个 transition-transform 只过渡变换。这里 hover 里同时写了 bg-blue-500,但背景色不在 transform 范围内,所以背景是瞬间变蓝、缩放是渐变的,两者节奏不同。想让背景也跟上得改成 transition-all,或者用 Windi 支持的多值写法。
hover 里的 transform scale-50 两个类要一起写,原因在 transform 那篇提过:Windi 用 CSS 变量实现变换叠加,scale-50 只是设置变量,必须有 transform 类声明那条引用变量的属性才生效。
hover:(...) 这种括号分组是 Windi CSS 的 variant group 语法,等价于给括号里每个类都加上 hover: 前缀,能少写很多重复前缀。Tailwind 原生不支持这个写法,UnoCSS 支持。迁移代码时这是个需要注意的差异点。

还没有留言,来发表第一条吧~