晃动动画应使用@keyframes定义,采用±2deg内小幅度非线性旋转,配合ease-in-out或自定义贝塞尔缓动,避免线性匀速导致生硬眩晕。

用 @keyframes 定义晃动关键帧,注意角度和缓动
晃动本质是小幅度、非线性旋转,不能直接用 rotate(5deg) → rotate(-5deg) 线性来回。人眼对匀速旋转敏感,容易显得生硬或眩晕。推荐用 ease-in-out 或自定义贝塞尔曲线,并控制最大偏转角在 ±2deg 以内。
常见错误是角度过大(比如 ±10deg)或周期太短(
@keyframes wiggle {
0%, 100% { transform: rotate(0); }
25% { transform: rotate(-1.2deg); }
50% { transform: rotate(1.2deg); }
75% { transform: rotate(-0.6deg); }
}
给卡片加 animation 时必须设 animation-fill-mode: forwards
否则动画播完会弹回初始状态(比如从 rotate(-0.6deg) 突然跳回 rotate(0)),破坏自然感。同时要禁用用户交互期间的重复触发,避免多次点击导致动画叠加卡顿。
-
animation: wiggle 1.2s ease-in-out 0.3s 1 forwards;—— 延迟 0.3s 启动,更符合“加载完成后的反馈”直觉 - 务必加
transform-origin: center;,否则旋转中心可能偏移(尤其卡片有 padding/margin 时) - 如果卡片本身用了
transform: scale(1.02)等其他变换,记得合并写法:transform: rotate(-1.2deg) scale(1.02);
只在页面加载时触发一次,别用 :hover 或 JS 反复绑定
目标是“首次加载动画”,不是交互反馈。用 CSS 最稳妥的方式是配合 HTML 的 class 流程:初始不加动画 class,JS 在 DOMContentLoaded 或 load 后添加一个 class(比如 is-loaded),再用 CSS 针对该 class 触发动画。
立即学习“前端免费学习笔记(深入)”;
这样比监听 IntersectionObserver 更轻量,也避免首屏外卡片误触发。示例结构:
.card { /* 基础样式 */ }
.card.is-loaded {
animation: wiggle 1.2s ease-in-out 0.3s 1 forwards;
}对应 JS 只需一句:document.querySelector('.card').classList.add('is-loaded');
移动端要注意 prefers-reduced-motion 降级处理
不少用户开启系统“减少运动”偏好,强制播放动画会引发不适。必须用媒体查询兜底:
@media (prefers-reduced-motion: reduce) {
.card.is-loaded {
animation: none;
transform: none; /* 清除可能残留的 rotate */
}
}另外,iOS Safari 对 transform 动画的渲染有时会闪白,可加 backface-visibility: hidden; 或 will-change: transform;(但后者慎用,避免过度触发合成层)。
真正难的是让晃动“像被轻轻推了一下”,而不是机械循环——节奏、幅度、缓动三者得一起调,改一次参数建议刷新页面看三遍效果。










