border-radius 之前:圆角是图片
在 border-radius 进入浏览器(约 2010)之前,CSS 里没有任何属性能画一条弧线。设计稿上一个 4px 的圆角,落到实现就是一道工序:先画一张带圆角的图,再想办法把它贴到一个宽高可变的盒子上——因为图片尺寸固定,而内容会伸缩。这一页回顾那套「切图工程」:用
SVG 画圆角矩形、用 resvg 烘焙成 PNG、用滑动门(sliding doors)让两张图随文字滑动、用 OOCSS 把四个角封装成可复用的「角对象」。
1 · 第一步:画一张圆角图
圆角图最简便的画法是 SVG 的 <rect> + rx / ry——矢量、参数化。但当年浏览器(IE6~8)不认 SVG 背景,且为求像素精确(各引擎抗锯齿不一),工作流是把 SVG 用栅格化工具烘焙成定尺 PNG 再上线。本页这些 PNG
是用命令行工具 resvg(resvg -w 220 -h 130 box.svg box-r36.png)从同一份 SVG 生成的真实文件。切到 PNG 档,演示的正是「没有 border-radius 时,纯靠一张位图得到圆角」。
为什么要烘焙成 PNG?矢量 SVG 在当年有两个坎:其一,IE8 及更早不支持把 SVG 当 CSS 背景;其二,设计师要求圆角在所有设备上逐像素一致,而早期各引擎对 SVG 的抗锯齿处理不同。用 resvg 这类栅格化器把 SVG 渲染成固定分辨率的 PNG
就绕开了这两点——代价是每个半径都是一张单独的图:本页 PNG 档只有 box-r0 / 12 / 24 / 36 / 48 / 60.png 六张烘焙好的文件,滑块会吸附到最近的一档。想要 37px?对不起,没这张图——改一次半径就得重新跑一次 resvg、重新上传。圆角的「样式」被固化进了二进制资产。
2 · 滑动门:让一张固定图适配可变宽度
圆角图有了,新问题是:按钮 / 标签的宽度由文字决定,而图片宽度是固定的。Douglas Bowman 2003 年在 A List Apart 提出的滑动门解法:把同一张(或两张)图分别钉在盒子的左右两端——左边那扇「门」露出左圆角,右边那扇露出右圆角,中间是两张图重叠的纯色平直段。文字一多,盒子变宽,两扇门向两侧滑开,中间平直段自然延长,而圆角永远贴着两端。
关键技巧:两扇门各管一个圆角。外层盒子用窄窄的 door-right.png(只含右圆角)钉在右端;内层用够宽的 door-left.png(左圆角 +
向右延伸的纯色身体)钉在左端,并在右侧留出宽度让右门露出来。两张图的圆角处都是透明的,且谁的背后都没有不透明色块遮挡——这正是关键:若像早期那样给盒子加一层
background-color 填底,透明圆角就会被填成方角。文字一多,内层变宽把右门往右推、中间纯色身体延长,两端圆角纹丝不动。切到「露出边界」看两扇门的交接线。
3 · 四角拼贴与 OOCSS 的「角对象」
滑动门只管水平伸缩(左右两扇门),要让盒子宽高都能变,就把四个圆角各做一张小图,分别钉在四个角,中间用可平铺的纯色 / 渐变填满——这就是「九宫格」(9-slice)的雏形。Nicole Sullivan 的 OOCSS(Object-Oriented
CSS,2009)把这套结构封装成可复用对象:一组与内容无关的嵌套 div + 角图,套到任意盒子上都能长出圆角,不必改内容的 HTML 语义。
OOCSS 的两条原则,正好对上圆角这件事。其一分离结构与皮肤(structure vs skin):圆角是「皮肤」,可独立成一个 .box-rounded 对象,与盒子的尺寸 / 定位(结构)无关;其二分离容器与内容(container vs
content):角对象不关心里面装的是表单还是文章,任何内容塞进去都能获得同一套圆角。这种「可复用、与内容解耦」的思路,正是后来组件化 CSS 的先声。
3.1 · border-radius 之前的几种圆角方案
| 方案 | 怎么做 | 代价 |
|---|---|---|
| 纯图片 | 整块背景就是一张带圆角的 PNG | 宽高写死,内容一变就破图;改半径要重新出图 |
| 滑动门 | 左右两张图随文字宽度滑动,中段平直延伸 | 只解决水平伸缩;需要嵌套元素与精确背景定位 |
| 四角 / 九宫格 | 四角图 + 可平铺边 / 中心,宽高皆可变 | 最多要 4~9 个额外 div,HTML 被「皮肤」污染 |
| OOCSS 角对象 | 把四角结构封装成可复用对象,套任意内容 | 仍是图片 + 多层 div;无法响应文字色 / 主题色变化 |
| 1px 阶梯 hack | 用几条高度递减的 div 模拟出锯齿状「伪圆角」 | 只在纯色背景下凑效,放大即露馅,不抗锯齿 |
私有前缀的过渡期。 border-radius 标准化之前,各引擎先用私有前缀抢跑:Gecko 是 -moz-border-radius、WebKit 是 -webkit-border-radius,且早期 -moz- 的角 longhand 命名都不一样(-moz-border-radius-topleft
而非 -moz-border-top-left-radius)。那几年的写法常是「三行叠一个圆角」:-webkit- / -moz- / 标准 各写一遍——这也是 Autoprefixer 这类工具诞生的土壤。
4 · 下一步
切图、滑动门、角对象——全部是在「图片是固定的、内容是可变的」这对矛盾里打转。看 border-radius 如何用一行声明把圆角变成渲染时计算的矢量弧,彻底取消这道工序。