Skip to content

过渡效果

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

常见问题#

🤔 过渡不生效?

  1. 检查属性是否可过渡
  2. 确保初始值和目标值都有定义
  3. 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 动画,它能实现更复杂的动画序列。