交互动效设计

网页动效的核心是:让页面元素”动起来”,从而引导视线、反馈操作、增强体验。

动效本质上是改变元素的某个 CSS 属性,并控制它变化的节奏。实现手段主要有两条路线:

  • 过渡 transition:由状态变化(如 :hover:focus、类名切换)触发,平滑地从一个值过渡到另一个值。
  • 动画 animation:定义好关键帧后自动播放,可以无限循环、延迟、反向等。

配合 transform 变换filter 滤镜clip-path 裁剪SVG 描边动画滚动驱动动画等,就能做出各类炫酷效果。

这篇笔记覆盖 2D/3D 变换、过渡、动画、逐帧动画、流光边框、3D 相册与导航、SVG 描边动画、裁剪、滤镜、CSS 变量与 calc、滚动时间线等动效相关知识点,适合快速复习。

一句话记忆transition 是”从 A 过渡到 B”(需要触发),animation 是”自己会动”(自动播放)。

一、过渡 transition

1. 基本语法

1
2
3
4
5
.box {
transition: 属性名 时长 速度曲线 延迟时间;
/* 常用简写:所有可过渡属性,0.3 秒完成 */
transition: all 0.3s;
}
组成部分 说明 常用值
属性名 要过渡的 CSS 属性 all / transform / opacity / color
时长 过渡持续多久 0.3s500ms
速度曲线 变化快慢节奏 ease(默认)、linearcubic-bezier()
延迟时间 多久后才开始 0s1s

2. 谁触发过渡

过渡不会自己发生,需要状态改变,最常见的是 :hover

1
2
3
4
5
6
7
.box {
width: 200px;
transition: all 0.3s;
}
.box:hover {
width: 300px; /* 鼠标移入时宽度平滑变化 */
}

3. 哪些属性可以过渡

  • 可过渡:数值类属性(widthheightcoloropacitytransformbackground-colormarginleftfilter 等)。
  • 不可过渡:displaybackground-image(渐变/图片切换)、position 等离散属性。

⚠️ 易忘点:想让元素”淡入淡出”不要过渡 display,改用 opacityvisibility

二、transform 2D 变换

transform 用于改变元素的形状、位置、角度,不改变元素在文档流中的占位,不影响布局。

1. 位移 translate

1
2
3
transform: translate(200px, 200px); /* 水平、垂直 */
transform: translateX(200px); /* 只水平 */
transform: translateY(200px); /* 只垂直 */
  • 位移以元素自身位置为基准。
  • 百分比相对于自身宽高(常用于垂直居中:translateY(-50%))。

2. 旋转 rotate

1
transform: rotate(360deg); /* 正值顺时针,负值逆时针 */

配合 transform-origin 改变旋转中心:

1
2
3
4
5
6
img {
transform-origin: right bottom; /* 以右下角为轴旋转,默认是中心点 center */
}
img:hover {
transform: rotate(360deg);
}

transform-origin 取值:关键字(center/left/right/top/bottom)、像素、百分比。

3. 缩放 scale

1
2
transform: scale(1.5);   /* 等比放大 1.5 倍 */
transform: scale(1.5, 1);/* 水平 1.5 倍、垂直不变 */
  • 参数是比例1 表示原大小,小于 1 缩小、大于 1 放大。
  • 同样不影响布局占位

4. 斜切 skew

1
2
transform: skewX(-16deg); /* 水平斜切 */
transform: skewY(8deg); /* 垂直斜切 */

常用于做出倾斜的文字标签、卡片侧边等。

5. 多组变换与顺序

多个变换用空格分隔,写在一个 transform 里:

1
transform: translateX(600px) rotate(360deg);

⚠️ 易错点:多个变换按书写顺序依次作用,顺序不同结果可能不同。例如 rotate(45deg) translateX(100px) 会沿旋转后的方向移动(斜着走),而 translateX(100px) rotate(45deg) 是先平移再原地旋转。不要把多个变换拆成多个 transform 声明——后面的会覆盖前面的。

三、transform 3D 变换

3D 变换让元素在三维空间中翻转,需要理解”透视”。

1. 三维旋转 rotateX / rotateY / rotateZ

1
2
3
transform: rotateX(180deg); /* 绕 X 轴(水平轴)翻转,像翻书上下翻 */
transform: rotateY(180deg); /* 绕 Y 轴(垂直轴)翻转,像翻书左右翻 */
transform: rotateZ(180deg); /* 绕 Z 轴,等同于 2D 的 rotate */

2. 透视 perspective

不加透视时,3D 旋转看不出立体感。透视有两种写法:

1
2
3
4
5
6
7
8
9
/* 写法一:写在父元素上,作用于其所有子元素 */
.box {
perspective: 1000px;
}

/* 写法二:写在 transform 里,且必须放在最前面 */
img {
transform: perspective(500px) rotateY(40deg);
}

⚠️ 记忆点:透视数值越小,观察者离得越近,立体感越强;数值越大越平缓。常见取值 500px~1200px。

3. 背面隐藏 backface-visibility

翻转图片时,默认会看到背面的镜像。加 backface-visibility: hidden 隐藏背面:

1
2
3
.box > div {
backface-visibility: hidden;
}

4. 开启 3D 空间 transform-style

1
2
3
.box {
transform-style: preserve-3d; /* 让子元素保留 3D 空间,默认是 flat 扁平 */
}

5. 3D 位移 translateZ / translate3d

1
2
transform: translateZ(60px);             /* 沿 Z 轴(面向屏幕方向)突出 */
transform: translate3d(0, 0, 60px); /* x y z 三个方向 */

6. 两面翻转图片(经典案例)

1
2
3
4
5
6
7
8
9
10
11
12
.box {
perspective: 1000px;
}
.box .front, .box .back {
backface-visibility: hidden;
transition: all 0.7s;
}
.box .back {
transform: rotateY(180deg); /* 背面先翻过去 */
}
.box:hover .front { transform: rotateY(-180deg); }
.box:hover .back { transform: rotateY(0deg); }

思路:正面和背面绝对定位叠在一起,背面预先转 180° 藏起来,hover 时两者一起翻转。

四、动画 animation

1. 定义关键帧 @keyframes

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* 两步写法 */
@keyframes move {
from { transform: translate(0, 0); }
to { transform: translate(300px, 600px); border-radius: 50%; }
}

/* 多关键帧写法(百分比) */
@keyframes move2 {
0% { transform: translate(0, 0); }
25% { transform: translate(300px, 0); }
50% { transform: translate(300px, 600px); }
75% { transform: translate(0, 600px); }
100% { transform: translate(0, 0); }
}

2. 使用动画

1
2
3
.box {
animation: move 4s; /* 动画名 + 时长 */
}

3. 完整写法(7 个子属性)

1
2
animation: 名称 时长 速度曲线 延迟时间 播放次数 播放方向 结束状态;
animation: rotate 2s linear 0s infinite normal forwards;
子属性 作用 常用值
animation-name 动画名称 @keyframes 定义的名字
animation-duration 时长 2s500ms(默认 0s,不写则无效)
animation-timing-function 速度曲线 ease(默认)、linearsteps()cubic-bezier()
animation-delay 延迟 1s负值表示提前开始(跳过前面片段)
animation-iteration-count 播放次数 数字、infinite(无限循环)
animation-direction 播放方向 normalreversealternate(交替)、alternate-reverse
animation-fill-mode 结束状态 noneforwards(停在最后一帧)、backwardsboth

4. 速度曲线 timing-function

效果
linear 匀速
ease 先慢后快再慢(默认)
ease-in 慢速开始,越来越快
ease-out 快速开始,越来越慢
ease-in-out 慢速开始和结束,中间快
steps(n) 分 n 步瞬间跳变(逐帧动画)
cubic-bezier(x1,y1,x2,y2) 自定义贝塞尔曲线

⚠️ 易忘点alternate 表示奇数遍正向、偶数遍反向,配合 infinite 做出”来回摆动”效果,如上下浮动的气泡。

5. 暂停动画 animation-play-state

1
2
3
4
.scroll { animation: move 5s linear infinite; }
.box:hover .scroll {
animation-play-state: paused; /* 鼠标悬停暂停轮播 */
}

6. 逐帧动画 steps()

steps(n) 让动画在 n 个瞬间跳变,常用于雪碧图逐帧动画

1
2
3
4
5
6
7
8
@keyframes move {
0% { background-position: 0 0; }
100% { background-position: -13700px 0; }
}
.box {
background: url(./img/step.png) no-repeat left / 13700px 513px;
animation: move 1s steps(25) infinite;
}

思路:把一长条连续的帧图片设为背景,动画只改变 background-position,用 steps(25) 让 25 帧逐帧跳变,形成动画。

7. 多个动画同时播放

逗号分隔即可:

1
animation: move 5s linear infinite, rotate 30s linear infinite alternate;

五、动效常用技巧与案例

1. 流光渐变边框按钮

思路:一个渐变背景的伪元素不断平移,外层 overflow: hidden 只露出边框一圈。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.btn {
position: relative;
overflow: hidden;
padding: 3px; /* 露出的边框厚度 */
}
.btn::before {
content: '';
position: absolute;
width: 200%; /* 足够宽才能平移 */
height: 100%;
background: linear-gradient(115deg, #4fcf70, #fad648, #a767e5, #12bcfe, #44ce7b);
background-size: 50% 100%;
animation: move 1s linear infinite;
}
@keyframes move {
to { transform: translateX(-50%); }
}

另一种用 conic-gradient(圆锥渐变)旋转做出光晕边框,再用 ::after + inset 盖住中心:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.btn2::before {
content: '';
position: absolute;
width: 500px;
height: 500px;
background-image: conic-gradient(transparent, transparent, transparent, #00ccff);
animation: rotate 1s linear infinite;
}
.btn2::after {
content: '';
position: absolute;
inset: 3px; /* 相当于 top/right/bottom/left 都设 3px */
background-color: #000;
border-radius: 10px;
}

记忆点:insettop/right/bottom/left 四值的简写;conic-gradient 是绕中心旋转的圆锥渐变,适合做流光。

2. 卡片折叠(:has() 选择器)

鼠标悬停某张卡片时,用 :has() 反向控制父容器,让悬停的卡片变宽:

1
2
3
4
5
6
ul {
display: grid;
grid-template-columns: 2fr 1fr 1fr 1fr;
transition: all .5s;
}
ul:has(.item:nth-child(2):hover) { grid-template-columns: 1fr 2fr 1fr 1fr; }

ul:has(子选择器) 表示”包含某个状态子元素的 ul”,从而让父元素响应子元素的 hover,非常实用。

3. 图片轮播(无缝滚动)

思路:放两组完全相同的内容,整体 translateX 移动一组宽度后瞬间回到起点,视觉上无缝循环;hover 暂停。

1
2
3
4
5
6
7
8
9
.scroll {
display: flex;
animation: move 5s linear infinite;
}
@keyframes move {
0% { transform: translateX(0); }
100% { transform: translateX(-1200px); }
}
.box:hover .scroll { animation-play-state: paused; }

4. 3D 悬停相册(倒影 + 兄弟选择器)

1
2
3
4
5
6
7
8
9
10
11
12
13
.item {
-webkit-box-reflect: below 1px linear-gradient(transparent, #0002); /* 倒影 */
}
.item:hover { transform: scale(1.2); z-index: 1; }

/* 悬停时,其余所有卡片变小并侧转 */
.box:hover .item:not(:hover) {
transform: perspective(500px) scale(0.9) rotateY(45deg);
}
/* 悬停卡片后面的兄弟朝反方向转 */
.box .item:hover ~ .item {
transform: perspective(500px) scale(0.9) rotateY(-45deg);
}

要点:

  • -webkit-box-reflect:倒影,参数为(位置 above/below + 偏移 + 渐变)。
  • :not(:hover):排除当前悬停元素,选中其余元素。
  • ~后续兄弟选择器,选中之后的所有兄弟。
  • 悬停元素要加 z-index: 1 盖住其他卡片。

5. 3D 导航(preserve-3d)

每个菜单项有正面和底面两个面,hover 时绕 X 轴翻转:

1
2
3
4
5
6
ul li {
transform-style: preserve-3d; /* 关键:开启子元素 3D 空间 */
}
ul li .front { transform: translateZ(20px); }
ul li .bottom { transform: translateY(20px) rotateX(-90deg); }
ul li:hover { transform: rotateX(90deg); }

六、SVG 与描边动画

1. SVG 基础

  • SVG 是矢量图,放大不失真,常用来做图标。
  • 通过 CSS 的 width / height 控制大小(color 对 SVG 无效)。
  • viewBox 定义坐标系统,pathd 属性描述路径。
1
2
3
<svg viewBox="0 0 1024 1024" width="200" height="200">
<path d="M512 928A416 ..." />
</svg>

2. 描边相关属性

1
2
3
4
5
svg {
fill: none; /* 填充颜色,none 为不填充 */
stroke: blue; /* 描边颜色 */
stroke-width: 3px; /* 描边宽度 */
}
属性 作用
fill 填充颜色(路径内部)
stroke 描边颜色(路径线条)
stroke-width 描边宽度
stroke-dasharray 虚线模式:”实线长度 空白长度”
stroke-dashoffset 虚线起始偏移,增大表现为”擦除”

3. 描边动画(画出来的效果)

核心是 stroke-dasharraystroke-dashoffset

1
2
3
4
5
6
7
8
9
10
11
svg {
fill: none;
stroke: red;
stroke-dasharray: 2783; /* 设为路径总长度 */
stroke-dashoffset: 2783; /* 初始也等于总长度 → 完全隐藏 */
animation: heart 5s linear infinite;
}
@keyframes heart {
0% { stroke-dashoffset: 2783; } /* 全被擦除,看不见 */
100% { stroke-dashoffset: 0; } /* 逐渐画出 */
}

⚠️ 记忆点stroke-dashoffset 越大,线条被”擦除”得越多。把 dasharraydashoffset 都设成路径总长度,再把 offset 从总长度动画到 0,就实现”一笔画”效果。路径总长度可用 JS getTotalLength() 获取,或直接估一个足够大的值。

4. SVG 拆分动画

SVG 里可用 <g> 把多个图形分组,对分组加 transform(SVG 元素用 transform 属性而不是 CSS transform):

1
2
3
<g class="drone" transform="translate(42 54)">
<!-- 多个 path -->
</g>
1
2
3
4
5
6
.drone { animation: move 10s linear infinite; }
@keyframes move {
0% { transform: translate(0); }
50% { transform: translate(200px); }
100% { transform: translate(0); }
}

七、裁剪 clip-path

clip-path 用一个形状”裁剪”元素,只显示形状内的部分。

1. polygon 多边形

1
clip-path: polygon(10% 0, 100% 1%, 90% 100%, 0% 100%);
  • 参数是若干坐标点(x% y%),按顺序连线成多边形。
  • 常配合 transition 让裁剪区域平滑变化(图片切换效果)。

2. circle 圆形

1
clip-path: circle(100px at 0% 50%); /* 半径 100px,圆心在左侧垂直居中 */

3. 文字裁剪 background-clip: text

让渐变”填充”文字:

1
2
3
4
5
6
7
8
9
10
h1 { color: #292929; position: relative; }
h1::after {
content: "HELLOWORLD";
color: transparent; /* 文字本身透明 */
background: linear-gradient(to right, #ff69b3, #fe0000, #ffff01, #9400d4);
background-clip: text; /* 背景按文字裁剪 */
-webkit-background-clip: text;
clip-path: circle(100px at 0% 50%); /* 再用 clip-path 做扫光 */
animation: move 2s linear infinite;
}

八、滤镜 filter 与背景滤镜 backdrop-filter

1. filter 常见函数

1
2
3
4
5
6
7
8
filter: blur(5px);              /* 高斯模糊 */
filter: brightness(150%); /* 亮度 */
filter: contrast(150%); /* 对比度 */
filter: saturate(150%); /* 饱和度 */
filter: grayscale(100%); /* 灰度(变黑白) */
filter: hue-rotate(90deg); /* 色相旋转 */
filter: sepia(100%); /* 褐色复古 */
filter: drop-shadow(5px 5px 5px red); /* 投影 */
函数 作用
blur() 高斯模糊,数值越大越糊
brightness() 亮度,100% 为原值
contrast() 对比度
saturate() 饱和度,0 变灰
grayscale() 灰度,100% 黑白
hue-rotate() 色相旋转
sepia() 复古褐色
drop-shadow() 投影

2. drop-shadow 与 box-shadow 区别

  • box-shadow:给盒子加阴影(矩形)。
  • drop-shadow:按元素实际形状/透明区域轮廓加阴影,对带透明背景的 PNG、SVG 更真实。

3. 背景滤镜 backdrop-filter(毛玻璃)

backdrop-filter 作用于元素背后的内容,实现毛玻璃/磨砂效果:

1
2
3
4
5
.card {
background: rgba(255, 255, 255, 0.4);
backdrop-filter: blur(18px); /* 背后内容模糊 */
backdrop-filter: saturate(150%) blur(28px); /* 组合使用 */
}

记忆点:filter 模糊元素自己backdrop-filter 模糊元素背后的内容。毛玻璃 = 半透明背景 + backdrop-filter blur。

九、CSS 变量 var 与计算函数 calc

1. 变量定义与使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/* 全局变量,定义在 :root 上,全站可用 */
:root {
--color: blue;
--gap: 16px;
}

.box {
color: var(--color);
border: 1px solid var(--color);
}

/* 局部变量,定义在某个元素上,只对该元素及其子元素生效 */
.nav {
--fz: 20px;
}
  • 变量名以 -- 开头,用 var(--变量名) 引用。
  • 可加默认值:var(--fz, 16px)(变量不存在时用 16px)。

2. calc 计算函数

1
2
3
.item {
width: calc(33.3333333% - var(--gap)); /* 计算:三等分减间距 */
}
  • calc()+- 两边要加空格*/ 可不加)。
  • 可混合单位,如 calc(100% - 20px)

3. var + calc 的典型应用

① 精灵图定位:用内联变量 --i 计算背景位置,逐行偏移:

1
2
3
ul li {
background: url(./img/sprite.png) no-repeat 0 calc(var(--i) * -58px);
}
1
2
3
<li style="--i:0;"></li>
<li style="--i:1;"></li>
<li style="--i:2;"></li>

② 交错动画延迟:每个元素延迟不同,形成依次出现效果:

1
2
3
ul li {
transition: all 0.5s calc(0.1s * var(--i));
}

③ 扇形展开角度:每个子项转不同角度散开:

1
2
3
4
5
6
.menu:hover span {
transform: rotate(calc(45deg * var(--i))) translateX(80px);
}
.menu:hover span i {
transform: rotate(calc(-45deg * var(--i))); /* 反向转回,保持图标正立 */
}

记忆点:--i 通常是写在内联 style 里按序递增的自定义索引,配合 calc() 批量生成规律性数值,是动效里非常常见的套路。

十、滚动驱动动画 animation-timeline

让动画的播放进度由滚动控制,而非时间。

1. scroll() 滚动时间线

动画进度绑定页面滚动距离:

1
2
3
4
5
6
7
8
9
.scrollbar {
width: 0%;
animation: grow 2s linear forwards;
animation-timeline: scroll(); /* 由滚动驱动,替代时间 */
}
@keyframes grow {
0% { width: 0%; }
100% { width: 100%; }
}

常用于页面顶部的阅读进度条

2. view() 视图时间线

动画进度绑定元素进入/离开视口的过程:

1
2
3
4
5
6
7
8
.box img {
animation: anim 1s linear forwards;
animation-timeline: view(); /* 元素滚入视口时触发 */
}
@keyframes anim {
0% { transform: scale(1); opacity: 0.2; }
100% { transform: scale(1.5); opacity: 1; }
}

3. 配合 position: sticky 做卡片堆叠

1
2
3
4
5
6
7
8
9
10
.main ul li {
position: sticky; /* 卡片吸顶,下一张叠上来 */
top: 0;
animation: scale 1s ease-in-out forwards;
animation-timeline: view();
}
@keyframes scale {
0% { transform: scaleX(1); }
100% { transform: scaleX(0.8); } /* 滚动时轻微压缩 */
}

⚠️ 注意:animation-timeline: scroll()/view() 是较新特性,浏览器兼容性有限,使用时需考虑降级(不影响核心功能)。

十一、单位与独立变换属性

1. vw / vh 视口单位

1
2
3
4
.box {
width: 50vw; /* 视口宽度的 50% */
height: 50vh; /* 视口高度的 50% */
}
  • vw:相对视口宽度100vw = 整个视口宽。
  • vh:相对视口高度100vh = 整个视口高。
  • 常用于整屏布局、响应式字号:font-size: calc(1em + 0.5vw)

2. turn 角度单位

1
2
transform: rotate(0.5turn); /* 半圈 = 180deg */
transform: rotate(1turn); /* 一整圈 = 360deg */

1turn = 360deg,写整圈时比 360deg 更直观。

3. 独立变换属性(新特性)

除了 transform 组合写法,还有独立的 translate / rotate / scale

1
2
3
img {
scale: 1.1; /* 等同于 transform: scale(1.1) */
}

三者同时使用时,会按固定顺序组合,且不会相互覆盖(普通 transform 多个声明会覆盖)。

十二、速查表

需求 写法
平滑过渡 transition: all .3s;
水平/垂直位移 transform: translateX(100px) / translateY(-50%)
旋转 transform: rotate(45deg)
缩放 transform: scale(1.2)
斜切 transform: skewX(-16deg)
旋转中心 transform-origin: right bottom;
3D 翻转 transform: rotateY(180deg);
透视 父元素 perspective: 1000px;
隐藏背面 backface-visibility: hidden;
开启 3D 空间 transform-style: preserve-3d;
定义动画 @keyframes 名 { from{} to{} }
使用动画 animation: 名 2s linear infinite;
逐帧动画 animation: 名 1s steps(25) infinite;
停在最后一帧 animation-fill-mode: forwards;
暂停动画 animation-play-state: paused;
流光边框 conic-gradient + ::before 旋转 + overflow: hidden
父级响应子级 hover ul:has(.item:hover)
排除当前元素 .item:not(:hover)
后续兄弟 .item:hover ~ .item
SVG 描边 stroke / stroke-width / fill: none
一笔画动画 stroke-dasharray + stroke-dashoffset 动画
裁剪 clip-path: polygon(...) / circle(...)
文字渐变 background-clip: text + color: transparent
图片模糊 filter: blur(5px)
毛玻璃 backdrop-filter: blur(18px)
倒影 -webkit-box-reflect: below 1px linear-gradient(transparent,#0002)
全局变量 :root { --x: 值 } + var(--x)
计算 calc(33.333% - var(--gap))
视口单位 100vw / 100vh
滚动进度条 animation-timeline: scroll();
元素进入视口触发 animation-timeline: view();