three.js 自带的几何图型粒子动画变形记

前言

粒子动画,真的很美,所以第一个就是学习简单的粒子动画,这篇文章使用基本的几何图形来实现不同几何图形之间的粒子过渡效果。

粒子之旅

在这里也没有什么可说的,上码:

// index.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>粒子变形记</title>
  <style>
    * {
      padding: 0;
      margin: 0
    }
    body,
    html {
      width: 100%;
      height: 100%;
    }
  </style>
</head>
<body>
  <script src="./js/three.min.js"></script>
  <script src="./js/Tween.js"></script>
  <script src="./js/orbitControls.js"></script>
  <script src="./js/index.js"></script>
</body>
</html>

下面为 js 代码

// index.js
let
  canera,
  scene,
  renderer,
  material,
  len = 0,
  geo = [],
  tw = [],
  points,
  toTeoArr = [],
  startPositionArray = [];
// 环境准备
function prepare() {
  // 获取浏览器窗口的宽高,后续会用
  const width = window.innerWidth
  const height = window.innerHeight
  // 创建一个 WebGL 渲染器,Three.js
  renderer = new THREE.WebGLRenderer({
    antialias: true
  })
  scene = new THREE.Scene()
  // 创建一个具有透视效果的摄像机
  camera = new THREE.PerspectiveCamera(45, width / height, 1, 1000)
  // 设置摄像机位置,并将其朝向场景中心
  camera.position.x = 100
  camera.position.y = 100
  camera.position.z = 100
  camera.lookAt(scene.position)
  renderer.setPixelRatio(window.devicePixelRatio)
  // 设置渲染器的清除颜色(即绘制下一帧前填充的颜色)
  renderer.setClearColor(0x000000)
  // canvas 的尺寸
  renderer.setSize(width, height)
  // 将渲染器的输出(此处是 canvas 元素)插入到 body
  document.body.appendChild(renderer.domElement)
  // 初始化摄像机插件(用于拖拽旋转摄像机,产生交互效果)
  let orbitControls = new THREE.OrbitControls(camera)
  orbitControls.autoRotate = true
  material = new THREE.PointsMaterial({ size: 0.1, color: 0x00ffff })
}
// 生成几何
function generalGeometrys() {
  // 生成一些点,作为 LatheBufferGeometry() 参数
  let vs1 = [];
  for (let i = 0; i < 120; i++) {
    vs1.push(new THREE.Vector2(Math.cos(i * 0.2) * 50, Math.sin(i * 0.2) * 25))
  }
  let vs2 = [];
  for (let i = 0; i < 120; i++) {
    vs2.push(new THREE.Vector2(Math.cos(i * 0.2) * 50, Math.tan(i * 0.1) * 10))
  }
  len = geo.push(
    new THREE.BoxBufferGeometry(20, 20, 20, 15, 15, 15),
    new THREE.LatheBufferGeometry(vs1),
    new THREE.LatheBufferGeometry(vs2),
    new THREE.SphereBufferGeometry(25, 38, 39),
    new THREE.BoxBufferGeometry(20, 20, 20, 15, 15, 15)
  );
}
function firstGeometry() {
  // 获取第一个几何的点位置数据数组
  startPositionArray = geo[0].attributes.position.array
  // 根据几何画点
  points = new THREE.Points(geo[0], material)
  // 添加到场景中
  scene.add(points)
}
// 使用动画,并让动画循环
function bindTween() {
  let twLen = 0;
  // 保存所有形状的点位置数据(不包括第一个形状)
  for (let i = 0; i < len; i++) {
    if (i >= len - 1) {
      break;
    }
    toTeoArr.push(geo[i + 1].attributes.position.array)
  }
  // 生成动画队列
  for (let i = 0; i < toTeoArr.length; i++) {
    tw.push(new TWEEN.Tween(startPositionArray).to(toTeoArr[i], 3000).easing(TWEEN.Easing.Exponential.InOut))
  };
  // 动画循环调用
  twLen = tw.length;
  if (twLen > 1) {
    for (let i = 0; i < twLen; i++) {
      if (i === twLen - 1) { // 如果最后一个 tween 动画
        tw[i].chain(tw[0])
      } else {
        tw[i].chain(tw[i + 1])
      }
    }
  }
  // 启动动画
  tw[0].start()
}
// 更新相关参数
function update() {
  TWEEN.update();
  // 渲染,即摄像机拍下此刻的场景
  renderer.render(scene, camera)
  if (points) {
    points.geometry.attributes.position.needsUpdate = true
    points.rotation.y += 0.002
  }
}
// 渲染
function render() {
  update()
  requestAnimationFrame(render)
}
function init() {
  prepare()
  generalGeometrys()
  firstGeometry()
  bindTween()
  render()
}
init()

至于效果,你可以运行下面的代码看看。

上面的 js 代码我觉得组织得已经很清晰了,并带上了相关备注,每一块代码都实现一个过程。这样是为了让你看起来不会觉得那么复杂。如果觉得还是看不太懂,那么可以先从宏观上把握,比如先从 init 中调用的方法从上往下一个一个开始阅读。