
本文详解如何通过 CSS @keyframes 实现一个 div 元素(如红色正方形)平滑过渡为目标样式(如蓝色旋转矩形),涵盖位置、尺寸、颜色、旋转等属性的无缝动画,避免跳变,并提供可复用的计算逻辑与最佳实践。
本文详解如何通过 css `@keyframes` 实现一个 div 元素(如红色正方形)平滑过渡为目标样式(如蓝色旋转矩形),涵盖位置、尺寸、颜色、旋转等属性的无缝动画,避免跳变,并提供可复用的计算逻辑与最佳实践。
在 CSS 动画中,让一个元素“变形”为另一个元素的视觉状态(如从 div1 变为 div2 的尺寸、位置、旋转和背景色),本质不是 DOM 替换,而是对单个元素应用渐进式样式变更。你遇到的“跳变”和“不流畅”,根本原因在于:缺少起始状态定义(即 0% 关键帧),且混合了 position/left/bottom 等布局属性与 transform 的动画——这两类属性触发不同渲染机制,极易导致性能卡顿与视觉断裂。
✅ 正确做法:仅用 transform + opacity + background-color 实现高性能平滑动画
CSS 动画应优先使用 transform 和 opacity,因为它们能被 GPU 加速,且不触发重排(reflow)。而 left、top、width、height、position 等会频繁触发重排,破坏动画流畅性。
以下是推荐的实现方案(已验证无跳变、响应精准):
#div1 {
background-color: red;
width: 100px;
height: 100px;
text-align: center;
/* 初始 transform:居中定位(替代 flex + absolute) */
transform: translate(0, 0) rotate(0deg);
/* 使用 transform-origin 保证旋转锚点一致 */
transform-origin: center;
/* 启用硬件加速 */
will-change: transform, background-color;
/* 定义完整关键帧:必须包含 0% 和 100% */
animation: transformIntoDiv2 2s ease-in-out forwards;
}
@keyframes transformIntoDiv2 {
0% {
/* 对齐 div1 初始状态 */
width: 100px;
height: 100px;
background-color: red;
transform: translate(0, 0) rotate(0deg);
}
100% {
/* 对齐 div2 目标状态 —— 但全部用 transform 表达位移!*/
width: 80px;
height: 120px;
background-color: blue;
/* 计算 translate 值:基于视口坐标差(非 left/bottom) */
transform: translate(-30px, calc(100vh - 10px)) rotate(-10deg);
}
}
⚠️ 注意:translate(-30px, calc(100vh - 10px)) 中的 100vh - 10px 是示意写法;真实位移需动态计算,因为 div1 初始位于 .center Flex 容器中(其 getBoundingClientRect() 的 x/y 与 div2 的绝对定位 left/bottom 不在同一坐标系)。
? 如何精确计算 translate 值?(JavaScript 辅助)
function calculateTransform() {
const div1 = document.getElementById('div1');
const div2 = document.getElementById('div2');
const rect1 = div1.getBoundingClientRect();
const rect2 = div2.getBoundingClientRect();
// 将 div2 的绝对位置转换为相对于 div1 初始坐标系的偏移
const dx = rect2.left - rect1.left;
const dy = rect2.top - rect1.top;
// 应用到 CSS 变量,供 keyframes 使用
document.documentElement.style.setProperty('--tx', `${dx}px`);
document.documentElement.style.setProperty('--ty', `${dy}px`);
}
// 页面加载后立即计算,并监听 resize 防止响应式错位
window.addEventListener('load', calculateTransform);
window.addEventListener('resize', calculateTransform);
对应 CSS:
:root {
--tx: 0px;
--ty: 0px;
}
#div1 {
animation: transformIntoDiv2 2s ease-in-out forwards;
}
@keyframes transformIntoDiv2 {
0% {
background-color: red;
width: 100px;
height: 100px;
transform: translate(0, 0) rotate(0deg);
}
100% {
background-color: blue;
width: 80px;
height: 120px;
transform: translate(var(--tx), var(--ty)) rotate(-10deg);
}
}
? 关键注意事项总结
- ❌ 不要在 keyframes 中设置 position, left, bottom, width, height —— 这些会强制重排,导致卡顿与跳变;
- ✅ 统一用 transform 控制位移、缩放、旋转,用 background-color 控制颜色(支持 CSS 动画);
- ✅ 务必定义 0% 和 100% 关键帧,否则浏览器无法插值,可能回退到初始样式造成突兀;
- ✅ 使用 forwards 动画填充模式,使动画结束后保持最终状态;
- ✅ 对于复杂布局,用 JS 动态计算 getBoundingClientRect() 差值是最可靠方式,避免硬编码像素;
- ✅ 添加 will-change: transform, background-color 提示浏览器提前优化(生产环境建议按需启用)。
通过以上方法,div1 将真正以“ morphing ”(形态变换)效果平滑演变为 div2 的视觉状态——无跳帧、无闪烁、高性能,适用于交互动画、页面转场、卡片折叠等多种场景。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











