二维的坐标旋转绕Z轴旋转,物体只有x、y坐标会变化(风车旋转),三维中还可以绕x和y轴旋转。
绕x轴旋转:只有y、z坐标会变化(轮胎滚动)。
绕y轴旋转,只有x,z坐标会变化(唱片机)。

坐标旋转

旋转公式

二维:

x1 = x * Math.cos(angle) - y * Math.sin(angle)
y1 = y * Math.cos(angle) + y * Math.sin(angle)

三维绕x轴:

y1 = y * Math.cos(angleX) - z * Math.sin(angleX)
z1 = z * Math.cos(angleX) + y * Math.sin(angleX)

三维绕y轴:

x1 = x * Math.cos(angleY) - z * Math.sin(angleY)
z1 = z * Math.cos(angleY) + x * Math.sin(angleY)

模拟三维坐标旋转

Demo

import { parseColor, captureMouse } from '../common/utils.js'

let canvas = document.getElementById('canvas')
let width = canvas.width = 500
let height = canvas.height = 500
let ctx = canvas.getContext('2d')
let balls = createBall(50)
let fl = 250
let vpX = width / 2
let vpY = height / 2
let mouse = captureMouse(canvas)
let angleY
let angleX

function createBall (nums) {
  let balls = []
  while (nums--) {
    balls.push(new Ball3d({
      radius: Math.random() * 10 + 10,
      lineWidth: 0,
      color: parseColor(Math.random() * 0xffffff),
      xpos: Math.random() * width - width / 2,
      ypos: Math.random() * height - height / 2,
      zpos: Math.random() * 400 - 200
    }))
  }
  return balls
}

// 绕X轴旋转小球
function rotateX (ball, angle) {                      
  let cos = Math.cos(angle)
  let sin = Math.sin(angle)
  let y1 = ball.ypos * cos - ball.zpos * sin
  let z1 = ball.zpos * cos + ball.ypos * sin
  ball.ypos = y1
  ball.zpos = z1
}


// 绕Y轴旋转小球
function rotateY (ball, angle) {
  let sin = Math.sin(angle)
  let cos = Math.cos(angle)

  let x1 = ball.xpos * cos - ball.zpos * sin
  let z1 = ball.zpos * cos + ball.xpos * sin

  ball.xpos = x1
  ball.zpos = z1
}

// 透视
function setPerspective (ball) {
  if (ball.zpos > -fl) {
    let scale = fl / (fl + ball.zpos)
    ball.scaleX = ball.scaleY = scale
    ball.x = vpX + ball.xpos * scale
    ball.y = vpY + ball.ypos * scale
    ball.visible = true
  } else {
    ball.visible = false
  }
}

function move (ball) {
  rotateX(ball, angleX)
  rotateY(ball, angleY)
  setPerspective(ball)
}

// 排序,让zpos值大的球在前面
function zsort (a, b) {
  return b.zpos - a.zpos
}
function draw (ball) {
  if (ball.visible) {
    ball.draw(ctx)
  }
}

;(function drawFrame () {
  window.requestAnimationFrame(drawFrame)
  ctx.clearRect(0, 0, width, height)
  // 根据鼠标坐标计算出角度
  angleY = (mouse.x - vpX) * 0.0005
  angleX = (mouse.y - vpY) * 0.0005

  balls.sort(zsort)
  balls.forEach(move)
  balls.forEach(draw)

})();