CSS Trigonometric Functions(三角函数):在样式表中进行复杂几何计算

CSS Trigonometric Functions(三角函数):在样式表中进行复杂几何计算

大家好,今天我们要深入探讨一个CSS中相对较新但功能强大的特性:CSS Trigonometric Functions,即CSS三角函数。传统上,CSS主要用于布局和样式,但随着calc()函数和自定义属性(CSS Variables)的引入,以及现在三角函数的加入,我们能够进行更复杂的几何计算,从而实现更灵活、更动态的视觉效果。

1. CSS三角函数概述

CSS三角函数允许我们在CSS中使用sin(), cos(), tan(), asin(), acos(), 和 atan() 函数。这些函数可以接受角度作为参数,并返回对应的三角函数值。这些返回值可以用于计算长度、宽度、位置等CSS属性,从而创建复杂的动画、响应式设计,以及基于角度的布局。

这些函数的出现极大地扩展了CSS的能力,使得无需JavaScript即可实现一些原本需要脚本才能完成的复杂效果。

2. 支持的三角函数及参数

CSS支持以下三角函数:

  • sin(angle): 返回给定角度的正弦值。
  • cos(angle): 返回给定角度的余弦值。
  • tan(angle): 返回给定角度的正切值。
  • asin(number): 返回给定数值的反正弦值(以弧度表示)。
  • acos(number): 返回给定数值的反余弦值(以弧度表示)。
  • atan(number): 返回给定数值的反正切值(以弧度表示)。
  • atan2(y, x): 返回点 (x, y) 的反正切值,考虑了象限。这是计算角度的更精确方式。

角度单位:

这些函数接受的角度可以使用以下单位:

  • deg: 度数 (degrees)
  • rad: 弧度 (radians)
  • grad: 百分度 (gradians)
  • turn: 圈数 (turns)

返回值:

  • sin(), cos(), tan() 返回一个介于 -1 和 1 之间的数值,或无穷大。
  • asin(), acos() 返回一个弧度值。
  • atan() 返回一个弧度值。
  • atan2() 返回一个弧度值。

兼容性:

需要注意的是,截至目前,CSS三角函数的支持情况并不一致。在使用前,请务必检查目标浏览器的兼容性。Can I Use 网站是一个很好的资源,可以查看不同浏览器的支持情况。

3. 基本用法示例

让我们从一些简单的例子开始,理解如何在CSS中使用三角函数。

示例 1: 使用 sin()cos() 创建简单的动画

<!DOCTYPE html>
<html>
<head>
<style>
  .circle {
    width: 50px;
    height: 50px;
    border-radius: 50%;
    background-color: red;
    position: absolute;
    left: 0;
    top: 0;
    animation: moveCircle 5s linear infinite;
  }

  @keyframes moveCircle {
    0% {
      left: calc(100px + 50px * cos(0deg));
      top: calc(100px + 50px * sin(0deg));
    }
    100% {
      left: calc(100px + 50px * cos(360deg));
      top: calc(100px + 50px * sin(360deg));
    }
  }
</style>
</head>
<body>
  <div class="circle"></div>
</body>
</html>

在这个例子中,我们创建了一个红色的圆圈,并使用 animation 属性使其移动。@keyframes 中,我们使用 sin()cos() 函数来计算圆圈的 lefttop 位置,使其围绕一个中心点旋转。 圆的中心点是 (100px, 100px), 半径是 50px.

示例 2: 动态调整元素大小

<!DOCTYPE html>
<html>
<head>
<style>
  :root {
    --angle: 0deg;
  }

  .box {
    width: calc(100px + 50px * sin(var(--angle)));
    height: calc(100px + 50px * cos(var(--angle)));
    background-color: blue;
    animation: changeSize 2s linear infinite;
  }

  @keyframes changeSize {
    0% {
      --angle: 0deg;
    }
    100% {
      --angle: 360deg;
    }
  }
</style>
</head>
<body>
  <div class="box"></div>
</body>
</html>

这里,我们使用 CSS 变量 --angle 来控制元素的宽度和高度。 sin() 函数影响宽度,cos() 函数影响高度。通过动画改变 --angle 的值,我们就可以动态地调整元素的大小。

示例 3: 使用 atan2() 计算角度

假设我们要创建一个指向鼠标的光标。

<!DOCTYPE html>
<html>
<head>
<style>
  body {
    height: 100vh;
    display: flex;
    justify-content: center;
    align-items: center;
  }

  .arrow {
    width: 50px;
    height: 10px;
    background-color: black;
    transform-origin: left center;
  }
</style>
</head>
<body>
  <div class="arrow"></div>

  <script>
    document.addEventListener('mousemove', (e) => {
      const arrow = document.querySelector('.arrow');
      const x = e.clientX - arrow.offsetLeft;
      const y = e.clientY - arrow.offsetTop; // Adjusted for element offset
      const angle = Math.atan2(y, x) * 180 / Math.PI; // Convert radians to degrees
      arrow.style.transform = `rotate(${angle}deg)`;
    });
  </script>
</body>
</html>

在这个例子中,虽然角度计算主要是在 JavaScript 中完成的,但它展示了 atan2() 的基本原理。 atan2(y, x) 返回点 (x, y) 相对于原点的角度。 我们需要将弧度转换为角度,才能用于 rotate() 变换。

注意:由于CSS本身无法直接监听鼠标事件,因此角度计算只能通过JavaScript完成,然后将角度值传递给CSS变量。 如果CSS支持事件监听,则可以在CSS中完成整个计算。

4. 高级应用:极坐标布局

CSS三角函数的一个强大应用是创建极坐标布局。 极坐标布局是一种基于角度和半径来定位元素的布局方式。

示例: 创建一个圆形菜单

<!DOCTYPE html>
<html>
<head>
<style>
  .container {
    position: relative;
    width: 200px;
    height: 200px;
    border-radius: 50%;
    margin: 50px auto; /* Center the container */
  }

  .item {
    position: absolute;
    width: 50px;
    height: 50px;
    border-radius: 50%;
    background-color: lightblue;
    text-align: center;
    line-height: 50px;
    color: white;
  }

  /* Using CSS variables and calc() for positioning */
  :root {
    --radius: 75px; /* Radius of the circle */
  }

  .item:nth-child(1) {
    top: calc(var(--radius) + var(--radius) * sin(0deg) - 25px);
    left: calc(var(--radius) + var(--radius) * cos(0deg) - 25px);
  }

  .item:nth-child(2) {
    top: calc(var(--radius) + var(--radius) * sin(60deg) - 25px);
    left: calc(var(--radius) + var(--radius) * cos(60deg) - 25px);
  }

  .item:nth-child(3) {
    top: calc(var(--radius) + var(--radius) * sin(120deg) - 25px);
    left: calc(var(--radius) + var(--radius) * cos(120deg) - 25px);
  }

  .item:nth-child(4) {
    top: calc(var(--radius) + var(--radius) * sin(180deg) - 25px);
    left: calc(var(--radius) + var(--radius) * cos(180deg) - 25px);
  }

  .item:nth-child(5) {
    top: calc(var(--radius) + var(--radius) * sin(240deg) - 25px);
    left: calc(var(--radius) + var(--radius) * cos(240deg) - 25px);
  }

  .item:nth-child(6) {
    top: calc(var(--radius) + var(--radius) * sin(300deg) - 25px);
    left: calc(var(--radius) + var(--radius) * cos(300deg) - 25px);
  }
</style>
</head>
<body>
  <div class="container">
    <div class="item">1</div>
    <div class="item">2</div>
    <div class="item">3</div>
    <div class="item">4</div>
    <div class="item">5</div>
    <div class="item">6</div>
  </div>
</body>
</html>

在这个例子中,我们使用 sin()cos() 函数以及 CSS 变量 --radius 来计算每个菜单项的 topleft 位置。 每个菜单项都围绕容器的中心点以一定的角度排列。公式 top: calc(var(--radius) + var(--radius) * sin(angle) - 25px);left: calc(var(--radius) + var(--radius) * cos(angle) - 25px); 将极坐标 (radius, angle) 转换为笛卡尔坐标 (x, y)。其中,var(--radius)是容器的中心坐标,sin(angle)cos(angle)计算相对于中心点的偏移,- 25px 是因为item的宽高均为50px,需要减去半径让中心点在计算的坐标上。

更灵活的方式:使用CSS变量和循环(需要预处理器,如Sass)

虽然上面的例子可以工作,但如果菜单项的数量发生变化,我们需要手动修改CSS。 为了解决这个问题,我们可以使用 CSS 预处理器(例如 Sass 或 Less)来生成 CSS。

首先,我们需要一个循环来生成每个菜单项的样式。以下是使用Sass的示例:

$item-count: 6;
$radius: 75px; // Radius of the circle
$item-size: 50px; // Size of each item
$container-size: 200px;

.container {
  position: relative;
  width: $container-size;
  height: $container-size;
  border-radius: 50%;
  margin: 50px auto; /* Center the container */
}

.item {
  position: absolute;
  width: $item-size;
  height: $item-size;
  border-radius: 50%;
  background-color: lightblue;
  text-align: center;
  line-height: $item-size;
  color: white;
}

@for $i from 1 through $item-count {
  .item:nth-child(#{$i}) {
    $angle: ($i - 1) * (360deg / $item-count);
    top: calc(#{$radius} + #{$radius} * sin(#{$angle}) - #{$item-size / 2});
    left: calc(#{$radius} + #{$radius} * cos(#{$angle}) - #{$item-size / 2});
  }
}

编译后的 CSS (大致):

.container {
  position: relative;
  width: 200px;
  height: 200px;
  border-radius: 50%;
  margin: 50px auto;
}

.item {
  position: absolute;
  width: 50px;
  height: 50px;
  border-radius: 50%;
  background-color: lightblue;
  text-align: center;
  line-height: 50px;
  color: white;
}

.item:nth-child(1) {
  top: calc(75px + 75px * sin(0deg) - 25px);
  left: calc(75px + 75px * cos(0deg) - 25px);
}

.item:nth-child(2) {
  top: calc(75px + 75px * sin(60deg) - 25px);
  left: calc(75px + 75px * cos(60deg) - 25px);
}

.item:nth-child(3) {
  top: calc(75px + 75px * sin(120deg) - 25px);
  left: calc(75px + 75px * cos(120deg) - 25px);
}

.item:nth-child(4) {
  top: calc(75px + 75px * sin(180deg) - 25px);
  left: calc(75px + 75px * cos(180deg) - 25px);
}

.item:nth-child(5) {
  top: calc(75px + 75px * sin(240deg) - 25px);
  left: calc(75px + 75px * cos(240deg) - 25px);
}

.item:nth-child(6) {
  top: calc(75px + 75px * sin(300deg) - 25px);
  left: calc(75px + 75px * cos(300deg) - 25px);
}

这个Sass代码更加灵活,因为我们可以通过修改 $item-count 变量来改变菜单项的数量,而无需手动修改每个菜单项的样式。

5. 结合 CSS 变量和 JavaScript

为了进一步提高灵活性,我们可以结合 CSS 变量和 JavaScript 来动态地控制布局。

示例:动态调整圆形菜单的半径

<!DOCTYPE html>
<html>
<head>
<style>
  .container {
    position: relative;
    width: 200px;
    height: 200px;
    border-radius: 50%;
    margin: 50px auto; /* Center the container */
    --radius: 75px; /* Default radius */
  }

  .item {
    position: absolute;
    width: 50px;
    height: 50px;
    border-radius: 50%;
    background-color: lightblue;
    text-align: center;
    line-height: 50px;
    color: white;
  }

  /* Using CSS variables and calc() for positioning */
  .item:nth-child(1) {
    top: calc(var(--radius) + var(--radius) * sin(0deg) - 25px);
    left: calc(var(--radius) + var(--radius) * cos(0deg) - 25px);
  }

  .item:nth-child(2) {
    top: calc(var(--radius) + var(--radius) * sin(60deg) - 25px);
    left: calc(var(--radius) + var(--radius) * cos(60deg) - 25px);
  }

  .item:nth-child(3) {
    top: calc(var(--radius) + var(--radius) * sin(120deg) - 25px);
    left: calc(var(--radius) + var(--radius) * cos(120deg) - 25px);
  }

  .item:nth-child(4) {
    top: calc(var(--radius) + var(--radius) * sin(180deg) - 25px);
    left: calc(var(--radius) + var(--radius) * cos(180deg) - 25px);
  }

  .item:nth-child(5) {
    top: calc(var(--radius) + var(--radius) * sin(240deg) - 25px);
    left: calc(var(--radius) + var(--radius) * cos(240deg) - 25px);
  }

  .item:nth-child(6) {
    top: calc(var(--radius) + var(--radius) * sin(300deg) - 25px);
    left: calc(var(--radius) + var(--radius) * cos(300deg) - 25px);
  }
</style>
</head>
<body>
  <div class="container">
    <div class="item">1</div>
    <div class="item">2</div>
    <div class="item">3</div>
    <div class="item">4</div>
    <div class="item">5</div>
    <div class="item">6</div>
  </div>

  <input type="range" id="radiusSlider" min="25" max="100" value="75">

  <script>
    const radiusSlider = document.getElementById('radiusSlider');
    const container = document.querySelector('.container');

    radiusSlider.addEventListener('input', () => {
      container.style.setProperty('--radius', `${radiusSlider.value}px`);
    });
  </script>
</body>
</html>

在这个例子中,我们添加了一个滑块,允许用户动态地调整圆形菜单的半径。 JavaScript 代码监听滑块的 input 事件,并将滑块的值设置为 CSS 变量 --radius 的值。 由于菜单项的位置是基于 --radius 计算的,因此它们会随着滑块的移动而动态地更新。

6. 解决兼容性问题

由于 CSS 三角函数的支持情况不一致,我们需要采取一些措施来解决兼容性问题。

  • 使用 CSS supports() 查询: 我们可以使用 CSS supports() 查询来检测浏览器是否支持 CSS 三角函数。如果浏览器不支持,我们可以提供一个备用方案。

    @supports (sin(1deg)) {
      /* 使用 CSS 三角函数的样式 */
    }
    
    @supports not (sin(1deg)) {
      /* 备用样式 */
    }
  • 使用 JavaScript polyfill: 可以使用 JavaScript polyfill 来模拟 CSS 三角函数的功能。 然而,这可能会影响性能,因此应该谨慎使用。

  • 优雅降级: 如果无法提供完全相同的功能,可以考虑提供一个简化的版本或完全不同的体验。

7. 一些注意事项

  • 性能: 复杂的 CSS 计算可能会影响性能,尤其是在动画中。 应该尽量减少不必要的计算,并使用性能分析工具来识别瓶颈。
  • 可读性: 使用 CSS 三角函数可能会使 CSS 代码变得难以阅读。 应该添加注释来解释代码的意图,并使用有意义的 CSS 变量名。
  • 维护性: 复杂的 CSS 代码可能难以维护。 应该将代码分解成更小的模块,并使用 CSS 预处理器来提高可维护性。
  • 角度单位: 务必注意角度单位。 sin(), cos(), 和 tan() 函数接受的角度单位是 deg, rad, grad, 和 turnasin(), acos(), 和 atan() 函数返回弧度值。

8. 三角函数赋能CSS,带来更强大的表现力

CSS三角函数的引入,极大地扩展了CSS的表现力。它使得我们能够创建更复杂的布局、更流畅的动画,以及更具交互性的用户界面。虽然目前兼容性还存在一些问题,但随着浏览器的不断更新,CSS三角函数必将在未来的Web开发中发挥越来越重要的作用。

9. 熟练掌握三角函数,设计更灵活的样式

通过学习和实践,我们可以掌握 CSS 三角函数的使用技巧,并将其应用到实际项目中,从而创建出更具创意和吸引力的Web应用。掌握三角函数可以让设计师和开发者不再局限于简单的矩形和圆形,可以创造出更复杂的几何形状和动画效果,让网页设计更具艺术性和独特性。

更多IT精英技术系列讲座,到智猿学院

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注