ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

CSS画Q版小羊:从盒子模型到定位布局的趣味入门实战

CSS画Q版小羊:从盒子模型到定位布局的趣味入门实战 “这种小羊最好骗回家了~”——如果你把这行字发给前端群里刚学 CSS 的朋友大概率会收到一个问号。但换成“用 CSS 画一只 Q 版小羊”很多写过两三个月页面的同学会立刻反应过来这就是那个适合新手快速获得成就感的练习小项目。我见过不少人在第一周就急着仿一个完整官网头部结果卡在浮动和定位里最后连基础的图形都画不出来。问题不在于天赋而在于太多人一上来就挑战“复杂系统”忽略了“用几个盒子拼出一个有反馈感的东西”这件事。这句话里最有意思的是“骗回家”三个字。技术学习里真正能让人坚持到下一阶段的从来不是“这个很重要所以你必须要学”而是“我居然做到了”。一只由纯 CSS 构成的小羊恰好能给你这种正反馈。它的图形足够简单不至于劝退又足够完整能覆盖定位、圆角、层级、嵌套这些前端基础概念。这篇博客我想聊的不只是复制一份代码让页面里出现一只羊而是拆开讲清楚为什么这类小练习适合新手动手前需要准备什么具体怎么一步步画出来卡住时该按什么顺序排查以及做完之后怎么把它升级成更像样的前端能力。我更想强调一个判断这种小羊项目的最大价值不是让你记住几个选择器而是帮你建立“先拆图、再布局、最后调细节”的前端落地思路。你以后写导航栏、写卡片、写后台管理界面用的都是同一套思考方式。1. 为什么我建议新手先做一只小羊而不是直接仿一个官网1.1 反馈感决定你能走多远很多新手学 CSS 时最典型的状态是“看懂了但写不出来”。看教程时每个属性都认识一关掉视频就不知道从哪下手。这种状态持续两周人就会开始怀疑自己是不是不适合写代码。真正的问题往往不是理解力而是没找到合适的练习颗粒度。仿官网看上去很励志实际上对新手极不友好。一个官网首页可能包含导航栏、轮播图、卡片、页脚、响应式布局失败以后你根本不知道是哪个环节出了问题。到底是浮动没清还是父容器高度塌了还是图片路径错了归因成本太高很容易放弃。小羊项目完全不同。它只有一块身体、一张脸、两只耳朵、两只眼睛、两团腮红所有零件加在一起不超过十个小盒子。完成它只需要十几分钟你能直接看到一只羊出现在浏览器里。这个“我做成了”的感觉比任何知识本身都更能推动学习。1.2 别小看这只羊它覆盖了前端最核心的底层布局问题有人说“画一只羊是不是太简单了没什么含金量”。如果你只是复制粘贴一个现成代码那确实没什么收获。但如果你从零开始拆解、布局、定位你已经在不知不觉中接触了这些内容盒模型羊的每一个部位本质上都是一个盒子有宽、高、背景色、圆角。嵌套结构脸是身体的子元素耳朵又是脸旁边的兄弟元素。父子定位想让耳朵、眼睛相对于脸摆放必须理解 position 的参照关系。圆角魔法为什么 border-radius 用百分比和用 px 会产生完全不同的视觉效果。层级关系耳朵要放在脸后面就需要用到 z-index。伪元素给眼睛加高光时可以用 ::before 或 ::after不用额外增加 HTML 标签。这些知识你背一万遍不如画一只羊来得直观。因为画图的过程会把每个属性变成“可见的改变”而不是一个孤立的定义。1.3 正确打开方式先拆图再写代码新手最容易犯的错是拿到目标就写代码。但真正的前端布局工作永远是从“拆”开始的。拿到一只小羊先观察它由几块主要形状组成大概是什么颜色哪些部分在下方哪些在上方哪些要重叠在心里画一个简单的层级树。比如身体是最外层的大盒子。脸部是身体里的一个小盒子。耳朵应该贴在脸的外侧。眼睛、腮红、鼻子都在脸上层。然后从外到内从大块到细节。先搭出身体的轮廓再放脸最后调整眼睛和鼻子。这个“先拆图、再布局、最后调细节”的流程是通用能力。以后做页面时你的第一个动作不是写 div而是看设计稿、划区域、定结构。2. 动手前先确认三件事2.1 环境不需要复杂很多新手以为写 CSS 需要装一堆工具其实这个项目只需要两样东西一个现代浏览器Chrome 或 Edge 都可以。一个文本编辑器VS Code 最常用其他编辑器也完全没问题。不需要框架不需要打包工具不需要拉取远程仓库。本地建一个文件夹在里面新建两个文件index.html 和 style.css。然后把 HTML 文件拖到浏览器里或者在 VS Code 里装一个 Live Server 插件右键打开。整个过程不到两分钟。文件结构大致是sheep-demo/ ├── index.html └── style.css2.2 前置知识清单虽然我说“新手也能做”但前提是你至少具备这些基础知道 HTML 标签是什么能写出一个 div。明白 class 属性的作用知道 CSS 里.类名代表什么。理解 CSS 的基本语法属性: 值;。会打开浏览器开发者工具能查看元素和样式。只要满足这四点就可以开始。如果你连 div 是什么都不知道建议先花半小时翻一翻 HTML 基础再回来做这个项目。2.3 明确边界先不做交互这个项目在第一个版本只追求静态图形准确。不要一上来就考虑响应式、动画、JavaScript 点击事件。因为你现在的核心目标是在有限范围内建立信心。等你能独立画出一只静态小羊后再考虑加动画加交互甚至用 CSS 变量做一身不同颜色的羊。把边界先划清楚能减少很多混乱也能让你更清楚地看到“哪些能力是先用到的哪些能力还没用到”。3. 画出这只小羊从 HTML 骨架到 CSS 细节3.1 先搭 HTML把每一块“零件”写清楚不管你要画的是羊还是猫第一步都是先写出“有哪些零件”。我做这个小项目时习惯把每个部位都写成独立的 div并且用有意义的 class 命名。这样后续调整样式时每一块的作用都一目了然。一个最小可用的 HTML 结构大概是这样的!DOCTYPE html html langzh-CN head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 titleCSS 小羊/title link relstylesheet hrefstyle.css /head body div classsheep div classear left/div div classear right/div div classsheep-face div classeye left/div div classeye right/div div classblush left/div div classblush right/div div classnose/div /div /div /body /html这里有一个容易忽略的点耳朵和脸是兄弟关系而不是父子关系。因为我想把耳朵放在脸的外侧让它们成为脸旁边左右各一的“挂件”。如果耳朵放在脸内部想要让耳朵超出脸的范围就得处理溢出隐藏或者负值定位反而会绕弯。class 命名用了 left、right 这种后缀是为了让你在 CSS 里能用.ear.left和.ear.right精确控制左右两侧不需要写重复的 HTML 结构。3.2 身体和脸从大块到细节接下来写 CSS。建议先把 body 和 .sheep 这些外层结构写好让画面先“立”起来。* { box-sizing: border-box; } body { display: flex; justify-content: center; align-items: center; min-height: 100vh; background: #f7f1e5; margin: 0; } .sheep { position: relative; width: 240px; height: 280px; background: #fff8f0; border-radius: 50% 50% 50% 50% / 45% 45% 55% 55%; box-shadow: 0 12px 24px rgba(0, 0, 0, 0.08); }body 用了 flex 让羊居中这样打开页面时羊会出现在浏览器正中间观感更好。box-sizing: border-box是很多项目里都会提前加上的基础设置。它让元素的宽高包含边框和内边距避免你在调尺寸时被意外撑开。.sheep的圆角写法用了斜杠分隔border-radius: 50% 50% 50% 50% / 45% 45% 55% 55%。斜杠左边控制水平半径右边控制垂直半径。这样做出来的不是一个正圆而是一个上端略微收窄的椭圆更像是绵羊蓬松的身体轮廓。然后是脸。脸上深棕色的椭圆。因为它是身体内部的子元素我想让它相对于 .sheep 居中所以需要给 .sheep 加上position: relative。这一步很关键。没有它.sheep-face的绝对定位就会相对于更外层的 body 去计算。.sheep-face { position: absolute; left: 50%; top: 55%; transform: translate(-50%, -50%); width: 150px; height: 170px; background: #8b6a4f; border-radius: 50% 50% 45% 45%; }这里left: 50%和top: 55%是把脸的左上角放到身体宽度一半、高度一半偏下的位置。但左上角居中不等于整体居中所以再用transform: translate(-50%, -50%)把脸向左、向上平移自身尺寸的一半。这个组合是 CSS 里让元素相对父级居中的常见方法。3.3 把耳朵、眼睛、腮红、鼻子放到位现在处理耳朵。耳朵要分别贴在脸的两侧同时应该被放到脸的后面所以需要 set 一个负 z-index。.ear { position: absolute; top: 24px; width: 52px; height: 90px; background: #8b6a4f; border-radius: 50%; z-index: -1; } .ear.left { left: -26px; transform: rotate(-20deg); } .ear.right { right: -26px; transform: rotate(20deg); }left: -26px让左耳朵从脸和身体的交界处向左探出来。这里的数值取决于耳朵宽度和脸宽度。脸宽度是 150px耳朵宽度是 52px所以耳朵左边缘大约会超出脸左边缘 26px。如果你改动了脸的宽度这里的负值也需要跟着调整。rotate(-20deg)让耳朵稍微倾斜看起来更自然。左耳向左旋转右耳向右旋转。眼睛、腮红和鼻子属于脸上的细节它们都在.sheep-face内部所以可以继续用绝对定位。.eye { position: absolute; top: 64px; width: 14px; height: 18px; background: #2d1f16; border-radius: 50%; } .eye.left { left: 36px; } .eye.right { right: 36px; } .blush { position: absolute; top: 96px; width: 16px; height: 10px; background: #f5a8a0; border-radius: 50%; } .blush.left { left: 22px; } .blush.right { right: 22px; } .nose { position: absolute; top: 108px; left: 50%; transform: translateX(-50%); width: 16px; height: 12px; background: #3a2a20; border-radius: 50%; }因为眼睛、腮红、鼻子都相对于 .sheep-face 定位所以它们的 left、right、top 都是相对于脸这个盒子计算的。鼻子居中使用left: 50%加transform: translateX(-50%)和刚才脸居中的原理一致。3.4 完整代码拿过去直接能跑把 HTML 和 CSS 拼在一起页面里就会出现一只 Q 版小羊。下面是完整代码示例你可以直接保存到本地试试。!DOCTYPE html html langzh-CN head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 titleCSS 小羊/title style * { box-sizing: border-box; } body { display: flex; justify-content: center; align-items: center; min-height: 100vh; background: #f7f1e5; margin: 0; } .sheep { position: relative; width: 240px; height: 280px; background: #fff8f0; border-radius: 50% 50% 50% 50% / 45% 45% 55% 55%; box-shadow: 0 12px 24px rgba(0, 0, 0, 0.08); } .sheep-face { position: absolute; left: 50%; top: 55%; transform: translate(-50%, -50%); width: 150px; height: 170px; background: #8b6a4f; border-radius: 50% 50% 45% 45%; } .ear { position: absolute; top: 24px; width: 52px; height: 90px; background: #8b6a4f; border-radius: 50%; z-index: -1; } .ear.left { left: -26px; transform: rotate(-20deg); } .ear.right { right: -26px; transform: rotate(20deg); } .eye { position: absolute; top: 64px; width: 14px; height: 18px; background: #2d1f16; border-radius: 50%; } .eye.left { left: 36px; } .eye.right { right: 36px; } .blush { position: absolute; top: 96px; width: 16px; height: 10px; background: #f5a8a0; border-radius: 50%; } .blush.left { left: 22px; } .blush.right { right: 22px; } .nose { position: absolute; top: 108px; left: 50%; transform: translateX(-50%); width: 16px; height: 12px; background: #3a2a20; border-radius: 50%; } /style /head body div classsheep div classear left/div div classear right/div div classsheep-face div classeye left/div div classeye right/div div classblush left/div div classblush right/div div classnose/div /div /div /body /html3.5 验证是否成功打开浏览器后你应该能看到页面中间有一个米白色的椭圆身体。身体中间偏下位置有一张深棕色椭圆脸。脸部左右两侧各有一只下垂的耳朵耳朵在脸后面。眼睛、腮红、鼻子都落在脸的正确位置上。如果某个部位没有出现或者位置不对不要急着改代码。先按后面要讲的排查顺序走一遍。4. 新手最容易错的地方不是代码而是“看不见的层级”4.1 耳朵为什么跑到脸后面去了很多人第一次写完这个小羊时发现耳朵不见了。其实耳朵很可能不是不见了而是被脸盖住了。默认情况下HTML 里后面出现的元素会盖住前面出现的元素。我的 HTML 里.ear写在.sheep-face前面所以正常情况下应该是耳朵在后、脸在前。但如果你把耳朵写在脸后面或者你想让耳朵有一部分探出来同时又不想让脸盖住耳朵就需要显式设置层叠关系。给.ear加上z-index: -1可以让它退到普通文档流的后面。但要注意z-index对非定位元素不生效。所以耳朵必须要有position: absolute或者relative等定位方式z-index 才会起作用。这就是为什么我在.ear里同时写上了position: absolute和z-index: -1。4.2 position 不是乱用的先把参照物搞清楚新手最常见的定位问题是明明给子元素写了position: absolute它却跑到了页面左上角或者跑到了浏览器窗口中心。原因通常是父元素没有设置position: relative。absolute是相对最近的“定位祖先”来定位的。如果父级没有设置为relative、absolute或fixed它就会一路向上找最后找到 body。为了避免这种情况建议给需要成为参照物的父容器主动加position: relative。以这个小羊为例.sheep加了position: relative所以.sheep-face可以相对它居中。.sheep-face加了position: absolute所以眼睛、腮红、鼻子可以相对它定位。耳朵用的也是绝对定位但它们相对.sheep定位而不是相对脸。因为我的 HTML 里耳朵和脸是兄弟节点不是父子节点。如果你修改了 HTML 结构把耳朵放进了脸里那眼睛和耳朵的参照物就会发生变化定位结果也完全不同。先记住改结构之前先想清楚它现在的定位参照物是谁。4.3 z-index 不是随便设 99999很多新手处理层级时习惯性把z-index设成很大的数比如 9999。这种方法大多数时候能解决问题但它掩盖了真正原因。z-index 并不是一个全局的“谁大谁在上面”。它是在同一个层叠上下文里比较的。如果父元素之间形成不同的层叠上下文子元素的 z-index 只能在各自的上下文里比较。在小羊项目里只要让耳朵的 z-index 为 -1并且确保耳朵有自己的定位它就会跑到普通文档流后面。不需要设成 9999。如果你发现设了 z-index 还是没用先检查三件事元素是否有position。元素是否位于不同的层叠上下文中。是不是父元素的 z-index 影响到了子元素的比较。4.4 border-radius 不是越大越好初学者常以为圆角越大越圆其实不一定。border-radius同时支持 px 和百分比两种值它们的行为有区别。百分比是相对元素自身宽高的。如果一个元素宽 240px、高 280px那么border-radius: 50%产生的椭圆半径是宽高各自的一半所以会是一个正椭圆。如果你只想让元素四周变圆但不要完全变成椭圆可以用固定像素例如border-radius: 20px。小羊身体我用了斜杠写法目的是让水平方向和垂直方向的圆角值可以分别控制做出“身体上部略窄、下部略宽”的视觉效果。你在调自己的样式时可以多试试这几种写法观察页面的变化。这个过程本身就是在建立手感。5. 如果画不出来按这个顺序排查5.1 先看现象别上来就改代码页面出错时先问自己是什么现象页面里的羊完全没出现羊出现了但所有部件堆在一起耳朵不见了脸不在身体中间眼睛位置偏移现象不同排查方向完全不同。不要一上来就乱改 CSS先记录现象再往下查。5.2 再看 HTML结构对不对第二步回到 HTML。逐一检查所有标签是否闭合div 有没有忘记写结束标签class 名称是否和 CSS 里完全一致大小写、空格都不能错。结构层级是否和预期一致耳朵是不是放在 body 外面了引入 CSS 的 link 路径是否正确如果你用的是外部样式表最常见的问题是路径写错。打开浏览器开发者工具点开 Elements 面板直接看当前页面上的 DOM 结构。如果元素没有出现说明 HTML 的问题。如果元素在但样式不对继续往下查。5.3 再看 CSS有没有写错属性如果 DOM 结构没问题就检查 CSS样式表文件是否被正确引入可以直接在开发者工具里看 style 标签或 link 标签是否生效。选择器是否写错.ear.left和.ear .left是两个完全不同的意思。前者表示同时有 ear 和 left 两个 class 的元素后者表示 ear 内部的 left 子元素。属性名和属性值有没有拼写错误border-radius拼成border-raduis浏览器不会报错但样式不会生效。建议用开发者工具的 Styles 面板逐条核对。如果一个样式被划掉了说明它可能被其他规则覆盖或者语法无效。5.4 再看布局参数位置不对往往是参照物错了HTML 和 CSS 语法都没问题但位置不对大概率是布局参数的问题。父容器有没有宽度和高度绝对定位的元素需要一个参考范围如果父级宽高为 0子元素无论怎么定位都会乱。position: absolute的参照物是谁是最近的定位祖先。如果父级没有relative它会一路跑到 body。transform 是否影响了位置translate(-50%, -50%)会让元素相对自身移动这个偏移经常被忽视。负值定位是否超出了父级边界.ear.left { left: -26px; }会让耳朵超出父级这本身是正常的但如果父级设置了overflow: hidden超出部分就会被裁掉。每改一次定位参数就刷新页面看一次效果。不要一次改五六处不然你不知道哪一处真正起了作用。5.5 最后看浏览器兼容和工具限制到这一步如果代码没问题但页面表现还是不对就要考虑浏览器和工具因素了。现代 Chrome、Edge 对基础 CSS 的兼容性都不错border-radius、position、z-index 这些都能直接用。但如果你用了 CSS 变量、aspect-ratio、某些新的颜色写法就要留意浏览器版本。小羊这个项目里我用的都是非常基础的属性一般不涉及兼容问题。还有一个隐蔽点浏览器可能有缓存。改完 CSS 后页面如果不更新先强制刷新一次或者打开开发者工具勾选 Disable cache。排查顺序可以总结成一句口诀先看现象再查结构然后查样式接着查定位最后查环境。6. 从“画一只羊”到“写一套组件”正确的进阶路径6.1 先把它变成可复用组件当你成功画出一只羊之后不要急着做下一个图形。先回头看看现在的代码有哪些地方可以变得更“通用”。比如小羊的嘴巴、颜色、身体形状都写死在 CSS 里。如果你想让页面同时出现三只不同颜色的小羊怎么办最粗暴的办法是复制三份 HTML再复制三段 CSS。但这样维护起来非常痛苦改一个耳朵形状要改三处。更合理的做法是把公共样式写成一个基础类再用 CSS 变量或者额外的 class 来控制差异。比如用 CSS 变量抽象出颜色.sheep { --body-color: #fff8f0; --face-color: #8b6a4f; background: var(--body-color); } .sheep--dark { --body-color: #e8ddd0; --face-color: #5b4436; }然后在 HTML 里写div classsheep/div div classsheep sheep--dark/div这样你就有了一只不同配色的小羊。这个“从写死到变量化”的过程比画十只羊更有价值因为它开始把你的代码变成可配置的组件。6.2 给羊加一点简单动画静态图形跑通后可以考虑加一点让页面“活”起来的动作。不需要复杂动画先从最简单的 hover 开始。比如鼠标悬停时小羊轻轻晃动.sheep { transition: transform 0.3s ease; } .sheep:hover { transform: rotate(-3deg) scale(1.02); }如果你想让眼睛眨动可以尝试用关键帧动画来实现。这个过程中你会接触到keyframes、animation、不同动画的时长曲线等概念。这些知识放到正式项目里同样适用。但要注意动画不要影响布局。transform和opacity这类属性不会触发重排对性能更友好。像width、top、left这类属性做动画也可以但性能通常较差。6.3 尝试用 CSS 变量抽象尺寸和距离很多新手喜欢用固定像素值写所有定位。在小项目里这没问题但一旦要复用你就会发现到处是魔法数字。实际上你可以把耳朵的宽度、脸的大小、颜色的色值都集中到 CSS 变量里。这样调整小羊的“表情”或“体型”时不用到处找数值改一处就可以全局生效。当然也不要过度设计。一个只有 100 行的练习项目如果硬要抽取出 20 个变量反而会增加理解成本。原则是出现重复时才抽象出现多个相同含义的数值时才变量化。6.4 什么阶段可以换更难的项目判断标准不是“我画完了一只羊”而是你能独立拿到一个目标效果图先拆出结构层级。你能用 position 和 z-index 控制元素层级。你能在出现问题时按照结构、样式、布局、环境的顺序排查。你能把画好的羊改成不同的颜色或尺寸。满足这些之后可以挑战下一个难度的项目做一个用户卡片、一个导航栏、一个登录表单或者一个简单的响应式页面。这些项目和真实业务更接近难度又超过画一只羊过渡起来会比较自然。6.5 这个练习解决不了什么我也要泼一点冷水CSS 画小羊并不解决所有前端问题。它不会让你立刻理解 JavaScript不会让你掌握 React 或 Vue也不会帮你处理复杂的浏览器兼容策略。它只是前端地基里的一块砖。这个练习的适用边界非常清晰适合零基础或入门 1 到 3 个月的开发者。适合希望巩固定位、层级、盒模型的人。不适合想把 CSS 图形画到极致的人那是一个单独的领域需要学习 SVG 或 Canvas。不适合把“会画一只羊”当成“会前端”的人。理解边界才不会把一次小小的成功当作终点。你真正从这个小项目里带走的应该是“先拆图、再布局、最后调细节”的工作方式以及“我能通过调试把它调出来”的信心。大部分时候技术学习里最稀缺的不是教程不是代码而是那种“再试一次说不定就成了”的劲头。一只小羊能把你骗进门接下来能不能走远就看你会不会用同样的方法去拆解更多更复杂的问题了。
返回列表