主题
作业讲解-幽灵按钮与作业讲解-幽灵按钮-使用伪元素实现边框(Day15 1/3)
本页对应 Day15_CSS3,共 7 段视频,约 1 小时 54 分。
学习目标
- 能够准确区分并列举
transform、transition、animation的核心属性,说明 2D/3D 变换方法及过渡、动画常用属性的作用与取值。 - 能够使用两个伪元素或子元素配合定位、宽度过渡,独立实现幽灵按钮的上下边框展开效果。
- 能够构建“四块拼图”HTML 结构,通过定位、
overflow: hidden和悬停位移过渡实现图片分块滑动效果。 - 能够使用精灵图、关键帧、
steps()与无限循环动画实现自行车骑行动画,并通过调整持续时间改变运动速度。 - 能够使用关键帧、
animation-delay、交替方向和无限循环实现多个字母依次跳动的 loading 动画。 - 能够使用
transform-style: preserve-3d、perspective、transform-origin和 3D 旋转完成可连续旋转的立方体盒子。
知识点
本节要解决什么问题
这一节把 CSS3 里最容易混在一起的三组能力放在一起复习:变换 transform、过渡 transition、动画 animation。它们不是互相替代的关系:transform 负责“变成什么样”,transition 负责“从 A 到 B 怎么平滑过去”,animation 负责“用关键帧编排一段可重复的运动”。先总结属性,再用幽灵按钮、过渡案例、自行车动画、loading 动画和立方体盒子把知识点落地。学习时不要只记属性名,要先判断:这个效果是一次状态变化,还是需要循环编排?是二维还是三维?变化发生在元素自身还是父元素?
变换 transform:先分清 2D 与 3D
为什么需要变换?因为只改宽高、位置,很难做旋转、缩放、翻转和三维折叠。怎么想?先看变换方法:2D 有位移 translate、缩放 scale、旋转 rotate;3D 多了 translate3d、rotateX/rotateY/rotateZ、rotate3d 等。怎么做?这些方法都作为 transform 的值,多个变换用空格隔开。位移参数写长度,像素、em、百分比都可以;百分比在 X 方向参照元素自身宽度,Y 方向参照自身高度,所以做定位元素居中时很实用。缩放写纯数字或百分比,表示比例;旋转写角度。
易错点:多个变换的顺序会影响结果,尤其“先旋转再位移”和“先位移再旋转”不一样,因为旋转后元素的 X、Y 方向已经变了。transform-origin 设置变换原点,对位移没有影响,但对旋转、缩放影响很大,默认是 center。3D 旋转要建立空间感:rotateX 绕 X 轴,rotateY 绕 Y 轴,rotate3d(x,y,z,角度) 的前三个参数表示各轴是否参与。做 3D 时,父元素要开 transform-style: preserve-3d;要近大远小,用 perspective;观察位置用 perspective-origin。backface-visibility 控制背面是否可见,旋转超过 90 度后就会看到背面,设为 hidden 就隐藏。
过渡 transition:状态变化要有触发条件
为什么需要过渡?因为颜色、背景、宽度、变换等如果瞬间切换会很生硬。怎么想?过渡通常要和一个“状态变化”结合::hover、:active、:focus,或者 JS 事件、媒体查询。怎么做?最核心的是 transition-duration,只设置它也能看到过渡;transition-delay 控制延迟;transition-property 控制哪些属性参与,默认是 all,但只有能过渡的属性才有效;transition-timing-function 控制运动曲线;复合属性 transition 常用写法里,写一个时间表示持续时间,写两个时间则第一个是持续时间、第二个是延迟。
易错点:不是所有属性都能过渡。长度、颜色、纯数字、滤镜、变换通常可以;display 从 inline 到 inline-block 这类离散变化不行,渐变色作为图像值也不行。调试时先确认“变化前后样式是否真的不同”,再确认“过渡加在发生变化的元素上”。
幽灵按钮:边框为什么用子元素或伪元素
这个案例的目标是:按钮默认有背景和文字,鼠标悬停时背景、文字变色,同时上下两条边框从中间向两侧展开。为什么不能直接给按钮加 border-top/bottom?因为普通边框没法单独做“从左到右展开”的动画。怎么想?用两个元素分别表示上、下边框,让它们脱离文档流,不影响文字。怎么做?给按钮设置宽高、背景、文字居中;两个边框元素用绝对定位,上边框 top:0; left:0,下边框 bottom:0; right:0,初始宽度为 0;悬停时宽度变为 100%;按钮和边框元素都加过渡。转写里调试时特别提醒:边框元素必须定位,否则行内元素不能设宽高,变成块元素又会把文字挤下去;定位后默认宽度由内容撑开,所以还要显式设宽度。
易错点:悬停选择器要写对,是“按钮悬停后改变后代边框”,不是把按钮自身样式写错。另一种实现是用 ::before 和 ::after 代替两个 span。这里有个坑: .btn:hover::before 中间不能加空格;伪元素选择器要紧挨着前面的选择器,加了空格含义就变成后代元素了。
过渡案例:四块图拼出完整画面
这个案例的难点不在过渡,而在 HTML 结构。为什么需要四块?因为要让完整图案的四个角分别向外移动。怎么想?底层放风景图,上层放一个遮罩容器,里面再放四个小容器,每个小容器里放同一张完整图,但只显示对应角。怎么做?外层相对定位,四个小容器绝对定位或浮动排列,每个小容器 overflow:hidden;里面的图片绝对定位,通过 left/top/right/bottom 调整显示区域。悬停时分别改四个图片的位置,并给图片加过渡。易错点:四个小容器要先定位并设好宽高,里面的图片也要定位,否则无法挪动显示区域;过渡加在需要变化的图片上。
自行车动画:关键帧加 steps 分步
为什么用动画?因为自行车要循环播放多帧。怎么想?整张精灵图有 32 个状态,元素宽高 130×130,总高 4160,所以结束位置是 4160 - 130 = 4030,背景位置用负值。怎么做?写 @keyframes,从 background-position: 0 0 到 0 -4030px,元素上设 animation: bike 1s infinite。易错点:默认运动曲线是平滑的,会像整张图在滑动;要改成 steps(31),因为 32 个状态之间是 31 次变化。调试时先发现动画“滑过去”不对,后来用分步运动才像真动画;鼠标悬停改 animation-duration 可以模拟上坡变慢。
loading 动画与立方体盒子
loading 动画的关键是:每个字母用 span 包起来,共用同一个关键帧,向上移动再回来;用 animation-delay 让每个字母依次延迟,形成波浪。易错点:行内元素不能直接做 transform,要改成 inline-block;还要配合 infinite 和 alternate。立方体盒子则先写六个面,全部绝对定位叠在一起,再用 translate 和 rotate 折到前后左右上下;父元素开 preserve-3d,设置 perspective 观察距离。调试时发现绕 Y 轴折左边时,旋转轴默认在中心,折出来不对,于是用 transform-origin: right 把轴放到右边;右边面用 left,上面用 bottom,下面用 top。最后给父元素加 3D 旋转动画,并用 transform-origin 的三值写法把旋转中心移到盒子中心,减少来回晃动。这个案例主要训练空间想象,理解不了也不必过度纠结,实际开发中 2D 变换和业务逻辑更常用。
本节回顾代码
css
1. tranform 变换(变形)
1.2 变换方法
2D变换:translateX() translateY() translate() scaleX() scaleY() scale() rotate()
3D变换:translateZ() translate3D() rotateX() rotateY() rotateZ() rotate3D()
1.3 css属性
需要给变换的元素: transform、transform-origin、backface-visibility
需要给变换的元素的父元素:transform-style、perspective、perspective-origin
2. transition 过渡
transition-duration
transition-delay
trnasition-property
trnasition-timing-funciton
transition
3. animation 动画
3.1 关键帧
3.2 css属性
animation-name
animation-duration
animation-delay
animatino-timing-function
animation-iteration-count
animation-direction
animation-play-state
animation-fill-mode
animation变换 transform:先建立空间,再写属性
为什么需要:只靠普通布局,元素只能平移位置、改宽高;要旋转、缩放、做 3D 盒子,就需要 transform。转写稿里口述的“宽斯form”“传丝算的”都是 transform 的误听,统一按 transform 理解。
怎么想:先把变换方法分成 2D 和 3D 两类。2D 有位移 translateX()/translateY()/translate()、缩放 scaleX()/scaleY()/scale()、旋转 rotate();3D 多出 translateZ()/translate3D() 和 rotateX()/rotateY()/rotateZ()/rotate3D()。位移参数写长度,百分比在 X 方向参照元素自身宽、Y 方向参照自身高;缩放写纯数字或百分比;旋转写角度。3D 旋转时脑子里要有 X、Y、Z 轴,rotate3D() 前三个参数是 0/1,表示是否沿对应轴转,最后才是角度。
怎么做:把变换方法作为 transform 的值,多个变换用空格隔开;旋转缩放的中心用 transform-origin;3D 背面隐藏用 backface-visibility;父元素开 3D 空间用 transform-style: preserve-3d,近大远小用 perspective,观察位置用 perspective-origin。
易错点:多个变换顺序不同,结果可能不同,尤其“先旋转再位移”会沿旋转后的轴移动;transform-origin 对旋转、缩放影响大,对位移没有影响;3D 变换要老老实实给父元素加 transform-style: preserve-3d,不要只靠浏览器碰巧支持;translate3D() 参数必须写三个,少了会被认为无效。
过渡 transition:变化要触发,也要“能过渡”
为什么需要:元素从红变绿、从窄变宽,如果瞬间切换会很生硬;transition 让样式变化在一段时间内完成。
怎么想:过渡往往要结合用户动作,例如 :hover、:active、:focus,也可以是 JS 事件或媒体查询。先确定“哪个属性变了”,再确定“持续多久、是否延迟、用什么运动曲线”。transition-property 默认是 all,但并不是所有属性都能过渡:长度、颜色、纯数字、滤镜、变换比较适合;背景渐变图这类图像值不能平滑过渡。
怎么做:最少写 transition-duration 就能看到效果;完整写 transition: 属性 持续时间 运动曲线 延迟。复合属性里只写一个时间,它是持续时间;写两个时间,第一个是持续时间,第二个是延迟。运动曲线可匀速、加速、减速、先加速再减速。
易错点:只写 transition 不写时间通常看不到过渡;transition-property 写成不能过渡的属性会失效;过渡要加在“发生变化的元素”上,伪元素做边框时伪元素自己也要加过渡;转写稿里的“幽灵按钮”用两个 span 或 ::before/::after 做上下边框,必须定位,否则行内元素不能设宽高,或块元素会把文字挤下去。
动画 animation:关键帧加时间控制
为什么需要:过渡通常依赖一次状态变化,而动画可以自动、循环、分步执行,适合 loading、自行车精灵图、立方体旋转。
怎么想:动画分“关键帧”和“CSS 属性”两部分。关键帧用 @keyframes 定义,最少可以只写结束帧,起始样式就用元素默认样式;一个元素可以用多个关键帧,同一个关键帧也能给多个元素,是多对多关系。属性上,animation-name 指定关键帧,animation-duration 指定时长,animation-delay 延迟,animation-timing-function 运动曲线,animation-iteration-count 执行次数(可 infinite),animation-direction 方向(可 alternate 交替),animation-play-state 控制运行/暂停,animation-fill-mode 控制执行前后状态。
怎么做:先写关键帧,再用 animation 复合属性挂到元素上。转写稿里的自行车动画,图有 32 个状态,实际变化 31 次,所以用 steps(31) 做分步运动;loading 动画用递增的 animation-delay 让每个字母错开;立方体盒子用 6 个面定位,配合 transform-origin 的 right/left/top/bottom 折面,整体旋转时还要注意旋转中心。
易错点:动画名拼错会完全不动;行内元素直接做 transform 可能不生效,需要改成 inline-block 等;只写一次执行不会循环,要加 infinite;需要上下往返时用 alternate;精灵图动画不用分步会“平滑滑过”,看起来不是逐帧动画;animation-fill-mode 默认是 none,需要结束后保持就写 forwards,需要开始前应用起始帧就写 backwards。
课堂画面






代码示例
示例一:幽灵按钮(伪元素 + transition)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>幽灵按钮</title>
<style>
* { margin: 0; padding: 0; }
body {
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
background: #333;
}
.btn {
position: relative; /* 作为两个伪元素的包含块,必须有 */
width: 380px;
height: 80px;
line-height: 80px; /* 文字垂直居中 */
text-align: center; /* 文字水平居中 */
font-size: 40px;
font-weight: 800;
color: #fff;
background: deeppink;
cursor: pointer;
transition: 800ms; /* 背景色、文字色的过渡 */
}
/* 上下两条边框用伪元素做,因为 border-top 没法只显示一段长度 */
.btn::before,
.btn::after {
content: ""; /* 伪元素必须写 content 才会生成 */
position: absolute; /* 定位后脱离文档流,不会把文字挤下去 */
width: 0; /* 初始宽度 0 => 看不见 */
height: 10px;
background: yellowgreen;
transition: 2s; /* 边框自己也要有过渡 */
}
.btn::before { top: 0; left: 0; } /* 上边框靠左 */
.btn::after { bottom: 0; right: 0; } /* 下边框靠右 */
/* 鼠标悬停:按钮自身变色 */
.btn:hover {
color: deeppink;
background: #fff;
}
/* 鼠标悬停:两个边框变长。伪类在前,伪元素在后,中间不能有空格 */
.btn:hover::before,
.btn:hover::after {
width: 100%;
}
</style>
</head>
<body>
<div class="btn">HTML5</div>
</body>
</html>关键行与易错点
position: relative必须给.btn:否则伪元素的绝对定位会参照整个页面,两条线会飞到窗口左上角。content: ""是伪元素生成的前提,漏写则整条规则失效。- 用伪元素而不是
border-top/bottom:border 无法做"从左边慢慢长到右边"的长度动画,只能用宽度从0变100%。 - 伪元素必须
position: absolute:一是行内元素设不了宽高;二是不定位会作为内容参与文档流,把文字挤下去。 - 初始
width: 0而不是display: none:display: none
常见错误
- 现象:旋转后再位移,元素跑偏 → 原因:
transform多个函数按书写顺序复合,旋转会改变后续位移的坐标轴 → 修法:先明确“先位移还是先旋转”,单独写一个变换验证方向,涉及旋转时尤其检查顺序。 - 现象:3D 立方体六个面被压平、看不出立体 → 原因:父元素未开启 3D 空间或子元素 3D 被扁平化 → 修法:父元素加
transform-style: preserve-3d,需要近大远小时再加perspective。 - 现象:
.btn:hover::before写成.btn :hover::before后边框不出现 → 原因:多写空格变成后代选择器,伪元素没有紧贴宿主选择器 → 修法:改为.btn:hover::before,伪类在前、伪元素在后且不加空格。 - 现象:给
span加transform或动画不生效 → 原因:普通行内元素不能直接变换 → 修法:改成display: inline-block或display: block后再变换。 - 现象:精灵图逐帧动画像平滑滑动,不像一帧一帧切换 → 原因:默认缓动会平滑过渡,未使用分步运动 → 修法:设置
animation-timing-function: steps(n),并核对帧数与位移量。 - 现象:鼠标移出后没有过渡,瞬间回位 → 原因:
transition只写在:hover里 → 修法:把transition写在基础状态,:hover只写变化后的样式。 - 现象:立方体折面方向不对、整体旋转时来回晃 → 原因:
transform-origin默认在中心,折边轴或整体旋转中心不对 → 修法:折边按需要设left/right/top/bottom,整体旋转中心可用三值transform-origin调到盒子中心。 - 现象:loading 字母同时跳,没有波浪效果 → 原因:所有
span使用相同延迟 → 修法:用:nth-child(n)分别设置递增的animation-delay,并配合交替、无限次。
练习
- 用伪元素实现“幽灵按钮”上下边框:HTML 不额外加标签,默认边框宽度为 0,
hover时变为 100%,过渡写在基础状态。 - 完成曲奇四宫格遮罩过渡:4 个
mask-item内放同一张大图,分别定位显示左上、右上、左下、右下,hover时四角向不同方向位移。 - 完成自行车精灵图动画:总图高 4160px,单帧 130×130,共 32 帧;用
steps()实现逐帧,hover时改变动画持续时间模拟上坡。 - 完成 loading 字母波浪动画:7 个
span,统一关键帧上下位移,利用:nth-child设置递增延迟,使用交替和无限次。 - 完成立方体盒子:6 个面绝对定位重叠,按折边设置
transform-origin,父元素开启 3D,整体用rotate3d匀速无限旋转。
原视频对照
| # | 视频 | 时长 |
|---|---|---|
| 1 | 回顾 | 22:04 |
| 2 | 作业讲解-幽灵按钮 | 10:10 |
| 3 | 作业讲解-幽灵按钮-使用伪元素实现边框 | 06:21 |
| 4 | 作业讲解-过渡案例 | 22:37 |
| 5 | 作业讲解-自行车动画 | 11:53 |
| 6 | 作业讲解-loading动画 | 07:17 |
| 7 | 作业讲解-立方体盒子 | 33:34 |
小结
- 多个变换函数顺序不同,最终效果可能完全不同,涉及旋转时必须先验证坐标轴变化。
- 3D 效果优先检查父元素:
preserve-3d是立体展示的前提,perspective只负责近大远小。 - 伪元素选择器必须紧贴宿主选择器,
hover与::before/::after可交集书写,但空格会改变语义。 - 过渡要写在基础状态,变化状态只写目标值;不可过渡的属性不要硬做过渡。
- 精灵图逐帧动画的核心是
steps()与结束位移量,帧数和总高度要算准。 - 动画的延迟、交替、无限次、持续时间要组合使用,才能做出波浪、上坡等效果。
- 3D 折面先定位移,再定旋转轴和原点;整体旋转中心不对时,优先调
transform-origin。 - 结构复杂的案例先画结构、再写样式,调试时用检查器核对选择器、过渡时间、动画名和关键帧。