CSS Animation 实战:在 freeCodeCamp 课程中用 left/top 偏移属性制造元素运动
【免费下载链接】freeCodeCampfreeCodeCamp.org's open-source codebase and curriculum. Learn math, programming, and computer science for free.项目地址: https://gitcode.com/GitHub_Trending/fr/freeCodeCamp
本篇技术指南基于 freeCodeCamp 开源课程中 Applied Visual Design 模块的练习题 "Create Movement Using CSS Animation" 展开。你将学会在 CSS 动画的关键帧中结合top、left等偏移属性控制元素运动轨迹,理解偏移属性依赖position定位这一核心前提,并能独立编写出同时包含垂直与水平两个方向运动的@keyframes规则,同时了解该练习在课程校验机制中是如何被断言验证的。
练习定位:课程结构与题目元数据
这道练习的源文件位于 587d78a7367417b2b2512ae1.md,属于applied-visual-design模块。从该模块的课程结构文件 applied-visual-design.json 可以看到,它在题目顺序中的位置是:
- "Learn How the CSS @keyframes and animation Properties Work"(理解
@keyframes与animation属性) - "Use CSS Animation to Change the Hover State of a Button"(用动画改变按钮悬停状态)
- "Modify Fill Mode of an Animation"(修改动画填充模式)
- 本题:"Create Movement Using CSS Animation"
- "Create Visual Direction by Fading an Element from Left to Right"(淡入淡出制造视觉方向)
- "Animate Elements Continually Using an Infinite Animation Count"(用无限次迭代让动画持续运行)
也就是说,本题承接前面几道关于@keyframes基础、悬停状态和animation-fill-mode的练习,是"让动画产生位移"的第一道实操题,后续题目会在此基础上引入animation-iteration-count: infinite等进阶属性。
题目文件的 YAML front matter 中标注了challengeType: 0。对照课程共用的题目类型定义 challenge-types.ts,0对应的正是html类型(纯 HTML/CSS 编辑器题型),而 challenge-schema.js 中对challengeType的 Joi 校验要求它是0到33之间的必填数字。front matter 中的其余字段含义如下:
| 字段 | 值 | 说明 |
|---|---|---|
id | 587d78a7367417b2b2512ae1 | 题目唯一标识,与文件名一致,并出现在块结构文件的challengeOrder中 |
title | Create Movement Using CSS Animation | 课程界面显示的题目标题 |
challengeType | 0 | HTML 编辑器题,学习者直接在 HTML/CSS 编辑器中作答 |
videoUrl | (一个外部视频地址) | 配套的讲解视频,课程平台内嵌播放 |
dashedName | create-movement-using-css-animation | 用于生成 URL 的短横线命名 |
核心概念:偏移属性为何能驱动动画位移
题目描述部分给出的核心论断是:
当元素具有指定的
position(如fixed或relative)时,可以在动画规则中使用 CSS 偏移属性right、left、top、bottom来制造运动。
这一点是整道题的技术前提。按照 CSS 规范,top、right、bottom、left这几个偏移属性只对已定位元素(即position不是static的元素)生效:对普通流中的静态元素设置left: 25px不会产生任何位移。因此题目提供的起始代码里,div显式声明了position: relative:
div { height: 40px; width: 70%; background: black; margin: 50px auto; border-radius: 5px; position: relative; }在relative定位下,top/left的值表示"相对元素正常流位置偏移多少",元素仍占据原来的布局空间,只产生视觉位移——这正是做位移动画时最常用、最安全的定位方式。而fixed定位则以视口为参考系,同样可以接受偏移属性驱动位移,适合"锁定在浏览器窗口某处再移动"的场景。
@keyframes的工作原理是:你在若干百分比(0% 到 100%)的锚点处声明属性值,浏览器在相邻锚点之间对可插值的属性(包括长度单位px的top、left)做逐帧插值。于是"在关键帧里改变偏移值",就等价于"让元素沿某个方向移动"。
示例拆解:垂直方向的 rainbow 动画
题目正文给出的示例规则如下,它同时做两件事:改变背景色,以及用top制造垂直运动:
@keyframes rainbow { 0% { background-color: blue; top: 0px; } 50% { background-color: green; top: 50px; } 100% { background-color: yellow; top: 0px; } }逐帧解读这条规则:
0%:top: 0px,元素处于未偏移的原始位置,背景为蓝色;50%:top: 50px,元素被向下推 50px,背景过渡到绿色;100%:top: 0px,元素回到起始高度,背景变为黄色。
由于top在 0% 与 100% 取值相同,元素完成一次"下沉再上浮"的往返运动,颜色则从蓝经绿过渡到黄,这就是 "rainbow" 这个名字的由来。
实战:为动画叠加水平方向运动
题目的要求是:为div的动画添加水平运动。具体要求:
- 使用
left偏移属性,向@keyframes规则中添加三条声明; 0%时left为0px;50%时left为25px;100%时left为-25px;- 不要修改原有的
top属性——最终动画必须同时具备垂直和水平两个方向的运动。
题目提供的起始代码(即编辑器中的 seed 代码)完整如下:
<style> div { height: 40px; width: 70%; background: black; margin: 50px auto; border-radius: 5px; position: relative; } #rect { animation-name: rainbow; animation-duration: 4s; } @keyframes rainbow { 0% { background-color: blue; top: 0px; } 50% { background-color: green; top: 50px; } 100% { background-color: yellow; top: 0px; } } </style> <div id="rect"></div>其中#rect选择器通过animation-name: rainbow把上面的@keyframes规则绑定到div上,animation-duration: 4s规定整个动画(从 0% 到 100%)用时 4 秒。
正确的完整解法,即在每个关键帧中追加left声明而不触碰top:
<style> div { height: 40px; width: 70%; background: black; margin: 50px auto; border-radius: 5px; position: relative; } #rect { animation-name: rainbow; animation-duration: 4s; } @keyframes rainbow { 0% { background-color: blue; top: 0px; left: 0px; } 50% { background-color: green; top: 50px; left: 25px; } 100% { background-color: yellow; top: 0px; left: -25px; } } </style> <div id="rect"></div>轨迹分析:这条动画到底画出了什么
把两个方向的关键帧值并排看:
| 关键帧 | 垂直top | 水平left | 综合位置(相对原始流位置) |
|---|---|---|---|
| 0% | 0px | 0px | 原位 |
| 50% | 50px(下) | 25px(右) | 右下方 |
| 100% | 0px(回到原位高度) | -25px(左) | 左上方回到原高度、偏左 25px |
可以推断出元素的运动轨迹大致是一条从原位向右下、再向左上摆动的弧线:先向右下方移动 25px/50px,随后回到原始高度但偏左 25px 结束。与垂直方向不同的是,水平方向的终点-25px与起点0px不一致——left在动画结束时停留在 -25px 的偏移量上。因为该规则没有设置animation-iteration-count(默认值为1,只播放一次)和animation-fill-mode(默认none,动画结束后不再保留 100% 关键帧的值),所以播放结束后元素会在下一次样式计算时恢复为未偏移的原始位置。这正是前面那道 "Modify Fill Mode of an Animation" 练习要解决的问题:用forwards等填充模式让动画终点值持久生效。
关键参数与常见坑
position不能少:如果把div上的position: relative删掉,top/left全部失效,动画只剩颜色变化而没有任何位移。这是本题最容易犯的错误。- 不要替换
top:题目明确要求保留top声明。两个方向互不干扰,浏览器会对每条声明的属性独立插值;删除top虽然也能通过部分正则校验之外的视觉效果,但不符合"同时具有垂直和水平运动"的验收标准。 - 负值
left: -25px是合法的:偏移量可以为负,表示向反方向偏移,本题特意用它让终点与起点不同。 animation-name与@keyframes的名称必须一致:#rect上写的是rainbow,关键帧规则名也是rainbow,二者通过这个名字关联,改名时两处要同步。- 时长由
animation-duration控制:4s指整段 0% → 100% 动画的总时长;若关键帧只有 0%、50%、100% 三个锚点,则 0%→50% 与 50%→100% 各占约 2 秒。
校验机制:题目是如何断言你的代码的
题目文档中内置了三条测试断言,用于验证每个关键帧里的left取值。它们基于正则表达式检查代码文本,逐条对应题目要求:
// 0% 关键帧应使用 left: 0px assert.match(code, /[^50]0%\s*?{[\s\S]*?left:\s*?0px(;[\s\S]*?|\s*?)}/gi); // 50% 关键帧应使用 left: 25px assert.match(code, /50%\s*?{[\s\S]*?left:\s*?25px(;[\s\S]*?|\s*?)}/gi); // 100% 关键帧应使用 left: -25px assert.match(code, /100%\s*?{[\s\S]*?left:\s*?-25px(;[\s\S]*?|\s*?)}/gi);从正则的写法可以看出校验的细节考量:
\s*?与[\s\S]*?允许声明前后存在任意空白和换行,因此left: 25px写成left: 25px或单独一行都能通过;(;[\s\S]*?|\s*?)分支兼容属性声明以分号结尾(后面还有其他声明)或作为块内最后一条声明(无分号、直接接})两种写法;- 第一条正则开头的
[^50]是一个防御性设计,用来区分0%与50%——避免把 "50%" 尾部的 "0%" 误匹配为起点关键帧。
这提示学习者:校验针对的是关键帧内部的文本结构,所以三条left声明必须分别落在 0%、50%、100% 三个块内,且取值精确为0px、25px、-25px。
小结与延伸
本题在极短的代码里覆盖了 CSS 动画的三个关键点:定位前提(position: relative使偏移属性生效)、双向运动合成(top与left在关键帧中独立插值)、以及负偏移值的使用。完成这道题后,可以顺着同一模块继续学习"Create Visual Direction by Fading an Element from Left to Right"(用不透明度制造运动方向感)和"Animate Elements Continually Using an Infinite Animation Count"(让动画无限循环),逐步把单次的位移动画扩展为持续运作的视觉效果。相关的课程素材均可在仓库中查阅:题目原文见 587d78a7367417b2b2512ae1.md,模块顺序见 applied-visual-design.json。
【免费下载链接】freeCodeCampfreeCodeCamp.org's open-source codebase and curriculum. Learn math, programming, and computer science for free.项目地址: https://gitcode.com/GitHub_Trending/fr/freeCodeCamp
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考