Three.js 学习记录 1

115 阅读5分钟

学习资料:B站 老陈打码

three.js官网

创建第一个3D 立方体

创建立方体之前需要准备场景、摄像机、渲染器

相机

官方示例 参数:

  • fov:摄像机视锥体垂直视野角度;
  • aspect:摄像机视锥体长宽比;
  • near:摄像体视锥体近端面;
  • far:摄像机视锥体远端面 image.png
// 导入 Three.js
import * as THREE from "three"


// 1、 创建场景
const scenc = new THREE.Scenc()
// 2、创建相机 
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)
// 3、设置相机位置 x、y、z
camera.position.set(0, 0, 10)
// 在场景中添加相机
scenc.add(camera)

// 4、添加几何体
// 创建几何体 x,y,z 坐标
const cubeGeometry = new THREE.BoxGeometry(1,1,1)
// 几何体材质
const cubeMaterial = new THREE.MeshBasicMaterial({colo:0xfff00})
// 根据坐标、材质创建几何体
const cube = new THREE.Mesh(cubeGeometry, cubeMaterial)
// 5、将几何体添加到场景
scenc.add(cube)

// 6、创建渲染器
const renderer = new THREE.WebGlRenderer()
// 设置渲染器大小
renderer.setSize(window.innerWidth, window.innerHeight)
// 将渲染器生成的canvas 添加到body
document.body.appendChild(renderer.domElement)

// 通过渲染器,将场景和相机渲染出来
renderer.render(scenc, camera)

image.png 第一个立方体就创建好了

使用轨道控制器 + 坐标线

但刚刚创建的立体图形目前的效果看起来是平面的,现在只需要添加一个控制器,使相机围绕目标进行运动才能看到立体效果。

控制器也有很多种,在官网可以查看不同的控制器的不同应用场景,目前根据需求选择,这里使用轨道控制器(OrbitControls

// 使用轨道控制器查看3D物体

// 导入轨道控制器
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'

// 创建轨道控制器 相机,事件监听的HTML元素
const controls = new OrbitControls(camera, renderer.domElement)
// 为动画添加阻尼效果,更加逼真,必须在动画循环里调用controls.update()。
controls.enableDamping = true

// 添加坐标辅助线 x轴:红色;y轴:绿色;z轴:蓝色
const axesHelper = new THREE.AxesHelper( 5 ); 
// 添加到场景 
sence.add( axesHelper );

// 渲染函数
function render () {
  // 通过改变 cube.position.x 实现平行移动效果
  cube.position.x > 5 ? cube.position.x = 0 : cube.position.x += 0.01

  controls.update()
  renderer.render(scenc, camera)
  // requestAnimationFrame 浏览器自带函数 渲染下一帧的时候调用render 函数 
  requestAnimationFrame(render)
}

image.png

使用Clock构造函数,实现物体匀速移动

// 创建时钟
const clock = new THREE.Clock()

function render () {
  controls.update()
  // let deltaTime = clock.getDelta()
  // console.log("两次获取时间的间隔时间:", deltaTime);
  let time = clock.getElapsedTime()
  let t = time % 5
  cube.position.x = t * 1
  // console.log("运行时间总时长:", time);
  renderer.render(scenc, camera)
  // 渲染下一帧的时候调用render 函数
  requestAnimationFrame(render)
}

监听window resize事件,动态更新渲染器、摄像头

// 根据尺寸变化实现自适应画面
window.addEventListener("resize", () => {
  // 更新渲染器尺寸大小
  renderer.setSize(window.innerWidth, window.innerHeight)
  // 设置渲染器像素比 window.devicePixelRatio 当前像素比
  renderer.setPixelRatio(window.devicePixelRatio)
  // 更新摄像头
  camera.aspect = window.innerWidth / window.innerHeight
  // 更新摄像头投影矩阵
  camera.updateProjectionMatrix()
})

使用GSAP动画库

GASP官网

安装:npm install gsap
引入:import { gsap } from "gsap";

// 导入gsap 动画库
import gsap from "gsap"

// 创建 立方体 X轴平移动画
let animate1 = gsap.to(cube.position, {
  // x坐标
  x: 5,
  // 多少秒内完成 
  duration: 5,
  // 动画效果
  ease: "power1.inOut",
  // 执行的次数,无限次:-1
  repeat: -1,
  // 往返运动
  yoyo: true,
  // 延迟执行时间
  delay: 1,
  // 动画开始回调
  onStart: () => { console.log('动画开始了!') },
  // 执行完成的回调函数
  onComplete: () => { console.log("执行完成") },
})
// 创建 立方体 旋转效果
let animate2 = gsap.to(cube.rotation, {
  x: 2 * Math.PI,
  duration: 5,
  ease: "power1.inOut",
  repeat: -1,
  yoyo: true,
  delay: 1
})

添加单机暂停/恢复动画事件,双击进入,退出全屏事件

window.addEventListener("dblclick", () => {
  // document.fullscreenElement 获取到当前进入全屏的元素
  const fullscreenElement = document.fullscreenElement
  if (!fullscreenElement) {
    // renderer 进入全屏
    renderer.domElement.requestFullscreen()
  } else {
    // 退出全屏 使用document对象
    document.exitFullscreen()
  }
})

window.addEventListener("click", () => {
  // animate1.isActive() 获取到 animate1 的状态 true 为在运行中
  if (animate1.isActive()) {
    // 暂停动画
    animate1.pause()
    animate2.pause()
  } else {
    // 恢复动画
    animate1.resume()
    animate2.resume()
  }
})

使用dat.gui 插件

使用文档

安装:npm install --save dat.gui
引入: import * as dat from 'dat.gui'

// 导入
import * as dat from 'dat.gui'

// 创建 gui实例
const gui = new dat.GUI()

const params = {
  color: "#ffff00",
  rotationFn: () => {
    if (animate2.isActive()) {
      animate2.pause()
    } else {
      animate2.resume()
    }
  },
  positionFn: () => {
    if (animate1.isActive()) {
      animate1.pause()
    } else {
      animate1.resume()
    }
  }
}

gui.add(cube.position, "x").min(0).max(5).step(0.01).name("修改X值")
gui.add(params, "color").name("修改立方体颜色").onChange(val => {
  cube.material.color.set(val)
})
gui.add(cube, "visible").name("显示立方体")

// 添加操作文件夹
let folder = gui.addFolder("设置立方体")
folder.add(params, "rotationFn").name("旋转立方体")
folder.add(params, "positionFn").name("立方体移动")
// 设置线框
folder.add(cube.material, "wireframe")

image.png

完整代码

import * as THREE from "three"
// 导入轨道控制器
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
// 导入gsap 动画库
import gsap from "gsap"
import * as dat from 'dat.gui'

// 目标:使用dat.gui 库


// 1、创建场景
const scenc = new THREE.Scene()

// 2、创建相机
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)

// 3、设置相机位置
camera.position.set(0, 0, 10)
scenc.add(camera)

// 4、添加物体
// 创建几何体
const cubeGeometry = new THREE.BoxGeometry(1, 1, 1)
// 几何体材质
const cubeMaterial = new THREE.MeshBasicMaterial({ color: 0xfff00 })
// 根据几何体,材质 创建物体
const cube = new THREE.Mesh(cubeGeometry, cubeMaterial)
// 将几何体添加到场景
scenc.add(cube)

let animate1 = gsap.to(cube.position, { x: 5, duration: 5, repeat: -1, ease: "power1.inOut", yoyo: true })
let animate2 = gsap.to(cube.rotation, {
  x: Math.PI * 2,
  duration: 5,
  repeat: -1,
  ease: "power1.inOut",
  yoyo: true
})

const params = {
  color: "#ffff00",
  rotationFn: () => {
    if (animate2.isActive()) {
      animate2.pause()
    } else {
      animate2.resume()
    }
  },
  positionFn: () => {
    if (animate1.isActive()) {
      animate1.pause()
    } else {
      animate1.resume()
    }
  }
}

const gui = new dat.GUI()
gui.add(cube.position, "x").min(0).max(5).step(0.01).name("修改X值")
gui.add(params, "color").name("修改立方体颜色").onChange(val => {
  cube.material.color.set(val)
})
gui.add(cube, "visible").name("显示立方体")

// 添加操作文件夹
let folder = gui.addFolder("设置立方体")
folder.add(params, "rotationFn").name("旋转立方体")
folder.add(params, "positionFn").name("立方体移动")
// 设置线框
folder.add(cube.material, "wireframe")


// 修改物体位置
// cube.position.set(5, 0, 0)
// cube.position.x = 3;
// 控制物体缩放
// cube.scale.set(1, 2, 3)
// cube.scale.x = 3
// 控制物体旋转
// cube.rotation.set(Math.PI / 4, 0, 0)
// cube.rotation.x = Math.PI / 6


// 创建渲染器
const renderer = new THREE.WebGLRenderer()
// 设置渲染器尺寸大小
renderer.setSize(window.innerWidth, window.innerHeight)
// 将渲染器生成的canvas 添加到body
document.body.appendChild(renderer.domElement)

// 通过渲染器,将场景和相机渲染出来
renderer.render(scenc, camera)

// 创建轨道控制器 相机,事件监听的HTML元素
const controls = new OrbitControls(camera, renderer.domElement)
// 为动画添加阻尼效果,更加逼真,必须在动画循环里调用.update()。
controls.enableDamping = true

// 添加坐标辅助线 x轴:红色;y轴:绿色;z轴:蓝色
const axesHelper = new THREE.AxesHelper(5)
scenc.add(axesHelper)

window.addEventListener("dblclick", () => {
  // document.fullscreenElement 获取到当前进入全屏的元素
  const fullscreenElement = document.fullscreenElement
  if (!fullscreenElement) {
    // renderer 进入全屏
    renderer.domElement.requestFullscreen()
  } else {
    // 退出全屏 使用document对象
    document.exitFullscreen()
  }
})

function render () {
  controls.update()
  renderer.render(scenc, camera)
  // 渲染下一帧的时候调用render 函数
  requestAnimationFrame(render)
}

render()

// 根据尺寸变化实现自适应画面
window.addEventListener("resize", () => {
  // 更新渲染器尺寸大小
  renderer.setSize(window.innerWidth, window.innerHeight)
  // 设置渲染器像素比 window.devicePixelRatio 当前像素比
  renderer.setPixelRatio(window.devicePixelRatio)
  // 更新摄像头
  camera.aspect = window.innerWidth / window.innerHeight
  // 更新摄像头投影矩阵
  camera.updateProjectionMatrix()
})