纯CSS绘制中国国旗与鎏金艺术字实战
1. 项目拆解这个案例到底在练什么1.1 核心需求解析“使用 CSS 绘制中国国旗并添加艺术字‘中国万岁’”这个项目看似不起眼实际上是一道非常典型的 CSS 综合练习题。目标很简单不引用任何外置图片不依赖 SVG 标签不用 Canvas 绘图只用 HTML 结构和纯 CSS 样式把一面比例准确、星位正确的国旗画出来再在旗面或页面上放一行有设计感的艺术字让画面整体有“可以拿去当活动海报”的完成度。我一开始接到类似需求的时候第一反应是想偷懒用背景图直接贴或者干脆画一个 SVG 塞进去。但客户的需求写得很明确必须纯 CSS。这个限制本身反而让项目变得有意思了。因为一旦不能用图片你就必须认真思考几个问题旗面怎么按 3:2 比例撑开五角星怎么用 CSS 画出来五颗星的相对位置该怎么换算文字怎么做出金属或烫金质感而不是简简单单的黑体字这一套问题走下来几乎把盒模型、定位、伪元素、渐变、动画、clip-path 全部覆盖了一遍。所以这个项目特别适合两类人。一类是刚学完 CSS 基础想做点“能拿得出手”的小作品来找手感的人另一类是想系统梳理定位和几何换算的进阶开发者因为这里面的坐标计算、比例换算和响应式适配含金量不低。甚至你把它当作一次面试前的 CSS 复习项目也完全说得过去。1.2 技术选型为什么坚持纯 CSS说实话如果只是要结果直接写一个内联 SVG五分钟就完事。SVG 的 polyline 画五角星不要太简单国旗的矢量图形也有一堆现成模板。但坚持纯 CSS 的意义不在“最终效果”而在“过程训练”。纯 CSS 绘制图形的最大特点是你只能用样式去“推导”形状而不是用坐标去“描述”形状。就好比搭积木和捏橡皮泥的区别SVG 像捏橡皮泥你想让棱角出现在哪里坐标一写就出来CSS 像搭积木你得先有方块、线条、裁剪区域这些基础件再用布局规则把它们拼出想要的轮廓。这个过程会让你对盒模型、层叠上下文、独立坐标系这些基础概念的理解加深不少。另外纯 CSS 方案还有一个实际好处它非常容易嵌入到各种静态页面、响应式页面和邮件模板里。不需要额外引入资源不需要处理 SVG 的跨域和加载时序问题页面一打开就渲染完成。你把整个国旗组件写成一个独立的 HTML 结构配上几个 CSS 变量后面想做深色版、浅色版、小尺寸版改几个变量就行维护成本比重新画图低得多。这个项目选 CCSS 而不是 SVG还有一层现实考量大多数前端页面里装饰性的图形元素并不需要真正的“矢量交互”。国旗作为一个展示性元素用户不会去点它也不会去拖拽它CSS 画出来的静态图形完全够用还能把包体积控制在零新增资源。对性能敏感的项目来说这是一种很划算的选择。2. 搭建基础框架国旗比例与背景层2.1 3:2 画布的三种实现方案画国旗的第一步不是画星而是先把旗面这块“画布”撑出来。国旗的标准比例是 3:2也就是说宽度与高度的比值固定为 1.5。这个比例必须精确否则后面所有星星的位置换算都会跟着跑偏。CSS 实现固定比例常见的有三种写法实现方案核心写法优点缺点aspect-ratioaspect-ratio: 3 / 2;语法直观代码最少现代浏览器支持好老浏览器需要前缀或降级方案padding-top 撑高width: 600px; padding-top: 66.67%;兼容性极强老项目常用内部定位需注意 padding 盒子模型视口单位自适应width: 60vmin; aspect-ratio: 3 / 2;天然响应式屏幕变化自适应需要处理最小宽度限制我个人推荐直接用aspect-ratio。以前写自适应比例最常见的是 padding-top 技巧原理是利用百分比 padding 参照父容器宽度计算这个特性把元素本身高度撑起来。这个方案确实稳但有点绕阅读代码的人需要反应一下才知道你在干嘛。aspect-ratio就直白多了浏览器原生保证宽高比不用任何 hack。实际项目中我会这么写基础框架.flag { position: relative; width: 600px; aspect-ratio: 3 / 2; background: #de2910; overflow: hidden; border-radius: 8px; box-shadow: 0 12px 30px rgba(0, 0, 0, 0.25); }这段代码里有两个细节容易被忽略。第一个是position: relative它必须加。因为后面五颗星都要用绝对定位放到旗面上绝对定位的参考系就是这里。没有这个相对定位星星会跑到页面根节点去位置全部乱掉。第二个是overflow: hidden它既能防止星形元素在边缘产生的溢出也能配合border-radius把旗面四个角裁出圆角让整体更像一个“展示元素”而不是一块生硬的色块。2.2 背景层的细节处理旗面主色我用的#de2910这个颜色接近标准的国旗红色。实际开发中你不需要纠结到底是不是“官方色值”只要视觉上正红偏暖、不偏粉不偏暗就行。如果你用的是设计稿里的其他红色直接用设计稿的色值即可五角星的黄色也同理。很多人做这类图形时会忽略“场景氛围”。一个孤零零的旗面怼在页面正中间观感会非常干巴。我通常会给旗面外面加一层底色渐变或者深色背景再给旗面一点阴影让它像放在展示台上一样。页面背景可以用深灰蓝或暗红褐衬托红旗的明度body { min-height: 100vh; display: flex; justify-content: center; align-items: center; background: linear-gradient(145deg, #1b1b1f 0%, #2a2025 100%); }这样处理之后红旗和金星会被衬托得非常明亮艺术字的黄也会更有“发光感”。这里的经验是做颜色鲜艳的图形时页面底色别再用白色白色会吞掉图形的饱和度深色背景反而能拉开层次。2.3 用 CSS 变量统一换算单位在进入星星绘制之前我建议先把整个旗面的“单位坐标系”定下来。国旗的标准网格是宽 30 单位、高 20 单位所有的星位、直径、边距都可以用这个网格来描述。与其在 CSS 里写死一个个像素值不如用变量定义好换算.flag { --flag-width: 600px; --flag-height: 400px; --unit-x: calc(var(--flag-width) / 30); --unit-y: calc(var(--flag-height) / 20); }这里的--unit-x和--unit-y就是每个标准单位的实际尺寸。宽 600px 时一个单位 x 是 20px高 400px 时一个单位 y 也是 20px。因为比例正好两个方向的单位值相等但你还是应该分开写防止后面把高度改成非标准数值时出现问题。有了这两个变量后面所有星星定位都可以写成left: calc(var(--unit-x) * 10)这种形式一看就知道是“10 个单位”语义清晰很多。3. 五角星的三种生成思路3.1 方案一clip-path 多边形裁剪画五角星我在实际项目里最常用、也最推荐的是clip-path多边形方案。先把星形看作一个十边形五个外角顶点五个内角凹点把这十个顶点按顺序连起来就是标准的五角星轮廓。关键问题是这十个点的坐标怎么来。你可以用数学公式推外顶点分布在半径为 R 的圆弧上内顶点分布在半径为 r 的圆弧上内半径与外半径的比值约为 0.382。为了好维护我用百分比坐标以星形元素自身为参考系。假设元素是正方形中心在 (50%, 50%)五角星十点坐标可以写成.star { clip-path: polygon( 50% 0%, 61.2% 34.5%, 97.6% 34.5%, 68.2% 55.9%, 79.4% 90.5%, 50% 69.1%, 20.6% 90.5%, 31.8% 55.9%, 2.4% 34.5%, 38.8% 34.5% ); }这个多边形就是从中心向上下左右展开的十个顶点最上面的尖在 50% 0%也就是元素顶部正中间。用的时候星形元素本身摆在一个正方形盒子里盒子多大星就多大。想控制星的大小控制盒子的宽高就行坐标不用动。使用clip-path的好处有三个第一代码纯粹一个属性就完成形状没有复杂的嵌套第二星形区域是真正的多边形如果后续要加 hover 效果、动画或者点击事件命中的也是星形区域体验好第三裁剪区域内的背景、渐变、图片都能直接作为星形填充物后面做金属质感非常方便。3.2 方案二CSS 变换组合法不用clip-path的话还有一种常见的土办法把三个同样元素分别旋转 60 度和 120 度叠在一起拼出一个六角放射形状。严格来说三个正三角叠加出来的不是五角星而是六角星或风格化的“雪花星”边缘数量不对。所以这个方案只能用在“神似”的场景比如做卡通风格的小图标、节日氛围图形不能用在需要精确结构的国旗上。另一种变换组合法是用一个正方形背景元素配两个伪元素分别旋转构造出棱角。缺点是代码量明显增加而且必须精确计算旋转中心稍微偏差一点就会出现“凹点不齐、星形歪斜”的问题。我踩过这个坑伪元素旋转时需要保证旋转中心正好在图形集合的中心点但绝对定位和 translate 混在一起时偏移计算很容易乱。所以除非是刻意追求“手作感”否则不建议用这种方案画精密星形。3.3 方案三字符方案与缺陷分析还有同学会想到用 Unicode 字符★直接渲染星形。这个做法胜在简单一行content: ★就能出来。但它有两个大坑。第一个坑是字体依赖。不同系统、不同字体渲染出来的★长得完全不一样有的胖有的瘦有的边角圆润到认不出来。你在某个系统上调好了换一台设备就变形。第二个坑是尺寸和比例不可控。字符是字面盒模型的一部分行高、基线、字间距都会影响它的显示位置你很难把它精确地对准某个坐标。我在一个练习项目里就翻过车本地看星尖朝上同事的浏览器里星尖偏了十几度最后我还是换回了 clip-path。字符方案的价值主要是快速占位。比如你在搭页面结构时还不想纠结星形细节可以先放一个字符当占位符等整体布局调通了再替换成真正的图形。这时候字符方案就顺手多了。4. 五颗星的摆放从标准坐标到像素换算4.1 把画布切成网格来理解国旗上五颗星的布局用 30 × 20 网格来描述最方便。大星在左上区域四颗小星在大星右下侧呈弧形环绕每一颗小星都有一个角指向大星中心。先记住几个标准点位单位以网格为准星中心横坐标中心纵坐标直径大星556小星 11022小星 21242小星 31272小星 41092这套坐标是非常经典的标准网格数据所以叫它“标准坐标”。看懂这套网格之后剩下的事就是把它换算成具体的像素或百分比。我们用 600 × 400px 的旗面举例每个单位 x 20px每个单位 y 20px那么大星中心就是left: 100px; top: 100px大星盒子的宽高等于 6 个单位也就是 120px。小星中心分别是 (200, 40)、(240, 80)、(240, 140)、(200, 180) 像素位置小星盒子宽高为 40px。换算成 CSS 变量写法就是.star-xl { width: calc(var(--unit-x) * 6); height: calc(var(--unit-y) * 6); left: calc(var(--unit-x) * 5); top: calc(var(--unit-y) * 5); transform: translate(-50%, -50%); } .star-sm { width: calc(var(--unit-x) * 2); height: calc(var(--unit-y) * 2); transform: translate(-50%, -50%); }注意这里必须配合transform: translate(-50%, -50%)。因为left和top定位的是元素左上角而这里记录的是星形中心坐标所以要用负一半自身尺寸的平移把中心对准到目标点。这个细节我每次讲给新手听都会强调不写这行所有星星的位置会整体往右下偏。4.2 小星星的朝向修正指向大星中心只把位置摆对还不够四颗小星的“朝向”也得对。理想效果是每颗小星都有一个角正对大星中心形成一种视觉上的统一感。但由于每颗小星相对大星的方向不同每一颗的旋转角度都不一样。星形默认是尖朝上的就是 clip-path 坐标里第一个顶点指向正上方所以旋转角度就是“从小星中心指向大星中心”的连线角度。用前端习惯的屏幕坐标系y 轴向下换算可得到近似值小星旋转角度近似小星 1-121°小星 2-98°小星 3-74°小星 4-51°为什么是这些值你可以打开控制台算一下大星中心在 (100, 100)小星中心在各自坐标两点连线的方向角代入Math.atan2就能得到。这里的负号表示逆时针旋转。实际项目中你们不必追求小数点级精确因为角度差两三度在视觉上完全看不出来。但方向不能反四颗小星都得指向大星这是判断你画得对不不对最直观的地方。我在第一次写这个项目的时候偷懒统一给四颗小星用了同一个旋转角结果星尖四散乱指看起来特别别扭。后来老老实实把每颗星的连线角度算出来才解决了这个问题。建议你也不要在这一步凑合四颗星分开写旋转值代码多四行效果天壤之别。4.3 星形的公共样式抽取五颗星有许多共同样式比如黄色填充、背景渐变、统一的阴影完全没必要写五遍。我习惯抽一个公共类.star { position: absolute; background: linear-gradient(145deg, #ffd456 0%, #ffdf88 50%, #f7b500 100%); box-shadow: 0 0 12px rgba(247, 181, 0, 0.45); }大星和小星则各自设置宽高、定位、旋转。这样的结构后面想统一调整星形颜色或阴影只改一处即可。这也是我反复强调为什么要用 CSS 变量的原因图形类项目的维护重点不是“画出来”而是“改起来方便”。5. 艺术字“中国万岁”的质感实现5.1 让文字穿上渐变金色外衣画完旗面剩下就是给“中国万岁”这句艺术字做质感。要做出金属色或者鎏金效果核心思路是用渐变背景填充文字而不是用单色文字。CSS 里有个组合用法.art-text { font-size: 4rem; font-weight: 900; background-image: linear-gradient(160deg, #fff4b0 0%, #ffd21f 45%, #ff9800 90%); -webkit-background-clip: text; background-clip: text; color: transparent; }background-clip: text意思是“把背景裁剪到文字形状以内”配合color: transparent文字显示出来就是渐变背景透过字形边缘的样子。这个技巧的本质仍然是背景只不过显示区域被文字轮廓限制住了。一定要写成-webkit-background-clip: text。即使我们现在基本只看 Chromium 系和 Safari这个前缀还是不能省Firefox 虽然支持background-clip: text但稳定性不如带前缀版本最稳妥的做法是background-clip: text和-webkit-background-clip: text一起写上。这段代码里有个容易踩的坑如果只写background-clip: text而不写color: transparent文字颜色会盖在渐变上面你什么都看不到或者只能看到文字的默认黑色。所以三个条件缺一不可背景渐变、background-clip 裁剪到文字、文字颜色设为透明。5.2 立体感和描边的小细节纯渐变文字在浅色背景下容易显得单薄尤其是当页面整体是深色时金黄色的字会有点“飘”。想让它像真金一样沉着再补两层描边和阴影。描边用-webkit-text-stroke.art-text { -webkit-text-stroke: 2px #b05200; paint-order: stroke fill; }paint-order: stroke fill可以控制描边画在填充的底下这样不会把渐变字的内侧边缘吃掉太多。注意paint-order的兼容性在个别浏览器里还是有限制的不过在现代浏览器里基本都能愉快工作。阴影部分最有价值的是多层text-shadow。单层阴影会显得假三层阴影能模拟出立体字的光影关系.art-text { text-shadow: 0 2px 0 #8f4000, 0 4px 8px rgba(0, 0, 0, 0.45), 0 0 24px rgba(255, 200, 40, 0.35); }第一层是 2px 的暗色位移给文字一个“厚度”第二层是柔化的投影把字和背景分离开第三层是金色光晕突出鎏金质感。这个三层结构是我在多次尝试里觉得性价比最高的方案再加第四层也不会有特别大的视觉提升反而会拖慢渲染。这里要特别提醒一句不要把text-shadow和前面color: transparent混淆。text-shadow 是基于字形轮廓产生的阴影颜色由阴影自己指定所以即使文字颜色是透明的阴影依然可见。这也意味着如果你阴影参数设置过大会出现“一坨糊”的效果看起来像文字消失了只剩阴影。调试时记得先把阴影关掉确认渐变文字正常了再打开阴影做微调。5.3 让文字“活”起来静态艺术字满足基本需求但如果想在活动页、节日横幅上使用加点动效会让完成度高一个档次。我最常用的有两个方向。第一个是流光扫过。在文字上方盖一个白色的高光渐变色条整体做一个平移运动模拟光扫过金属表面的效果。实现起来并不需要用额外的 DOM 元素直接在文字的背景上做动画就行。技巧是把背景尺寸放大到 200% 以上再用关键帧改变background-position让渐变里的高光条从左边滑到右边.art-text { background-image: linear-gradient( 110deg, #fff4b0 0%, #ffd21f 40%, #fffbe8 50%, #ffd21f 60%, #ff9800 100% ); background-size: 200% 100%; animation: gold-shine 4s ease-in-out infinite; } keyframes gold-shine { 0% { background-position: 100% 0; } 100% { background-position: -100% 0; } }第二个是微微缩放的呼吸感。适合文字较长、需要抓住用户眼球的时候。用scale做 1.0 到 1.03 之间的往复变化幅度不要大大了会显得廉价小幅度才有一种“高级的律动感”。.art-text { animation: breath 3s ease-in-out infinite; } keyframes breath { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.03); } }如果你两个 anim 都要用建议把它们写在一个动画里或者用多个元素分层处理避免 transform 和 background-position 两个动画互相抢占animation-name导致冲突。我一开始就是动画叠加出错流光扫到一半文字疯狂跳动排查了半天才意识到是 animation 属性互相覆盖了。6. 实操过程与完整代码6.1 完整 HTML/CSS 代码到这里该讲的原理都讲完了直接放一份完整可运行的实操代码。我尽量保持结构简单方便你直接复制到本地改着玩。这里以 600 × 400px 的旗面为基础配合上流光艺术字和整页居中布局!DOCTYPE html html langzh-CN head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 titleCSS 国旗绘制与艺术字练习/title style * { margin: 0; padding: 0; box-sizing: border-box; } body { min-height: 100vh; display: flex; justify-content: center; align-items: center; background: linear-gradient(145deg, #1b1b1f 0%, #2a2025 100%); font-family: Noto Sans SC, Microsoft YaHei, sans-serif; } .flag { position: relative; width: 600px; aspect-ratio: 3 / 2; background: #de2910; overflow: hidden; border-radius: 8px; box-shadow: 0 12px 30px rgba(0, 0, 0, 0.25); --flag-width: 600px; --flag-height: 400px; --unit-x: calc(var(--flag-width) / 30); --unit-y: calc(var(--flag-height) / 20); } .star { position: absolute; background: linear-gradient(145deg, #ffd456 0%, #ffdf88 50%, #f7b500 100%); box-shadow: 0 0 12px rgba(247, 181, 0, 0.45); clip-path: polygon( 50% 0%, 61.2% 34.5%, 97.6% 34.5%, 68.2% 55.9%, 79.4% 90.5%, 50% 69.1%, 20.6% 90.5%, 31.8% 55.9%, 2.4% 34.5%, 38.8% 34.5% ); transform: translate(-50%, -50%); } .star-xl { width: calc(var(--unit-x) * 6); height: calc(var(--unit-y) * 6); left: calc(var(--unit-x) * 5); top: calc(var(--unit-y) * 5); } .star-sm { width: calc(var(--unit-x) * 2); height: calc(var(--unit-y) * 2); } .star-sm.s1 { left: calc(var(--unit-x) * 10); top: calc(var(--unit-y) * 2); transform: translate(-50%, -50%) rotate(-121deg); } .star-sm.s2 { left: calc(var(--unit-x) * 12); top: calc(var(--unit-y) * 4); transform: translate(-50%, -50%) rotate(-98deg); } .star-sm.s3 { left: calc(var(--unit-x) * 12); top: calc(var(--unit-y) * 7); transform: translate(-50%, -50%) rotate(-74deg); } .star-sm.s4 { left: calc(var(--unit-x) * 10); top: calc(var(--unit-y) * 9); transform: translate(-50%, -50%) rotate(-51deg); } .art-text { position: absolute; left: 50%; bottom: 8%; transform: translateX(-50%); font-size: 3.2rem; font-weight: 900; letter-spacing: 0.08em; white-space: nowrap; background-image: linear-gradient( 110deg, #fff4b0 0%, #ffd21f 40%, #fffbe8 50%, #ffd21f 60%, #ff9800 100% ); background-size: 200% 100%; -webkit-background-clip: text; background-clip: text; color: transparent; -webkit-text-stroke: 2px #b05200; paint-order: stroke fill; text-shadow: 0 2px 0 #8f4000, 0 4px 8px rgba(0, 0, 0, 0.45), 0 0 24px rgba(255, 200, 40, 0.35); animation: gold-shine 4s ease-in-out infinite; } keyframes gold-shine { 0% { background-position: 100% 0; } 100% { background-position: -100% 0; } } /style /head body div classflag div classstar star-xl/div div classstar star-sm s1/div div classstar star-sm s2/div div classstar star-sm s3/div div classstar star-sm s4/div div classart-text中国万岁/div /div /body /html把这段代码存成 html 文件浏览器打开就能看到效果。如果你只是把它当作静态练习注意把style里的图片路径、字体路径都去掉这份代码是零外部依赖的。6.2 关键参数与效果对照表我整理了一张参数速查表方便你复制之后快速调整要调整的内容修改位置推荐值范围旗面颜色.flag的background建议 #de2910 附近的暖红色星形基础大小.star-xl和.star-sm的宽高大星 6 单位小星 2 单位星形位置各个星类的left/top按网格系数换算别直接写像素星形旋转.s1到.s4的rotate()参考 -121° / -98° / -74° / -51°艺术字尺寸.art-text的font-size视旗面宽度而定建议 2.8rem 到 4rem艺术字描边色-webkit-text-stroke用比文字底色暗的红棕色更自然动效速度keyframes gold-shine的时长4s 到 6s 比较舒缓太快会眼花有一类常见需求是把整个图缩到手机端看。此时 600px 定宽会超出屏幕你可以把.flag的宽度改成min(600px, 92vw)然后用aspect-ratio保持比例内部所有calc(var(--flag-width))的换算会自动跟着缩小。CSS 变量的这一层“传递性”就是整个项目里最值得保留的部分。7. 常见问题与避坑实录7.1 星形变形为什么你的五角星不是正的最常被问到的就是我照着代码写了但星星看起来又歪又扁根本不是五角星。问题通常出在盒子的宽高比上。clip-path里的百分比是相对元素自身宽高的如果你把星形盒子的宽设成 80px、高设为 60px那些百分比坐标点就会被拉伸五角星自然变形。解决办法很简单确保星形元素的宽高相等。大星盒子 120×120小星盒子 40×40比例必须是 1:1。如果你需要不同尺寸只改盒子的整体宽高不改里面的 polygon 坐标。我在很多项目里都是把星形写成一个独立组件宽高用变量控制这样就不会出现“一边拉大一边拉变形”的情况。7.2 五颗星的定位不准怎么办如果你发现五颗星里的某几颗明显偏离优先检查三件事。先看父元素有没有position: relative。没有这个属性left和top会相对更上层定位星星全部散架。再看transform: translate(-50%, -50%)是否被覆盖了尤其是小星需要旋转角度的时候很容易把translate和rotate分开写后写的属性把前一个覆盖掉。我见过有人写成transform: rotate(-121deg)漏了translate(-50%, -50%)结果每颗星中心偏出半个身位。正确的写法是把两个函数放在同一个 transform 声明里transform: translate(-50%, -50%) rotate(-121deg)。最后检查 CSS 变量有没有生效。如果你改了.flag的宽度但没改--flag-width内部所有 calc 换算就全错了。这类问题在控制台里通常不会报错排查起来最费时间。所以开发展初期可以先用绝对像素值跑通再切换成变量。7.3 文字渐变不生效的浏览器差异background-clip: text在不同浏览器里的兼容表现不太一样最常见的现象是文字变成透明底、没有填充色或者背景渐变完全没显示出来。处理方式很简单语法里必须同时包含-webkit-background-clip: text和background-clip: text。前者照顾 Safari 和早期 Chromium 内核后者照顾新标准。同时检查color: transparent是不是被某些全局样式覆盖了比如 reset 样式里写了color: #333优先级更高的选择器会直接压过它。调试时先在控制台临时给.art-text加一个背景色块确认背景渐变本身正常显示再逐步排查裁剪和文字颜色。如果你是在邮件或者老旧 WebView 环境里使用这个效果大概率不支持background-clip: text此时要准备一个降级方案用纯色文字加阴影。视觉上虽然没有鎏金渐变但至少不会出现“文字隐身”的尴尬情况。7.4 缩放适配与高清屏经验最后一个坑来自高清屏和缩放。当你把页面缩放到 200% 或者放在 2x 屏上时clip-path的边缘可能会出现轻微的锯齿或毛边。这是正常的抗锯齿处理结果不用太在意。但如果锯齿特别明显可以给星形加一层细的outline或者drop-shadow从视觉上掩盖边缘噪点。另外动画里的background-position在高分辨率下可能会闪烁。经验是给.art-text加上transform: translateZ(0)把它提升到独立的合成层能明显减少动画过程中的重绘闪烁。这个技巧也叫“强制 GPU 加速”在做光影类动画时非常好用。但注意不要全局滥用合成层否则内存占用会上升。7.5 一个额外的小建议如果你想把项目做得更完整可以把五颗星的位置数据抽成数据结构用循环渲染出来。整个国旗的星位其实就是四组坐标加四个旋转角非常适合做成配置项。这样以后想换布局、换尺寸只需要改配置数据不需要改动 CSS 结构。不过那是进阶玩法了新手阶段先把静态版本敲出来每一步都弄明白再去做抽象优化收益会更大。我个人在实际操作中的体会是这类“纯 CSS 画图”的项目最忌讳一上来就追求全自动、全动态。先把静态效果做到位再慢慢加动效、加配置、加响应式每一步都能清楚地看到变化思路才不会乱。如果你在某个环节卡住了回到网格坐标、回到盒模型基本上都能找到答案。