首页
/ freeCodeCamp 课程实战:用 CSS transform scale() 在 :hover 状态下缩放元素

freeCodeCamp 课程实战:用 CSS transform scale() 在 :hover 状态下缩放元素

2026-09-06 13:49:30作者:齐冠琰

本文围绕 freeCodeCamp 课程库中 Applied Visual Design 模块的一道经典 CSS 挑战展开:如何在鼠标悬停(:hover)时用 transform: scale() 把元素放大 1.1 倍。读完本文,你将理解 transform 与伪类组合产生交互效果的核心机制、这道挑战的完整种子代码与参考答案,并看清这道挑战在 freeCodeCamp 仓库中是如何被定义、组织与运行验证的。

1. 这道挑战在课程中的位置

该挑战文件位于 curriculum/challenges/english/blocks/applied-visual-design/587d78a5367417b2b2512ada.md,frontmatter 中声明的标题为 Use the CSS Transform scale Property to Scale an Element on Hover(在悬停时使用 CSS Transform scale 属性缩放元素)。

curriculum/structure/blocks/applied-visual-design.json 中的 challengeOrder 数组可以确认它在课程中的编排位置:

  • 上一题(587d78a5367417b2b2512ad9)是 Use the CSS Transform scale Property to Change the Size of an Element,讲解静态地用 transform: scale(1.5) 改变 #ball2 的大小;
  • 本挑战(587d78a5367417b2b2512ada)在此基础上引入 :hover 伪类,把缩放变成"按需触发"的交互效果;
  • 下一题(587d78a6367417b2b2512adb)则转向 transform 的另一个函数 skewX()

也就是说,这道题是 freeCodeCamp 课程体系里从"静态变换"过渡到"交互变换"的关键节点:先学会 scale() 能做什么,再学会什么时候做。

2. 核心概念:transform + 伪类 = 零 JS 的交互效果

挑战的 --description-- 部分给出的定义是:transform 属性提供了一系列函数,可以对元素进行缩放(scale)、移动(move)、旋转(rotate)、斜切(skew)等变换。当它与 :hover 这类描述元素特定状态的伪类结合使用时,就能非常轻松地给元素增加交互性。

原文档给出的示例是把所有段落元素在悬停时放大到 2.1 倍:

p:hover {
  transform: scale(2.1);
}

这条规则包含两个要点:

  1. 选择器p:hover 只在鼠标指针位于段落上时才匹配;离开后规则失效,元素回到未变换状态;
  2. 声明scale(2.1) 是单个参数形式的缩放,表示 X 轴与 Y 轴同时按 2.1 倍等比缩放。scale() 也接受两个参数 scale(x, y),分别控制水平与垂直方向的比例;比例小于 1 则是缩小,等于 1 则维持原尺寸。

原文档中还有一句重要的注意事项:

Note: Applying a transform to a div element will also affect any child elements contained in the div. (对 div 应用 transform 会影响该 div 内包含的所有子元素。)

这是因为 transform 作用于元素的整个渲染盒——一旦父级被变换,其内部所有子内容会作为整体一起被缩放、位移或变形,而不是逐个重新布局。这在做"卡片悬停放大"这类效果时通常是期望行为,但也要意识到子元素无法单独保持原尺寸。

3. 题目要求与种子代码

挑战的 --instructions-- 部分给出的任务是:

divhover 状态添加一条 CSS 规则,使用 transform 属性使用户悬停时把 div 元素放大到原始尺寸的 1.1 倍。

--seed-contents-- 段提供了练习者初始看到的完整代码(来自 原挑战文件的 --seed-contents-- 段):

<style>
  div {
    width: 70%;
    height: 100px;
    margin:  50px auto;
    background: linear-gradient(
      53deg,
      #ccfffc,
      #ffcccf
    );
  }



</style>

<div></div>

逐项拆解种子代码:

  • width: 70%; height: 100px:给 div 一个可见的块级尺寸;
  • margin: 50px auto:上下各 50px 外边距,左右 auto 实现水平居中(这正是该块前文 Center an Element Horizontally Using the margin Property 一节练过的技巧);
  • background: linear-gradient(53deg, #ccfffc, #ffcccf):53 度方向的线性渐变,从浅青到浅粉——这正是该块 Create a Gradual CSS Linear Gradient 一节刚学过的渐变语法,让被缩放的色块有足够视觉对比。

<style> 块末尾留出的空行,就是练习者需要补写规则的位置。

4. 参考答案

挑战文件的 --solutions-- 段给出的完整解法只需在原有 div 规则之后追加一条悬停规则:

<style>
  div {
    width: 70%;
    height: 100px;
    margin:  50px auto;
    background: linear-gradient(
      53deg,
      #ccfffc,
      #ffcccf
    );
  }
  div:hover {
    transform: scale(1.1);
  }
</style>
<div></div>

运行后,鼠标移入色块时它会平滑地(瞬时地)放大 10%,移出后恢复原状。注意题目刻意选择了 1.1 而不是 2.1:页面级 2 倍以上的缩放很容易造成内容溢出或遮挡,而 1.0~1.2 倍是悬停反馈中更常用的克制幅度。

5. 自动判题:测试断言是怎么写的

挑战的 --hints-- 段同时承载了人工提示和机器判题正则。提示文字说"用户悬停时 div 的尺寸应放大 1.1 倍",随后给出的断言是:

assert.match(code, /div:hover\s*?{\s*?transform:\s*?scale\(1\.1\);/gi);

这条正则值得细读,它展示了 CSS 类挑战的判题方式——对源码文本做模式匹配,而非比对渲染结果:

  • div:hover\s*?\{\s*?:匹配选择器 div:hover、其后任意空白、左花括号及内部空白;
  • transform:\s*?scale\(1\.1\);:要求声明 transform: scale(1.1);,其中 1\.1 对小数点做了转义;
  • 标志位 gig 全局搜索(代码任意位置),i 忽略大小写(Transformtransform 均可通过)。

由于正则没有锚定行首行尾,写法规则前后空行多少、缩进如何都不影响通过——判题只关心 div:hover { transform: scale(1.1); } 这一语义序列是否存在。

6. 仓库纵深:一道挑战文件是如何被消费运行的

理解了题目本身,再回到 freeCodeCamp 仓库看这道挑战作为"数据"如何被平台消费,有助于理解课程文件各段落的职责。

6.1 frontmatter 元数据。文件头部的 YAML 块声明了 id587d78a5367417b2b2512ada,与文件名一致,并被块结构 JSON 的 challengeOrder 引用)、titlechallengeType: 0dashedName(用作 URL 路径与翻译索引),以及指向外部视频讲解与论坛帖子的 videoUrlforumTopicId。这些字段由课程数据校验器统一约束,例如 curriculum/schema/challenge-schema.js 中对 solutions 等字段有显式定义(其中 solutions: Joi.array().items(Joi.array().items(fileJoi).min(1)) 规定了每个解法是一组文件数组,对应多文件编辑器的文件结构)。

6.2 分段式正文。文件正文用 # --description--# --instructions--# --hints--# --seed--(含 ## --seed-contents--)、# --solutions-- 这类小节标题把内容切块。课程解析器据此把"给学习者看的说明"、"初始代码"、"测试断言"、"参考解法"分离出来,分别注入运行时的不同位置。

6.3 客户端执行链路。在客户端,经典挑战页的入口是 client/src/templates/Challenges/classic/show.tsx。从源码结构看,该组件通过 Redux actions 完成一组编排:createFiles(用种子内容初始化编辑器文件)、initTests(把 --hints-- 段中的断言装配成测试)、executeChallenge(在沙箱 iframe 中执行代码并跑测试,通过后弹出完成窗口)。本文挑战的 div:hover 正则断言,正是经这一链路被注入并执行的。challengeType: 0 对应的正是 classic 这一传统挑战形态,challengeTypes 常量在 @freecodecamp/shared 包中定义并被 show.tsx 直接引用。

6.4 判据的"宽容边界"。回到第 5 节的正则可以印证:因为断言是文本级匹配,div:hover 规则写在全局 div 规则之前或之后都能通过;但若漏写分号(scale(1.1) 后无 ;)或写成 scale(1.10) 之类,正则不会命中,判题即失败。这是使用正则判题的典型取舍:实现简单、抗格式差异,但只保证"写了正确的声明",不验证最终视觉效果。

7. 实战延伸:让缩放更自然的两个方向

在完成本题 div:hover { transform: scale(1.1); } 之后,有两个自然的进阶方向,也正好对应该模块后续章节的主题:

  1. 加过渡(transition)transform 是可动画属性,为 div 增加 transition: transform 0.2s; 可以让放大/还原在约 200ms 内渐变完成,而不是瞬时跳变——悬停反馈的"手感"往往就来自这一步;
  2. 走向完整动画:当交互需求从"状态切换"升级为"连续动作"时,transform 会与 @keyframesanimation 属性配合使用,这正是本模块紧随其后的 Learn How the CSS @keyframes and animation Properties Work 等挑战的内容。

同时注意文档原文的提醒同样适用于进阶场景:一旦在 div 上挂了 transform(无论是否悬停激活),该 div 会成为子元素定位的包含块参考基准变化来源,且子元素会随其整体变换——在复杂页面中调试"为什么子元素也跟着动了"时,这通常是第一嫌疑点。

8. 小结

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
898
5.82 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
921
1.84 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.8 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
531
596
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.02 K
519
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.36 K
1.46 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
391