前置知识: CSS

边框圆角

10 min入门

border-radius 完整原理:1-4 值语法、椭圆半径、百分比计算、圆角裁剪规则与常见形状实现。

前置知识

建议先阅读以下内容再进入本文:

1. 历史动机与发展脉络

圆角在平面设计中长期用于柔化界面。CSS 2.1 没有圆角能力,开发者只能使用背景图片(九个切片)模拟圆角,成本高且难以缩放。2005 年起 Mozilla 率先在 Firefox 中实现 -moz-border-radius,WebKit 随后跟进 -webkit-border-radius;2010 年 CSS Backgrounds and Borders Level 3 工作草案将 border-radius 标准化,2017 年该规范成为 W3C Recommendation。如今 border-radius 是支持度最完整的 CSS 属性之一。

规范演变中最重要的细节是椭圆半径与百分比:早期实现只支持长度值,百分比由 CSS3 引入,并规定了“相邻圆角重叠时等比缩小”的行为(Corners must not overlap 原则),保证任何尺寸下角部曲线都合法。

timeline
    title border-radius 演进
    2002 : CSS2 无圆角,使用背景图切片
    2005 : Firefox 实现 -moz-border-radius
    2007 : WebKit 实现 -webkit-border-radius
    2010 : CSS Backgrounds 3 草案标准化
    2017 : W3C Recommendation 发布
    2020+ : 所有现代浏览器无前缀支持

2. 形式化定义

border-radius 的正式语法:

border-radius: [ <length-percentage> ]{1,4} [ / [ <length-percentage> ]{1,4} ]?

一至四个值的分配规则与 margin/padding 相同(顺时针):一个值表示四角相同;两个值表示左上/右下、右上/左下;三个值表示左上、右上/左下、右下;四个值按左上、右上、右下、左下。

斜杠前为四个角的水平半径,斜杠后为垂直半径。只写一个值时垂直半径默认等于水平半径(正圆角);写斜杠时形成椭圆角。

长写属性:

border-top-left-radius、border-top-right-radius、border-bottom-right-radius、border-bottom-left-radius。每个长写属性接受一个或两个值(水平、垂直)。

百分比解析:水平百分比相对于元素内容盒加边框盒的宽度(即边框盒宽度),垂直百分比相对于高度。因此一个 50% 的水平半径加 50% 的垂直半径在矩形元素上形成椭圆,在正方形上形成圆。

重叠收缩规则:设角半径在对应边上的投影长度之和超过边长时,所有半径按同一比例缩小。例如宽度 100px、四个角水平半径均为 60px 时,各角收缩为 50px。

flowchart LR
    A["border-radius: 10px"] --> B["四角相同的小圆角"]
    C["border-radius: 50%"] --> D["正方形变圆形"]
    E["border-radius: 20px / 40px"] --> F["椭圆角:水平20 垂直40"]
    G["border-radius: 10px 20px 30px 40px"] --> H["四角依次不同"]

3. 理论推导与原理解析

3.1 椭圆参数方程

圆角曲线是四分之一椭圆弧。设水平半径 rx、垂直半径 ry,角部曲线上的点满足椭圆参数方程:

x=rx⋅cos⁡θ,y=ry⋅sin⁡θ,θ∈[0,π/2]x = rx \cdot \cos\theta,\quad y = ry \cdot \sin\theta,\quad \theta \in [0, \pi/2]

当 rx = ry 时退化为圆弧。浏览器绘制圆角时,把该曲线光栅化为路径;背景、边框、内阴影、外阴影都沿着这条路径裁剪或扩展。

3.2 百分比半径与盒子尺寸

半径百分比参照边框盒。对 200px 宽、100px 高的元素,border-radius: 50% 产生 rx=100px、ry=50px 的椭圆角,四角连接后元素内部剩余区域呈“胶囊竖切”形状。这也是为什么 50% 只在正方形上产生正圆。

3.3 相邻圆角的收缩推导

设上边长为 W,左上角水平半径 r1、右上角水平半径 r2。若 r1 + r2 > W,则按比例因子 f = W / (r1 + r2) 同时缩放两角(垂直半径同比例)。该规则保证角部曲线不相交,是 CSS 规范的强制行为,开发者无法覆盖。

4. 代码示例(带详尽注释)

4.1 基础圆角

.card {
  /* 四个角统一 12px 圆角 */
  border-radius: 12px;
}

.badge {
  /* 水平/垂直半径一致,形成正圆角 */
  border-radius: 50%;
  width: 48px;
  height: 48px;
}

讲解:12px 是卡片圆角的中性值;50% 配合正方形宽高形成圆形,是头像、状态点的标准写法。

4.2 胶囊按钮

.pill-button {
  /* 水平半径取高度一半(需要与高度联动) */
  border-radius: 999px;
  padding: 8px 24px;
  background: #1677ff;
  color: #fff;
  border: none;
}

讲解:999px 是“足够大”的半径,浏览器会自动收缩到高度一半,形成胶囊形状。该写法无需精确计算高度,是弹性高度的推荐方案。

4.3 椭圆角与局部圆角

.dialog {
  /* 水平 16px、垂直 32px:顶部更“圆”的椭圆角 */
  border-radius: 16px / 32px;
}

.tab {
  /* 只圆顶部两角:贴合标签页设计 */
  border-radius: 10px 10px 0 0;
}

.speech-bubble {
  /* 左上角小圆角,其余角大圆角:聊天气泡 */
  border-radius: 4px 16px 16px 16px;
}

讲解:border-radius 的四值顺序是左上、右上、右下、左下。聊天气泡的典型处理是“指向源头的一角更尖”。

4.4 与 overflow 配合裁剪图片

<div class="avatar-frame">
  <img src="avatar.jpg" alt="用户头像">
</div>
.avatar-frame {
  width: 96px;
  height: 96px;
  border-radius: 50%;
  /* 关键:溢出裁剪,让图片跟随圆角 */
  overflow: hidden;
}
.avatar-frame img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

讲解:border-radius 只作用于元素自身的背景与边框,不会自动裁剪子内容;必须配合 overflow: hidden 才能让图片呈现圆形。object-fit: cover 保证图片不变形地填满。

4.5 响应式椭圆

.ellipse {
  /* 宽度百分比半径:随容器宽度变化 */
  border-radius: 50% / 25%;
  aspect-ratio: 2 / 1;
  background: linear-gradient(135deg, #36cfc9, #1677ff);
}

讲解:50% / 25% 表示水平半径是宽度一半、垂直半径是高度四分之一。配合 aspect-ratio 固定宽高比,可以构造稳定的椭圆装饰。

4.6 圆角与阴影/边框的配合

.elevated {
  border: 2px solid #d9d9d9;
  border-radius: 16px;
  /* 阴影形状跟随圆角路径 */
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
}

讲解:box-shadow 与 border-radius 共享同一路径计算,阴影自动贴合圆角。但注意:outline 在旧浏览器中不贴合圆角;现代浏览器(Chrome 94+、Firefox 88+)的 outline 已跟随圆角。

4.7 设计令牌管理

:root {
  /* 圆角梯度令牌:小、中、大、全圆 */
  --radius-sm: 4px;
  --radius-md: 8px;
  --radius-lg: 16px;
  --radius-full: 999px;
}

.button { border-radius: var(--radius-md); }
.card { border-radius: var(--radius-lg); }
.avatar { border-radius: var(--radius-full); }

讲解:把圆角收敛为有限梯度,保证全站视觉一致性,也方便主题切换。这是设计系统的基础实践。

5. 对比分析

5.1 border-radius 与 clip-path

维度border-radiusclip-path
形状椭圆/圆角矩形任意多边形、路径
内容裁剪不裁剪子内容裁剪整个元素
阴影跟随是否(clip 裁剪阴影)
性能极好好(复杂路径略差)
典型场景卡片、头像异形图形、动画遮罩

5.2 简写与长写属性对比

简写可读性好,但会同时重置四个角;长写属性可以精确控制单个角。动画中若只改变一个角,使用长写属性避免隐式重置。

5.3 圆角与 border-image 的冲突

border-image 与 border-radius 不兼容:使用 border-image 时圆角失效。需要同时满足时,用嵌套元素或 SVG 背景替代。

6. 常见陷阱与最佳实践

陷阱一:忘记 overflow: hidden,子图片溢出圆角。

陷阱二:border-radius: 50% 用于非正方形元素得到椭圆,误以为是圆。用 aspect-ratio: 1/1 固定正方形。

陷阱三:圆角值过大时浏览器自动收缩,与预期不符。理解收缩规则后按设计意图选择值。

陷阱四:在 border-image 元素上使用圆角,圆角被忽略。

陷阱五:为性能过度使用大半径阴影。圆角阴影成本可接受,但避免在滚动容器内大量叠加。

陷阱六:border-radius 对 table 元素(border-collapse: collapse)不生效。需要给 td 或使用 border-spacing: 0 方案。

最佳实践:设计令牌统一管理半径;头像与胶囊用 50%/999px;图片裁剪记得 overflow;动画优先长写属性。

7. 工程实践

7.1 主题化圆角

// theme.ts:设计令牌类型约束
export const radii = {
  sm: '4px',
  md: '8px',
  lg: '16px',
  full: '999px'
} as const

export type RadiusToken = keyof typeof radii

讲解:类型约束防止团队使用随意数值,配合 CSS 变量实现运行时主题切换。

7.2 圆角头像组件

<script setup>
defineProps<{
  src: string
  alt: string
  size?: number
  round?: boolean
}>()
</script>

<template>
  <span class="avatar" :style="{ width: size + 'px', height: size + 'px' }"
        :class="{ round: round }">
    <img :src="src" :alt="alt" />
  </span>
</template>

<style scoped>
.avatar {
  display: inline-block;
  overflow: hidden; /* 裁剪图片到圆角内 */
  border-radius: 8px; /* 默认小圆角 */
}
.avatar.round {
  border-radius: 50%; /* 圆形模式 */
}
.avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
</style>

讲解:组件把“圆角+裁剪+图片填充”三件套封装,调用方只传尺寸与形状模式,避免每个页面重复踩坑。

8. 案例研究:环形进度与圆角卡片

场景一:圆形进度环。用圆角 50% 的容器加 conic-gradient 实现:

.ring {
  width: 120px;
  height: 120px;
  border-radius: 50%;
  /* 圆锥渐变从 0% 到 75% 着色,其余灰色 */
  background: conic-gradient(#1677ff 0% 75%, #f0f0f0 75% 100%);
  display: grid;
  place-items: center;
}
.ring::before {
  content: "75%";
  width: 96px;
  height: 96px;
  border-radius: 50%;
  background: #fff;
  display: grid;
  place-items: center;
}

讲解:外层圆环用圆锥渐变绘制进度,内层伪元素盖出中心孔。圆角在这里承担“所有元素都是正圆”的几何保证。

场景二:嵌套卡片圆角比例。外层 16px、内层 10px 的视觉层次:

.outer {
  border-radius: 16px;
  padding: 12px;
  background: #fafafa;
}
.inner {
  border-radius: 10px;
  padding: 16px;
  background: #fff;
}

讲解:嵌套圆角遵循“内层半径 ≈ 外层半径 - padding”的经验公式,视觉上保持平行曲线。12px padding 对应 4px 差值,曲线近似同心,观感统一。

9. 知识要点总结与深入讲解

border-radius 的语法分两段:前段四角水平半径,后段(斜杠后)垂直半径。一值全同、二值对角、三值、四值顺时针,与 margin 的记忆方式完全一致。

百分比永远参照边框盒宽高,所以 50% 在正方形上是圆、在矩形上是椭圆。想要“正圆”必须保证元素本身是正方形。

圆角不会裁剪子内容,overflow: hidden 才负责裁剪。阴影与背景跟随圆角,outline 在现代浏览器中也跟随。理解这些边界行为,才能避免“圆角了但图片还是方的”这类问题。

1. border-radius 语法

.box {
  border-radius: 10px;
}
.box {
  border-radius: 10px 20px 30px 40px;
} /* 左上 右上 右下 左下 */
.box {
  border-radius: 50px / 20px;
} /* 水平/垂直半径 */

1. 常见形状

.circle {
  border-radius: 50%;
}
.pill {
  border-radius: 9999px;
}
.leaf {
  border-radius: 0 100% 0 100%;
}
.diagonal {
  border-radius: 50% 0 50% 0;
}
.blob {
  border-radius: 60% 40% 30% 70% / 60% 30% 70% 40%;
}

2. 实战效果

.bubble {
  border-radius: 12px;
  border-bottom-left-radius: 2px;
}
.card {
  border-radius: 8px;
  overflow: hidden;
}
.button {
  border-radius: 6px;
  transition: border-radius 0.3s;
}
.button:hover {
  border-radius: 12px;
}

3. 注意事项

  • 百分比参照元素尺寸
  • 圆角不会裁剪溢出内容(需配合 overflow: hidden)
  • 表格 border-collapse: collapse 时圆角无效

border-radius 基础

基本写法:统一圆角 border-radius: <值>;

/* 四个角相同圆角 */
.box {
  border-radius: 8px;
}

基本写法:百分比圆角 border-radius: <百分比>;

/* 使用百分比圆角 */
.box {
  border-radius: 50%;
}

基本写法:圆形 border-radius: 50%;

/* 创建圆形元素 */
.avatar {
  width: 100px;
  height: 100px;
  border-radius: 50%;
}

基本写法:无圆角 border-radius: 0;

/* 移除圆角 */
.box {
  border-radius: 0;
}

border-radius 多值

基本写法:双值圆角 border-radius: <对角1> <对角2>;

/* 左上右下 和 右上左下 */
.box {
  border-radius: 10px 20px;
}

基本写法:三值圆角 border-radius: <左上> <右上左下> <右下>;

/* 三个值设置圆角 */
.box {
  border-radius: 10px 20px 30px;
}

单行写法:四值圆角 border-radius: <左上> <右上> <右下> <左下>;

/* 单行设置四个角不同圆角 */
.box {
  border-radius: 10px 20px 30px 40px;
}

换行写法:四值圆角 border-top-left-radius: <值>; border-top-right-radius: <值>; border-bottom-right-radius: <值>; border-bottom-left-radius: <值>;

/* 换行设置四个角不同圆角 */
.box {
  border-top-left-radius: 10px;
  border-top-right-radius: 20px;
  border-bottom-right-radius: 30px;
  border-bottom-left-radius: 40px;
}

单角圆角

基本写法:左上角圆角 border-top-left-radius: <值>;

/* 仅设置左上角圆角 */
.box {
  border-top-left-radius: 10px;
}

基本写法:右上角圆角 border-top-right-radius: <值>;

/* 仅设置右上角圆角 */
.box {
  border-top-right-radius: 10px;
}

基本写法:右下角圆角 border-bottom-right-radius: <值>;

/* 仅设置右下角圆角 */
.box {
  border-bottom-right-radius: 10px;
}

基本写法:左下角圆角 border-bottom-left-radius: <值>;

/* 仅设置左下角圆角 */
.box {
  border-bottom-left-radius: 10px;
}

椭圆圆角

基本写法:椭圆角 border-radius: <水平> / <垂直>;

/* 设置椭圆角 */
.box {
  border-radius: 50% / 30%;
}

基本写法:单角椭圆 border-top-left-radius: <水平> <垂直>;

/* 左上角椭圆 */
.box {
  border-top-left-radius: 50px 25px;
}

基本写法:多角椭圆 border-radius: <水平1> <水平2> / <垂直1> <垂直2>;

/* 多角椭圆 */
.box {
  border-radius: 50px 20px / 25px 10px;
}

border 边框

基本写法:完整边框 border: <宽度> <样式> <颜色>;

/* 设置完整边框 */
.box {
  border: 1px solid #ccc;
}

基本写法:border-width 单值 border-width: <值>;

/* 设置四条边框宽度 */
.box {
  border-width: 2px;
}

基本写法:border-width 多值 border-width: <上> <右> <下> <左>;

/* 分别设置四条边框宽度 */
.box {
  border-width: 1px 2px 3px 4px;
}

基本写法:border-style 实线 border-style: solid;

/* 设置边框样式为实线 */
.box {
  border-style: solid;
}

基本写法:border-style 虚线 border-style: dashed;

/* 设置边框样式为虚线 */
.box {
  border-style: dashed;
}

基本写法:border-style 点线 border-style: dotted;

/* 设置边框样式为点线 */
.box {
  border-style: dotted;
}

基本写法:border-style 双线 border-style: double;

/* 设置边框样式为双线 */
.box {
  border-style: double;
}

基本写法:border-color 边框颜色 border-color: <颜色>;

/* 设置边框颜色 */
.box {
  border-color: #007bff;
}

单边边框

基本写法:顶边边框 border-top: <宽度> <样式> <颜色>;

/* 仅设置顶边边框 */
.box {
  border-top: 2px solid red;
}

基本写法:右边边框 border-right: <宽度> <样式> <颜色>;

/* 仅设置右边边框 */
.box {
  border-right: 2px solid red;
}

基本写法:底边边框 border-bottom: <宽度> <样式> <颜色>;

/* 仅设置底边边框 */
.box {
  border-bottom: 2px solid red;
}

基本写法:左边边框 border-left: <宽度> <样式> <颜色>;

/* 仅设置左边边框 */
.box {
  border-left: 2px solid red;
}

基本写法:无边框 border: none;

/* 移除边框 */
.no-border {
  border: none;
}

border-image 边框图片

基本写法:border-image 简写 border-image: url("<图片>") <切片> <重复>;

/* 使用图片作为边框 */
.box {
  border: 10px solid transparent;
  border-image: url("border.png") 30 round;
}

基本写法:border-image-source 图片源 border-image-source: url("<图片>");

/* 设置边框图片源 */
.box {
  border-image-source: url("border.png");
}

基本写法:border-image-slice 切片 border-image-slice: <值>;

/* 设置边框图片切片 */
.box {
  border-image-slice: 30;
}

基本写法:border-image-width 宽度 border-image-width: <值>;

/* 设置边框图片宽度 */
.box {
  border-image-width: 10px;
}

基本写法:border-image-outset 外延 border-image-outset: <值>;

/* 设置边框图片外延 */
.box {
  border-image-outset: 5px;
}

基本写法:border-image-repeat 重复 border-image-repeat: round;

/* 边框图片平铺方式 */
.box {
  border-image-repeat: round;
}

outline 轮廓

基本写法:outline 完整轮廓 outline: <宽度> <样式> <颜色>;

/* 设置元素轮廓(不占空间) */
.input:focus {
  outline: 2px solid #007bff;
}

基本写法:outline-offset 偏移 outline-offset: <值>;

/* 设置轮廓与元素的距离 */
.button:focus {
  outline: 2px solid blue;
  outline-offset: 4px;
}

基本写法:outline-style 样式 outline-style: <样式>;

/* 设置轮廓样式 */
.box {
  outline-style: solid;
}

基本写法:outline-width 宽度 outline-width: <值>;

/* 设置轮廓宽度 */
.box {
  outline-width: 2px;
}

基本写法:outline-color 颜色 outline-color: <颜色>;

/* 设置轮廓颜色 */
.box {
  outline-color: #007bff;
}

基本写法:移除轮廓 outline: none;

/* 移除默认轮廓 */
.input:focus {
  outline: none;
}

常见圆角效果

基本写法:胶囊形 border-radius: <高度>;

/* 创建胶囊形按钮 */
.pill {
  height: 40px;
  border-radius: 20px;
}

基本写法:顶部圆角 border-radius: <值> <值> 0 0;

/* 仅顶部圆角 */
.card-top {
  border-radius: 10px 10px 0 0;
}

基本写法:底部圆角 border-radius: 0 0 <值> <值>;

/* 仅底部圆角 */
.card-bottom {
  border-radius: 0 0 10px 10px;
}

基本写法:左侧圆角 border-radius: <值> 0 0 <值>;

/* 仅左侧圆角 */
.card-left {
  border-radius: 10px 0 0 10px;
}

基本写法:右侧圆角 border-radius: 0 <值> <值> 0;

/* 仅右侧圆角 */
.card-right {
  border-radius: 0 10px 10px 0;
}

基本写法:不对称圆角 border-radius: <值1> <值2> <值3> <值4>;

/* 创建不对称圆角 */
.blob {
  border-radius: 60% 40% 30% 70% / 60% 30% 70% 40%;
}

响应式圆角

基本写法:clamp 响应式圆角 border-radius: clamp(<最小>, <理想>, <最大>);

/* 响应式圆角 */
.box {
  border-radius: clamp(4px, 2vw, 16px);
}

基本写法:媒体查询调整圆角 @media (max-width: <值>) { border-radius: <值>; }

/* 小屏幕调整圆角 */
.box {
  border-radius: 16px;
}
@media (max-width: 768px) {
  .box {
    border-radius: 8px;
  }
}

基本写法:嵌套媒体查询圆角 .box { border-radius: <值>; @media (max-width: <值>) { border-radius: <值>; } }

/* CSS 原生嵌套媒体查询圆角 */
.box {
  border-radius: 16px;
  @media (max-width: 768px) {
    border-radius: 8px;
  }
}

动手试试

  1. 给一张图片写 border-radius: 50% 变成圆形头像;
  2. 用四个值 10px 20px 30px 40px 观察每个角的变化;
  3. 用斜杠语法 50% / 25% 做椭圆角卡片;
  4. 进阶挑战:结合 overflow: hidden 做圆角图片容器。

核心知识点

一句话记住圆角:border-radius 一到四个值对应四个角(顺时针),斜杠 / 分隔水平与垂直半径;50% 是圆/胶囊的关键。

  • 四个值:左上、右上、右下、左下(顺时针);
  • 两个值:对角一组;三个值:左上加右下、右上加左下、右下;
  • 斜杠语法:水平 / 垂直,可做椭圆角;
  • border-radius: 50% 正方形变圆、胶囊形变胶囊;
  • 配合 overflow: hidden 让图片/子元素跟随圆角裁剪。

注意事项与改进建议

问题点说明改进方案
圆角不生效元素没有背景/边框圆角需要可见盒或背景衬托
图片直角圆角被图片盖住容器 overflow: hidden
百分比与像素混用结果难预测统一用百分比或像素
大圆角卡顿裁剪开销避免超大圆角配合阴影动画

扩展学习

  • 盒模型:css/050-CSS3BoxModelDetailed;
  • 阴影:css/270-Shadow;
  • 背景裁剪:css/290-BackgroundEnhancement。