CSS 形状与几何

深入理解 CSS 形状的各种创建方法:clip-path、border-radius、shape-outside 浮动环绕、shape-margin 形状边距、以及如何用 CSS 创建复杂几何形状。

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;
}

延展阅读