CSS 过渡(Transition)让属性值的变化变得平滑。无需 JavaScript,就能为悬停、聚焦等交互添加流畅的动画效果。
基本语法#
transition 简写#
.box { transition: property duration timing-function delay;}
/* 示例 */.button { background-color: #3b82f6; transition: background-color 0.3s ease;}
.button:hover { background-color: #2563eb;}单独属性#
.box { transition-property: background-color; transition-duration: 0.3s; transition-timing-function: ease; transition-delay: 0s;}transition-property#
指定哪些属性参与过渡:
/* 单个属性 */.box { transition-property: opacity;}
/* 多个属性 */.box { transition-property: opacity, transform, background-color;}
/* 所有属性 */.box { transition-property: all;}
/* 无过渡 */.box { transition-property: none;}🔶 all 会影响性能,推荐明确指定需要过渡的属性。
可过渡属性#
并非所有属性都能过渡。能过渡的属性需要有中间值:
/* 可以过渡 */opacity: 0 → 1 /* 数值 */width: 100px → 200px /* 长度 */color: red → blue /* 颜色 */transform: scale(1) → scale(1.1)
/* 不能过渡 */display: none → block /* 离散值 */visibility: hidden → visible /* 特殊处理 */transition-duration#
动画持续时间:
.fast { transition-duration: 0.15s;}.normal { transition-duration: 0.3s;}.slow { transition-duration: 0.5s;}
/* 毫秒单位 */.box { transition-duration: 300ms;}transition-timing-function#
控制动画的速度曲线:
预设函数#
.ease { transition-timing-function: ease;} /* 默认:慢-快-慢 */.linear { transition-timing-function: linear;} /* 匀速 */.ease-in { transition-timing-function: ease-in;} /* 慢-快 */.ease-out { transition-timing-function: ease-out;} /* 快-慢 */.ease-in-out { transition-timing-function: ease-in-out;} /* 慢-快-慢 */cubic-bezier#
自定义贝塞尔曲线:
.custom { /* cubic-bezier(x1, y1, x2, y2) */ transition-timing-function: cubic-bezier(0.68, -0.55, 0.265, 1.55);}常用自定义曲线:
/* 弹跳效果 */.bounce { transition-timing-function: cubic-bezier(0.68, -0.55, 0.265, 1.55);}
/* 快速开始 */.snap { transition-timing-function: cubic-bezier(0, 0.8, 0.2, 1);}
/* 平滑结束 */.smooth { transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);}steps#
步进动画:
.stepped { transition-timing-function: steps(4, start);}
.typing { transition-timing-function: steps(20, end);}transition-delay#
延迟开始时间:
.delayed { transition-delay: 0.2s;}
/* 负值:跳过部分动画 */.skip-start { transition-delay: -0.1s;}多属性过渡#
统一设置#
.box { transition: all 0.3s ease;}分别设置#
.box { transition: transform 0.3s ease, opacity 0.2s linear, background-color 0.5s ease-out;}交错动画#
.card { transition: transform 0.3s ease, opacity 0.3s ease 0.1s, /* 延迟 0.1s */ box-shadow 0.3s ease 0.2s; /* 延迟 0.2s */}实战案例#
按钮悬停#
.button { padding: 0.75rem 1.5rem; background-color: #3b82f6; color: white; border: none; border-radius: 6px; cursor: pointer; transition: background-color 0.2s, transform 0.2s, box-shadow 0.2s;}
.button:hover { background-color: #2563eb; transform: translateY(-2px); box-shadow: 0 4px 12px rgba(59, 130, 246, 0.4);}
.button:active { transform: translateY(0); box-shadow: 0 2px 4px rgba(59, 130, 246, 0.4);}卡片悬停#
.card { background: white; border-radius: 12px; overflow: hidden; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); transition: transform 0.3s ease, box-shadow 0.3s ease;}
.card:hover { transform: translateY(-8px); box-shadow: 0 12px 24px rgba(0, 0, 0, 0.15);}
.card-image { width: 100%; height: 200px; object-fit: cover; transition: transform 0.5s ease;}
.card:hover .card-image { transform: scale(1.05);}导航链接#
.nav-link { position: relative; padding: 0.5rem 0; color: #6b7280; text-decoration: none; transition: color 0.2s;}
.nav-link::after { content: ''; position: absolute; bottom: 0; left: 0; width: 0; height: 2px; background-color: #3b82f6; transition: width 0.3s ease;}
.nav-link:hover { color: #3b82f6;}
.nav-link:hover::after { width: 100%;}输入框焦点#
.input { width: 100%; padding: 0.75rem 1rem; border: 2px solid #e5e7eb; border-radius: 8px; outline: none; transition: border-color 0.2s, box-shadow 0.2s;}
.input:focus { border-color: #3b82f6; box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);}
.input:valid { border-color: #10b981;}
.input:invalid:not(:placeholder-shown) { border-color: #ef4444;}下拉菜单#
.dropdown { position: relative;}
.dropdown-menu { position: absolute; top: 100%; left: 0; min-width: 200px; padding: 0.5rem 0; background: white; border-radius: 8px; box-shadow: 0 10px 40px rgba(0, 0, 0, 0.15); opacity: 0; visibility: hidden; transform: translateY(-10px); transition: opacity 0.2s, transform 0.2s, visibility 0.2s;}
.dropdown:hover .dropdown-menu,.dropdown:focus-within .dropdown-menu { opacity: 1; visibility: visible; transform: translateY(0);}开关组件#
.switch { position: relative; width: 48px; height: 24px; background-color: #e5e7eb; border-radius: 12px; cursor: pointer; transition: background-color 0.2s;}
.switch::after { content: ''; position: absolute; top: 2px; left: 2px; width: 20px; height: 20px; background: white; border-radius: 50%; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); transition: transform 0.2s;}
.switch.active { background-color: #3b82f6;}
.switch.active::after { transform: translateX(24px);}性能优化#
使用 transform 和 opacity#
这两个属性可以被 GPU 加速,不会触发布局重排:
/* 好:使用 transform */.box { transition: transform 0.3s;}.box:hover { transform: scale(1.1);}
/* 避免:改变尺寸属性 */.box { transition: width 0.3s;}.box:hover { width: 110%;}will-change#
提示浏览器预先优化:
.animated { will-change: transform, opacity;}
/* 用完后移除 */.animated.done { will-change: auto;}🔶 不要滥用 will-change,只在需要时使用。
减少重排属性#
/* 触发重排的属性(避免过渡) */width, height, padding, margin, border-width, font-size, position
/* 只触发重绘的属性(较好) */color, background-color, box-shadow, border-color
/* 最佳性能的属性 */transform, opacity常见问题#
🤔 过渡不生效?
- 检查属性是否可过渡
- 确保初始值和目标值都有定义
display: none的元素无法过渡
🤔 如何过渡到 auto?
高度到 auto 不能直接过渡,使用 max-height 作为替代:
.collapsible { max-height: 0; overflow: hidden; transition: max-height 0.3s ease;}
.collapsible.open { max-height: 500px; /* 足够大的值 */}🤔 如何检测过渡结束?
element.addEventListener('transitionend', (e) => { console.log(`${e.propertyName} 过渡完成`)})过渡效果是提升用户体验的简单方式。合理使用过渡可以让界面更加流畅自然。下一篇我们将学习 CSS 动画,它能实现更复杂的动画序列。