技术知识文章集合TECHNICAL ARCHIVE · 457 DOCUMENTS

显示模式

登录
ARCHIVE DOCUMENTCSS

现代 CSS 解决方案:CSS 原生支持的三角函数

所属馆藏
CSS
文件格式
Markdown
原始路径
CSS/26-现代 CSS 解决方案:CSS 原生支持的三角函数
本文目录9 个章节
  1. 一、CSS 三角函数的基本语法
  2. 二、三角函数与运动轨迹
  3. 三、快速实现圆弧和圆周轨迹
  4. 四、使用三角函数生成波浪线
  5. 五、用曲线进行 CSS 创意绘图
  6. 六、动画和可访问性注意事项
  7. 七、兼容性和降级建议
  8. 总结
  9. 参考资料

现代 CSS 解决方案:CSS 原生支持的三角函数

Category(分类): CSS Status: 已整理

CSS 中有许多数学函数,可以通过计算生成长度、比例、颜色和动画参数。常见函数包括:

  • calc():对长度、百分比、数字等 CSS 值进行计算;
  • min():从多个值中取较小值;
  • max():从多个值中取较大值;
  • clamp():将值限制在最小值和最大值之间;
  • sin()cos()tan():根据角度计算正弦、余弦和正切值。

本文重点介绍 CSS 原生三角函数,以及它们在轨迹动画、圆弧、Loading、波浪线和 CSS 绘图中的应用。

一、CSS 三角函数的基本语法

sin()cos()tan() 属于 CSS Values and Units Level 4 中的数学函数。它们接收一个能够解析为角度或无单位数字的值,并返回无单位数字:

.box {
  width: calc(sin(30deg) * 100px);
  height: calc(cos(45deg) * 100px);
}

三角函数返回的结果具有以下特点:

  • sin()cos() 的结果范围通常是 -11
  • tan() 在接近 90deg270deg 等位置时可能变得非常大;
  • 无单位数字会按弧度解释;
  • 带角度单位的值可以直接使用。

常见角度单位:

.angle {
  --a: 30deg;
  --b: 0.5rad;
  --c: 100grad;
  --d: 0.25turn;
}

deg()rad() 不是这里需要使用的通用角度转换函数。通常直接使用 degradgradturn 单位即可。

不要把 tan() 直接用于 opacity

例如:

/* 不推荐:tan(60deg) 约为 1.732,超出常用 opacity 范围 */
.bad-example {
  opacity: tan(60deg);
}

如果需要将正弦结果映射到 01,可以这样写:

.good-example {
  opacity: calc((sin(30deg) + 1) / 2);
}

不过,透明度一般不需要使用三角函数,示例中更值得关注的是数值映射方式。

浏览器支持与渐进增强

现代 Chrome、Edge、Firefox 和 Safari 已经支持 CSS 三角函数,但较旧浏览器可能不支持。可以使用 @supports 做特性检测:

@supports (width: calc(1px * sin(45deg))) {
  .trig-effect {
    --supports-trig: 1;
  }
}

@property 属于可注册自定义属性相关能力,浏览器支持情况还需要单独确认。需要兼容旧浏览器时,应准备静态回退、JavaScript 计算或预编译后的 CSS。

二、三角函数与运动轨迹

三角函数最直观的用途之一是生成周期性运动。正弦和余弦曲线的取值会随着角度变化而变化:

  • sin() 适合生成上下摆动的值;
  • cos()sin() 相差一个相位;
  • 同时使用 sin()cos() 可以生成圆周轨迹。

正弦和余弦曲线示意图

1. 基础小球

<div class="g-single" aria-hidden="true"></div>
.g-single {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: #000;
}

基础小球示意图

2. 使用 @property 实现正弦轨迹

未注册的 CSS 自定义属性通常不能平滑插值。为了让角度和长度在关键帧之间连续变化,需要使用 @property 注册它们:

@property --distance {
  syntax: '<length>';
  inherits: false;
  initial-value: 0px;
}

@property --angle {
  syntax: '<angle>';
  inherits: false;
  initial-value: 0deg;
}

然后让小球沿水平方向移动,同时根据角度计算垂直偏移:

.g-single {
  position: relative;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: #000;
  animation: move-sine 5s infinite ease-in-out;
  transform: translate(
    calc(var(--distance) - 40vw),
    calc(5 * sin(var(--angle)) * 1em)
  );
}

@keyframes move-sine {
  from {
    --distance: 0px;
    --angle: 0deg;
  }

  to {
    --distance: 80vw;
    --angle: 1080deg;
  }
}

这里:

  • --distance0px 变化到 80vw,控制水平方向;
  • --angle0deg 变化到 1080deg,表示三个周期;
  • 5 * sin(var(--angle)) * 1em 控制竖直方向的振幅;
  • transform 只改变视觉位置,通常不会直接触发布局。

单个小球的正弦轨迹动画

3. 多个小球组成曲线

如果让多个小球沿同一轨迹运动,并为每个小球设置不同的动画延迟,就可以形成连续的波形效果。

HTML 可以由模板或 JavaScript 生成。示意结构如下:

<ul class="g-multi" aria-hidden="true">
  <li></li>
  <li></li>
  <!-- 实际项目中可以生成更多 li,例如 80 个 -->
</ul>

下面的重复代码使用了 Sass,因此代码块应标记为 scss,不能直接当作原生 CSS 使用:

$count: 80;

.g-multi {
  position: relative;
  width: 100%;
  height: 8em;
  margin: 0;
  padding: 0;
  list-style: none;
}

.g-multi li {
  position: absolute;
  top: 50%;
  left: 0;
  width: 20px;
  height: 20px;
  margin-top: -10px;
  border-radius: 50%;
  background: #000;
  animation: move-sine 5s infinite ease-in-out;
  transform: translate(
    calc(var(--distance) - 40vw),
    calc(5 * sin(var(--angle)) * 1em)
  );
}

@for $i from 1 through $count {
  .g-multi li:nth-child(#{$i}) {
    animation-delay: #{($i - 1) * -0.06}s;
  }
}

@keyframes move-sine {
  from {
    --distance: 0px;
    --angle: 0deg;
  }

  to {
    --distance: 80vw;
    --angle: 1080deg;
  }
}

如果要完全使用原生 CSS,可以在构建阶段由 JavaScript、模板循环或手写规则生成不同的 animation-delay,而不能直接使用 Sass 的 @for$count

多个小球组成的正弦曲线动画

三、快速实现圆弧和圆周轨迹

在没有三角函数之前,CSS 实现圆周运动通常需要嵌套元素、旋转父元素、负外边距或多个关键帧。使用 sin()cos() 后,可以直接根据角度计算横纵坐标。

圆弧运动的传统实现示意图

1. 小球沿圆周运动

<div class="orbit-dot" aria-hidden="true"></div>
@property --orbit-angle {
  syntax: '<angle>';
  inherits: false;
  initial-value: 0deg;
}

.orbit-dot {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 20px;
  height: 20px;
  margin: -10px;
  border-radius: 50%;
  background: #000;
  animation: orbit 3s infinite linear;
  transform: translate(
    calc(sin(var(--orbit-angle)) * 10vmin),
    calc(cos(var(--orbit-angle)) * 10vmin)
  );
}

@keyframes orbit {
  from {
    --orbit-angle: 0deg;
  }

  to {
    --orbit-angle: 360deg;
  }
}

sin()cos() 的结果都是无单位数字,乘以 10vmin 后就得到长度:

x = sin(angle) × radius
y = cos(angle) × radius

这里从 0deg360deg 会完成一周运动。使用 cos() 作为纵坐标时,小球初始位置会位于圆周上方;如果想改变起点,可以调整角度或交换两个函数。

小球沿圆周运动的动画

2. 使用三角函数制作 Loading

多个小球设置不同的动画延迟,就可以组成旋转 Loading。需要给容器明确的尺寸和定位上下文:

<ul class="loader" role="status" aria-label="加载中">
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
</ul>
$count: 10;

@property --loader-angle {
  syntax: '<angle>';
  inherits: false;
  initial-value: 0deg;
}

.loader {
  position: relative;
  width: 160px;
  height: 160px;
  margin: 0 auto;
  padding: 0;
  list-style: none;
}

.loader li {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 8px;
  height: 8px;
  margin: -4px;
  border-radius: 50%;
  animation: loader-orbit 3s infinite linear;
  transform: translate(
    calc(sin(var(--loader-angle)) * 60px),
    calc(cos(var(--loader-angle)) * 60px)
  );
}

@for $i from 1 through $count {
  .loader li:nth-child(#{$i}) {
    animation-delay: #{($i - 1) * -0.15}s;
    background: hsl(#{100 + $i * 15} 80% 60%);
  }
}

@keyframes loader-orbit {
  from {
    --loader-angle: 0deg;
  }

  to {
    --loader-angle: 360deg;
  }
}

这里的 @for 是 Sass 代码,负责生成不同的延迟和颜色。浏览器最终接收的是编译后的普通 CSS 与 @property、三角函数。

三角函数实现的 Loading 动画

四、使用三角函数生成波浪线

box-shadow 支持多个阴影。通过 Sass 循环生成一组不同位置的阴影,再用正弦函数计算横向偏移,就可以近似绘制波浪线。

这种方案的核心思路是:

  1. 沿纵向逐步增加阴影位置;
  2. 使用 sin() 计算每个阴影的横向偏移;
  3. 将所有阴影合并到同一个元素上;
  4. 通过多个元素和不同颜色生成多条曲线。

Sass 示例

@function shadow-set($step, $amplitude, $color) {
  $shadow: 0 0 0 0 $color;

  @for $i from 1 through 50 {
    $angle: $i * 15deg;
    $x: calc(sin(#{$angle}) * #{$amplitude});
    $y: $i * $step;

    $shadow: $shadow, #{$x} #{$y} 0 0 $color;
  }

  @return $shadow;
}

.wave {
  width: 10px;
  height: 10px;
  margin: 0 auto;
  border-radius: 50%;
  background: #f00;
  box-shadow: shadow-set(3px, 3px, #f00);
}

.wave:nth-child(2) {
  width: 6px;
  height: 6px;
  background: #fc0;
  box-shadow: shadow-set(3px, 3px, #fc0);
}

.wave:nth-child(3) {
  width: 4px;
  height: 4px;
  background: #000;
  box-shadow: shadow-set(2px, 3px, #000);
}
<div class="wave"></div>
<div class="wave"></div>
<div class="wave"></div>

编译后,一个元素可能包含几十个甚至更多阴影:

多重 box-shadow 生成波浪线

单条波浪线的阴影组成

这种方案的局限

  • 编译后的 CSS 体积较大;
  • 阴影数量越多,绘制成本越高;
  • 曲线精度和阴影尺寸之间需要权衡;
  • 不适合复杂、动态或大面积图形;
  • SVG、Canvas 或渐变可能更适合生产环境。

因此,这种技巧适合作为 CSS 实验、装饰性图案或小面积效果,不应默认作为所有波浪线的最佳方案。

五、用曲线进行 CSS 创意绘图

有了根据角度计算位置的能力后,可以把多个元素、阴影、渐变、圆角和裁剪结合起来,制作书签、鱼、海草和其他装饰性图案。

CSS 曲线创意图形示意图

示例参考:

CSS-doodle 曲线图形示意图

CSS 三角函数创意图形示意图

这些案例更适合用来理解数学函数和 CSS 绘图的结合。生产项目中还需要关注:

  • 动画是否影响低端设备;
  • 图形是否需要响应式缩放;
  • 是否需要支持减少动态效果的用户偏好;
  • 是否会造成大量阴影或图层;
  • 重要内容是否应该使用 SVG 或真实 HTML 结构表达。

六、动画和可访问性注意事项

如果动画不是内容传达的必要部分,应尊重用户的减少动态效果偏好:

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

装饰性小球、波浪线和 Loading 图形可以使用 aria-hidden="true"。如果 Loading 表示真实的加载状态,应提供可访问名称,例如:

<div class="loader" role="status" aria-label="正在加载">
  <span aria-hidden="true"></span>
</div>

不要仅使用复杂 CSS 图形表达重要信息,必要时应提供文本或其他语义化内容。

七、兼容性和降级建议

CSS 三角函数、@property 和复杂的 CSS 数学表达式应根据目标浏览器测试。可以准备以下降级方式:

  • 不支持三角函数时显示静态图形;
  • 使用 JavaScript 计算坐标;
  • 在构建阶段由 Sass 或脚本预计算数值;
  • 使用 SVG、Canvas 或图片作为回退;
  • 对装饰性动画关闭效果,而不是影响页面主要内容。

例如:

.orbit-dot {
  /* 不支持三角函数时的静态回退位置 */
  transform: translate(0, -10vmin);
}

@supports (transform: translate(calc(sin(45deg) * 1px))) {
  .orbit-dot {
    transform: translate(
      calc(sin(var(--orbit-angle)) * 10vmin),
      calc(cos(var(--orbit-angle)) * 10vmin)
    );
  }
}

总结

  • sin()cos()tan() 是现代 CSS 数学函数,不是 CSS3 的传统基础属性;
  • 三角函数可以直接使用 degradgradturn 等角度单位;
  • 无单位数字按弧度解释,不需要使用 deg()rad() 转换;
  • sin()cos() 适合生成周期运动,二者结合可以生成圆周轨迹;
  • 让 CSS 自定义属性平滑动画时,通常需要通过 @property 注册类型;
  • Sass 的 @for@function 和变量插值不能直接交给浏览器执行;
  • box-shadow 生成曲线很有趣,但编译后代码量和绘制成本可能较高;
  • SVG、Canvas、渐变和 clip-path 可能更适合生产环境中的复杂图形;
  • 使用动画时应考虑浏览器兼容性、性能、降级和 prefers-reduced-motion
  • CSS 数学函数增加了 CSS 的表现力,但也应根据场景控制复杂度。

参考资料

457 DOCUMENTS · 10 COLLECTIONS
ARCHIVE SEARCH457 篇文章

SEARCH GUIDE

输入关键词开始搜索

支持搜索文章标题、所属分类和原始文档路径。

按分类浏览

10 COLLECTIONS