CSS 形状与几何
为什么 CSS 形状是现代 UI 的细节体验
在现代 Web UI 设计中,超越直角矩形的设计越来越常见。圆角、斜切角、菱形、圆形——这些形状让界面更有趣味和现代感。更重要的是,CSS 形状不仅仅是视觉上的装饰,还涉及到文本环绕、裁剪区域、碰撞检测等功能性场景。
理解这些技术的边界和适用场景,是写出精细 UI 的基础。形状可以决定文本如何流动,可以创造独特的视觉效果,可以让动画更加流畅自然。
面试定位:CSS 形状不是高频面试考点,但它展示了 CSS 的图形能力边界。涉及 clip-path 动画性能、shape-outside 浮动环绕机制、border-radius 斜杠语法等问题时,体现了候选人对 CSS 渲染机制的深度理解。
clip-path 创建任意形状
clip-path 是 CSS 中最强大的形状创建工具,它通过定义一个裁剪区域来决定元素的哪些部分可见。理解 clip-path 的工作机制,对于实现复杂 UI 效果至关重要。
基本形状函数
.shapes {
/* 圆形 - 半径 at 圆心位置 */
clip-path: circle(50% at 50% 50%); /* 圆心在正中间,半径50% */
clip-path: circle(100px at 50% 50%); /* 指定像素半径 */
clip-path: circle(50% at 30% 70%); /* 圆心位置偏移 */
/* 椭圆形 - rx ry at cx cy */
clip-path: ellipse(50% 30% at 50% 50%); /* 水平50%宽度,垂直30%高度 */
clip-path: ellipse(100px 50px at 50% 50%); /* 明确指定两个半径 */
/* 矩形(inset)- 上右下左内边距 */
clip-path: inset(10px 20px 30px 40px); /* 基本用法 */
clip-path: inset(20px round 10px); /* 带圆角 */
clip-path: inset(0 round 0 100% 0 0); /* 斜切角效果 */
}
polygon 多边形
polygon() 函数是 clip-path 中最灵活的工具,通过定义多个顶点坐标来创建任意多边形。坐标使用百分比或像素值,以空格分隔。
.polygon {
/* 三角形 - 三个顶点构成 */
clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
/* 菱形 */
clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
/* 五边形 - 五个顶点 */
clip-path: polygon(50% 0%, 100% 38%, 82% 100%, 18% 100%, 0% 38%);
/* 六边形 */
clip-path: polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0% 50%);
/* 复杂多边形 - 星形 */
clip-path: polygon(
50% 0%, 61% 35%, 98% 35%, 68% 57%,
79% 91%, 50% 70%, 21% 91%, 32% 57%,
2% 35%, 39% 35%
);
}
斜切角与切角效果
斜切角(beveled corner)是现代 UI 中常见的效果,常用于导航栏、按钮、卡片头部:
/* 右上和左下斜切 */
.clip-skewed {
clip-path: polygon(
0 0, /* 左上顶点 */
100% 0, /* 右上顶点 */
100% calc(100% - 20px), /* 右下(减斜角高度) */
calc(100% - 20px) 100%, /* 右下内缩 */
0 100% /* 左下 */
);
}
/* 左侧斜切导航栏 */
.skewed-nav {
clip-path: polygon(5% 0%, 100% 0%, 95% 100%, 0% 100%);
background: #333;
color: white;
padding: 1rem 2rem;
}
使用 SVG path
path() 函数允许你使用 SVG 路径语法来定义复杂形状,这使得几乎任何几何形状都可以通过 clip-path 实现:
.svg-clip {
/* 矩形路径 */
clip-path: path("M 0 0 L 100 0 L 100 100 L 0 100 Z");
}
.svg-clip-curve {
/* 贝塞尔曲线 */
clip-path: path("M 50 0 C 100 0 100 50 50 100 C 0 50 0 0 50 0");
}
.svg-clip-heart {
/* 心形路径 */
clip-path: path("M 50 88 C 20 65 5 45 5 30 C 5 10 20 0 35 0 C 45 0 50 5 50 10 C 50 5 55 0 65 0 C 80 0 95 10 95 30 C 95 45 80 65 50 88 Z");
}
动画中的 clip-path
clip-path 的一个强大特性是它可以被动画化,前提是起始和结束状态的顶点数相同:
.morphable {
/* 初始状态:圆形 */
clip-path: circle(50% at 50% 50%);
transition: clip-path 0.5s ease-in-out;
}
.morphable:hover {
/* 悬停状态:多边形(顶点数必须相同) */
clip-path: polygon(
50% 0%, 61% 35%, 98% 35%, 68% 57%,
79% 91%, 50% 70%, 21% 91%, 32% 57%,
2% 35%, 39% 35%
);
}
/* 持续动画效果 */
.breathing-shape {
animation: breathe 3s ease-in-out infinite;
}
@keyframes breathe {
0%, 100% {
clip-path: circle(40% at 50% 50%);
}
50% {
clip-path: circle(50% at 50% 50%);
}
}
shape-outside 浮动环绕
shape-outside 是 CSS Shapes 规范中的核心属性,它定义了浮动元素周围的形状区域,让文本能够环绕这个形状而不是元素的矩形边界流动。
基本用法
.float-shape {
float: left;
shape-outside: circle(50%); /* 文本环绕圆形流动 */
shape-outside: ellipse(50% 30%); /* 文本环绕椭圆形流动 */
shape-outside: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%); /* 菱形 */
}
实际示例:图片文本环绕
<article class="wrapper">
<div class="float-element"></div>
<p>文本内容将环绕浮动元素的形状区域流动...</p>
</article>
.wrapper {
font: 1.2em / 1.5 sans-serif;
}
.float-element {
float: left;
width: 200px;
height: 200px;
shape-outside: circle(50%); /* 50%半径的圆形形状区域 */
shape-margin: 20px; /* 形状与文本之间的间距 */
}
参考盒子(Reference Box)
shape-outside 使用参考盒子来确定坐标系统,不同的参考盒子会影响形状的计算:
.reference-box-examples {
/* 使用 margin-box(默认)- 考虑外边距 */
shape-outside: margin-box circle(50%);
/* 使用 border-box - 考虑边框 */
shape-outside: border-box polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
/* 使用 padding-box - 考虑内边距 */
shape-outside: padding-box ellipse(50% 30%);
/* 使用 content-box - 仅内容区域 */
shape-outside: content-box inset(10px);
}
shape-image-threshold 图像阈值
当使用图像定义形状时,shape-image-threshold 设置 alpha 通道的阈值来决定哪些像素是形状的一部分:
.image-shape {
float: left;
width: 300px;
height: 300px;
shape-outside: url("mask.png");
shape-image-threshold: 0.5; /* alpha值大于0.5的像素才算作形状区域 */
}
渐变作为形状
使用 CSS 渐变可以创建复杂的形状区域:
.gradient-shape {
float: left;
width: 300px;
height: 300px;
/* 创建一个半圆形状区域 */
shape-outside: radial-gradient(
circle at 0% 50%,
transparent 50%,
black 50%
);
}
shape-margin 形状边距
shape-margin 在 shape-outside 定义的形状周围创建额外的间距,防止文本过于贴近形状边缘:
.shapes-with-margin {
float: left;
width: 200px;
height: 200px;
shape-outside: circle(50%);
shape-margin: 20px; /* 文本与圆形之间20px间距 */
shape-margin: 2em; /* 也可以使用 em 单位 */
}
/* 组合使用 */
.combined-shapes {
float: left;
shape-outside: polygon(
0 0,
100% 0,
50% 100%
);
shape-margin: 15px;
}
border-radius 高级用法
border-radius 不仅是创建圆角的工具,它的斜杠语法允许创建不对称圆角,实现椭圆和复杂形状。
差异化圆角
.different-radius {
/* 四个角不同的半径 */
border-radius: 10px 20px 30px 40px;
/* 对应:左上、右上、右下、左下 */
/* 对角线相同 */
border-radius: 50% 50% 0 0; /* 上方圆角50%,下方为0 */
}
/* 水平/垂直半径斜杠语法 */
.ellipse-radius {
border-radius: 50% / 50%; /* 圆形(两个方向半径相等) */
border-radius: 50% / 30%; /* 水平椭圆 */
border-radius: 30% / 50%; /* 垂直椭圆 */
/* 每个角不同的斜杠组合 */
border-radius: 10px 20px / 30px 40px;
/* 解释:
* 左上:水平10px,垂直30px
* 右上:水平20px,垂直40px
* 右下:水平10px,垂直30px
* 左下:水平20px,垂直40px
*/
}
百分比与像素混合
.percent-pixels {
/* 水平用百分比,垂直用像素 */
border-radius: 50% / 20px;
/* 复杂组合 */
border-radius: 100px 0 100px 0 / 50px 0 50px 0;
/* 形成一个胶囊形状的一半 */
}
复杂形状示例
/* 药丸形状 */
.pill {
border-radius: 999px; /* 超大值创建完美药丸形 */
}
/* 半圆 */
.semi-circle {
width: 200px;
height: 100px;
border-radius: 200px 200px 0 0; /* 上半圆 */
}
/* 椭圆 */
.ellipse {
width: 200px;
height: 100px;
border-radius: 50%; /* 自动形成椭圆 */
}
/* 月牙形状 - 使用 box-shadow 模拟 */
.moon {
width: 100px;
height: 100px;
border-radius: 50%;
box-shadow: 20px 20px 0 0 yellow;
}
实战:常见 UI 形状
头像
.avatar {
width: 60px;
height: 60px;
border-radius: 50%; /* 完美圆形 */
object-fit: cover; /* 保持图片比例 */
}
.avatar-rounded {
border-radius: 8px; /* 圆角方形 */
}
.avatar-pill {
border-radius: 999px; /* 胶囊形状 */
}
引用符号
.quote {
position: relative;
padding-left: 2.5rem;
}
.quote::before {
content: """;
position: absolute;
left: 0;
top: -0.25rem;
width: 40px;
height: 40px;
background: linear-gradient(135deg, #667eea, #764ba2);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 1.5rem;
}
斜切导航栏
.skewed-nav {
/* 使用 polygon 创建斜切效果 */
clip-path: polygon(3% 0%, 100% 0%, 97% 100%, 0% 100%);
background: linear-gradient(135deg, #1a1a2e, #16213e);
color: white;
padding: 1rem 3rem;
font-weight: 500;
transition: clip-path 0.3s ease;
}
.skewed-nav:hover {
/* 悬停时改变形状 */
clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%);
}
@media (max-width: 768px) {
.skewed-nav {
clip-path: none;
padding: 0.75rem 1.5rem;
}
}
锯齿效果
.sawtooth {
position: relative;
padding-bottom: 30px;
}
.sawtooth::after {
content: "";
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 30px;
background: linear-gradient(135deg, #667eea 25%, transparent 25%),
linear-gradient(225deg, #667eea 25%, transparent 25%);
background-size: 40px 40px;
}
圆角卡片与悬停效果
.rounded-card {
background: white;
border-radius: 16px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05), 0 10px 20px rgba(0, 0, 0, 0.08);
overflow: hidden;
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.rounded-card:hover {
transform: translateY(-4px);
box-shadow: 0 8px 12px rgba(0, 0, 0, 0.08), 0 16px 32px rgba(0, 0, 0, 0.12);
}
/* 内部元素继承圆角 */
.card-image {
width: 100%;
height: 200px;
object-fit: cover;
}