起因

最近有一个需求,需要在一个环形图上面根据角度画一条标线。

类似这样:

三角函数java咋求 js三角函数求角度_html

第一个想法就是用三角函数算出起点和终点的坐标,无奈数学都还给老师了,就按照记忆里的三角函数知识补了补课。

但是落到代码上的时候发现,javascript Math.sin()Math.cos() 的表现好像和预期不太一样?

弧度制

第一个问题是Math.sin(x:number):number 接收的参数 x 不是在 CSS 中常用的角度单位 Deg,是角度的弧度表示。

首先 度(deg)弧度(rad) 都是平面角的单位。

单位弧度定义为圆弧长度等于半径时的圆心角。

三角函数java咋求 js三角函数求角度_三角函数java咋求_02

根据公式圆的周长为 2πr。所以一个圆的弧度为 2π。

一个圆的角度是360°。所以 2πrad = 360°,πrad = 180°。1rad 也就等于 180°/π,约57.3°。

搞清楚了角度和弧度的转换,就可以使用前端比较熟悉的角度为单位来封装组件了。

三角函数的单位圆定义

知道了入参代表的含义,我们还需要知道Math.sin(x:number):number 返回值代表的含义。

Math.sin(x:number):number 方法返回一个 -1 到 1 之间的数值,想弄清楚这个数值代表的含义,需要知道一个知识点,三角函数的单位圆定义。

在直角三角形中,正弦、余弦以及其它三角函数只有当角度大于 0 且小于 π/2 (这里的 π/2 为弧度,即 90°) 时才有意义。

但是,在单位圆上,对于任意的实数角度,这些函数都有直观的意义。如图:

三角函数java咋求 js三角函数求角度_三角函数_03

正弦函数与余弦函数可以用如下方法定义。

三角函数java咋求 js三角函数求角度_html_04

设圆心为(0,0),这里的 x、y 就是当圆的半径为 1 时,点以圆心为坐标系的坐标。

落到 canvas 上

环形图是使用图表库画的,不需要我操心。

但是想在 canvas 中画一条线,就需要知道起点和终点相对于 canvas 的计算机坐标系的坐标。

我们需要对三角函数的单位圆坐标系进行转换和平移。平移的过程如下。

  1. 首先要改变 y 轴方向,计算机坐标系的 y 轴正值朝下,圆的坐标系的 y 轴正值朝上,所以要 y = -y。
  2. 之后就是将坐标系平移到左上,x 和 y 都要加上圆心在计算机坐标系上的位置,得出的就是计算机坐标系的坐标。

代码:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta
      name="viewport"
      content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"
    />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
  </head>
  <body>
    <canvas id="canvas" width="300" height="300"></canvas>
    <div style="width: 300px; text-align: center" id="text"></div>
    <script defer>
      function renderMarkLine(deg) {
        // 以 12 点钟方向为起点的顺时针角度,这样比较容易理解。
        deg = deg || 0;

        const canvasElement = document.getElementById("canvas");
        const context = canvasElement.getContext("2d");
        context.clearRect(0, 0, canvasElement.width, canvasElement.height);

        const center = [150, 150];
        const radius = 100;
        context.strokeStyle = "#000";
        context.lineWidth = 10;
        // 画环
        context.beginPath();
        context.arc(center[0], center[1], radius, 0, 2 * Math.PI);
        context.stroke();

        // 画标线
        const clockwiseDeg = (360 + (90 - deg)) % 360;
        const clockwiseRad = (clockwiseDeg / 180) * Math.PI;

        console.log(clockwiseDeg);

        // 圆坐标系中的坐标
        const markLineStart = [
          radius * 0.8 * Math.cos(clockwiseRad),
          radius * 0.8 * Math.sin(clockwiseRad),
        ];
        const markLineEnd = [
          radius * 1.2 * Math.cos(clockwiseRad),
          radius * 1.2 * Math.sin(clockwiseRad),
        ];

        // 计算机坐标系中的坐标
        const canvasMarkLineStart = [
          center[0] + markLineStart[0],
          center[1] - markLineStart[1],
        ];
        const canvasMarkLineEnd = [
          center[0] + markLineEnd[0],
          center[1] - markLineEnd[1],
        ];

        // 绘制标线
        context.beginPath();
        context.strokeStyle = "red";
        context.moveTo(...canvasMarkLineStart);
        context.lineTo(...canvasMarkLineEnd);
        context.stroke();
      }

      let deg = 0;
      const textElement = document.getElementById("text");

      function render() {
        renderMarkLine(deg);
        textElement.textContent = `deg: ${deg}`;
      }
      render();

      setInterval(() => {
        deg += 30;
        render();
      }, 1500);
    </script>
  </body>
</html>

参考资料:

  1. https://zh.wikipedia.org/wiki/单位圆
  2. https://zh.wikipedia.org/wiki/弧度