第三章 三角函数与圆周运动
说实话,很多前端同学一看到「三角函数」就头疼。我当年也一样。
但后来我发现,三角函数其实就是描述「转圈圈」的数学工具。你想想看,钟表指针在转,车轮在转,地球在转——这些运动背后,全是三角函数在撑腰。
这一章,我们就把它彻底搞明白。
3.1 正弦、余弦、正切:三个好兄弟
先看一个单位圆。半径为1,圆心在原点。
当一条半径从水平方向开始逆时针旋转,它和圆相交的点,坐标就是 (cosθ, sinθ)。
- sinθ = 对边 / 斜边 = 点的 y 坐标
- cosθ = 邻边 / 斜边 = 点的 x 坐标
- tanθ = 对边 / 邻边 = sinθ / cosθ
我刚开始做动画时,总记混 sin 和 cos。后来我给自己编了个口诀:「sin 是竖着的,cos 是横着的」。嗯,虽然土,但管用。
核心记忆点:
- sin 控制上下(y轴)
- cos 控制左右(x轴)
- tan 控制斜率(用得少,但做渐变效果时有用)
3.2 函数图像长什么样?
把角度从 0 到 2π 画出来,sin 和 cos 的图像就像波浪一样起伏。
- sin 图像:从 0 开始,先升到 1,再降到 -1,最后回到 0。像个平滑的波浪。
- cos 图像:从 1 开始,降到 -1,再升回 1。和 sin 差了一个 π/2 的相位。
- tan 图像:这个比较「暴躁」。在 π/2 和 3π/2 处有垂直渐近线,值会突然跳到无穷大。
我在项目中做过一个「呼吸灯」效果,用的就是 sin 函数。灯光的亮度在 0.3 到 1.0 之间平滑变化,看起来就像真的在呼吸。说白了,就是 brightness = 0.65 + 0.35 * sin(time) 这么简单。
避坑指南: 我曾经在 tan 函数上栽过跟头。做旋转木马效果时,直接用 tan 控制角度,结果在 π/2 附近画面直接「飞」了。后来我改用 sin 和 cos 组合,才稳下来。记住:tan 不适合做连续动画。
3.3 弧度制 vs 角度制
为什么前端里都用弧度?因为 Math 库只认弧度。
| 角度 | 弧度 | sin | cos |
|---|---|---|---|
| 0° | 0 | 0 | 1 |
| 30° | π/6 | 0.5 | 0.866 |
| 45° | π/4 | 0.707 | 0.707 |
| 60° | π/3 | 0.866 | 0.5 |
| 90° | π/2 | 1 | 0 |
| 180° | π | 0 | -1 |
| 360° | 2π | 0 | 1 |
转换公式很简单:
// 角度转弧度
const toRadians = (deg) => deg * Math.PI / 180;
// 弧度转角度
const toDegrees = (rad) => rad * 180 / Math.PI;
我个人习惯在代码里统一用弧度。只在调试时,才把弧度转成角度看看数值是否合理。
3.4 平滑往复运动:三角函数的拿手好戏
你想想看,一个元素从 A 点移动到 B 点,再回到 A 点,如此循环——这不就是 sin 或 cos 的波形吗?
核心公式就一行:
// 平滑往复运动
const value = center + amplitude * Math.sin(frequency * time + phase);
- center:运动中心位置
- amplitude:振幅(摆动幅度)
- frequency:频率(控制快慢)
- phase:相位(控制起始位置)
- time:时间变量(通常用 requestAnimationFrame 累加)
举个例子,让一个小球在屏幕上左右摆动:
let time = 0;
function animate() {
time += 0.02;
const x = 400 + 200 * Math.sin(time);
const y = 300;
ball.style.transform = `translate(${x}px, ${y}px)`;
requestAnimationFrame(animate);
}
animate();
这段代码里,小球会在 200px 到 600px 之间来回摆动。频率由 time += 0.02 控制,数值越大摆动越快。
实战经验: 我在做「加载动画」时,经常用这个公式。比如三个点依次弹跳,每个点加上不同的相位偏移:
// 三个点依次弹跳
const phases = [0, Math.PI * 2 / 3, Math.PI * 4 / 3];
dots.forEach((dot, i) => {
const y = 100 + 30 * Math.sin(time + phases[i]);
dot.style.transform = `translateY(${y}px)`;
});
这样三个点就会像波浪一样依次起伏,视觉效果非常流畅。
3.5 组合运动:让动画更丰富
单个 sin 只能做直线往复。但把 sin 和 cos 组合起来,就能画出圆、椭圆甚至更复杂的轨迹。
// 圆周运动
const radius = 150;
const x = centerX + radius * Math.cos(time);
const y = centerY + radius * Math.sin(time);
为什么是 cos 控制 x,sin 控制 y?因为 cos 从 1 开始,sin 从 0 开始。这样物体从右侧开始,逆时针画圆。
如果想画椭圆,只需要给 x 和 y 不同的半径:
const x = centerX + 200 * Math.cos(time);
const y = centerY + 100 * Math.sin(time);
这样就是一个横向拉长的椭圆轨迹。
注意: 做圆周运动时,time 的增量要控制好。增量太大,动画会「跳帧」;增量太小,运动太慢。我一般用 0.01 到 0.05 之间,具体看效果微调。
3.6 实际项目中的避坑指南
我曾经在一个 H5 活动中,用三角函数做「波浪文字」效果。每个字上下浮动,看起来像海浪一样起伏。
一开始我直接用 Math.sin(time + index * 0.5),结果文字抖动得很厉害。后来发现是时间增量不均匀导致的。
解决方案:用 performance.now() 获取精确时间,而不是用累加器。
let startTime = performance.now();
function animate() {
const elapsed = (performance.now() - startTime) / 1000;
letters.forEach((letter, i) => {
const y = 50 + 20 * Math.sin(elapsed * 2 + i * 0.8);
letter.style.transform = `translateY(${y}px)`;
});
requestAnimationFrame(animate);
}
这样不管帧率怎么变,动画始终是平滑的。
小技巧: 调试三角函数动画时,可以在控制台打印 Math.sin(time) 的值。如果数值在 -1 到 1 之间稳定变化,说明公式没问题。如果突然跳到 NaN,八成是 time 变成了 Infinity。
3.7 本章小结
三角函数其实没那么可怕。记住三点:
- sin 和 cos 是「波浪」,适合做平滑往复
- 弧度是前端唯一的选择,学会转换
- 组合 sin 和 cos 能画出各种轨迹
下一章,我们会用这些知识做更复杂的动画效果。到时候你会发现,三角函数就是前端动画的「万能钥匙」。