最近两天撸了两个 canvas,3D 旋转 & ribbon。

3D 旋转我是从这里学来的,因为原博主写的比较简单,所以重新实现了一下。

基础知识

基本原理

基本了解了圆周运动的坐标计算公式 & 3D模型之后,简单总结一下就是,如果想模拟一个物体的 3D 旋转,就必须扩展一下圆周运动的坐标计算公式。
盗用一下一只会飞的鱼的图:

由此可以得到公式

1
2
3
4
5
6
7
8
9
10
11
// 绕Z轴
newX = x * cos(angleZ) - y * sin(angleZ);
newY = y * cos(angleZ) + x * sin(angleZ);
// 绕X轴
newY = y * cos(angleX) - z * sin(angleX);
newZ = z * cos(angleX) - y * sin(angleX);
// 绕Y轴
newX = x * cos(angleY) - z * sin(angleY);
newZ = z * cos(angleY) + x * sin(angleY);

实际操作上,我们是没有办法改变鼠标在 Z 轴上的值,我们只能改变 X 和 Y 轴的坐标,因此主要的两个旋转公式就是围绕着 X 和 Y 使用的。

实现

参数准备

1
2
3
4
5
6
7
8
9
10
11
const W = canvas.width = window.innerWidth;
const H = canvas.height = window.innerHeight;
const balls = [];
const ballsNum = 250;
const fl = 250;
const vpX = W / 2;
const vpY = H / 2;
let mouseX = 0;
let mouseY = 0;
let angleX;
let angleY;
  • W 、H: canvas 的宽高;
  • balls: 用来存储小球的容器;
  • ballsNum: 小球数量的上限;
  • fl: 眼睛距离屏幕的距离;
  • vpX、vpY: 消失点的坐标,即 canvas 的中心点;
  • mouseX、mouseY: 鼠标的 X 、Y 轴坐标;
  • angleX、angleY: X 和 Y 轴的旋转角度;

生成小球

1
2
3
4
5
6
7
8
for (let i = 0; i < ballsNum; i++) {
const r = Math.random() * 5 + 1;
const ball = new Ball(r);
ball.xpos = Math.random() * 300 - 150;
ball.ypos = Math.random() * 300 - 150;
ball.zpos = Math.random() * 300 - 150;
balls.push(ball);
}

这里很简单,随机定义小球的半径 & x、y、z 轴的坐标,并且推入存储容器中。

旋转角度计算

1
2
angleX = (mouseY - vpY) * 0.0001;
angleY = (mouseX - vpX) * 0.0001;

这里我们用鼠标位置与消失点的偏移量来计算 X 、 Y 轴的旋转角度。

运动量计算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
function move(ball) {
rotateX(ball, angleX);
rotateY(ball, angleY);
setPerspective(ball);
}
function rotateX(ball, angle) {
const cos = Math.cos(angle);
const sin = Math.sin(angle);
const y1 = ball.ypos * cos - ball.zpos * sin;
const z1 = ball.zpos * cos + ball.ypos * sin;
ball.ypos = y1;
ball.zpos = z1;
}
function rotateY(ball, angle) {
const cos = Math.cos(angle);
const sin = Math.sin(angle);
const x1 = ball.xpos * cos - ball.zpos * sin;
const z1 = ball.zpos * cos + ball.xpos * sin;
ball.xpos = x1;
ball.zpos = z1;
}
function setPerspective(ball) {
if (ball.zpos > -fl) {
ball.scaleX = ball.scaleY = fl / (fl + ball.zpos);
ball.x = vpX + ball.xpos * ball.scaleX;
ball.y = vpY + ball.ypos * ball.scaleY;
ball.visible = true;
} else {
ball.visible = false;
}
}

这里就是对之前原理的一个运用,计算 X 、 Y 的轴旋转后的各个小球的坐标。
最后再计算小球在 Z 轴的坐标对缩放的影响。

总结

其实只要掌握了坐标计算公式 & 3D 环境搭建的原理,3D 旋转其实还是蛮容易的,但是做出来的效果却非常酷炫,到 codeopen 上可以看到各种各样的旋转效果。
所以之前提及的几篇文章可以多看看,那些是非常基础的概念,理解好之后再加上点创意,就可以创造很多了 🤓