工作之余,就来学学three.js。为了快速开始体验three.js,我选择用vue的脚手架+vue3项目快速搭建了开发环境。项目创建好后,就正式开始three.js的学习之旅吧~
1. 安装并引入three.js
安装:npm install three
引入:
// 方式 1: 导入整个 three.js核心库
import * as THREE from 'three';
const scene = new THREE.Scene();
// 方式 2: 按需导入
import { Scene } from 'three';
const scene = new Scene();
2. 阅读并按照官方文档进行操练
2.1 了解three.js中的四大要素
three.js中的四大要素:场景、相机、渲染器和渲染主体(物体)。
首先对四大元素进行初始化及参数的配置。
import {Scene,PerspectiveCamera,WebGLRenderer, BoxGeometry, MeshBasicMaterial,Mesh} from 'three'
// 场景
let scene = new Scene()
// 相机 这里用的是透视摄像机
// PerspectiveCamera( fov : Number, aspect : Number, near : Number, far : Number )
/**
* 视锥体参数
* fov: 视野角度
* aspect:长宽比
* near:近剪切面
* far:远剪切面
*/
let camera = new PerspectiveCamera(35,window.innerWidth/window.innerHeight,0.1,1000)
// 设置相机位置 两种设置方式
camera.position.z = 18
// camera.position.set(0,20,100)
// 渲染器初始化及渲染参数的配置
let renderer = new WebGLRenderer({
antialias:true, // 抗锯齿 默认值为false,看起有点粗糙,所以我这里设置为true
// alpha:true // canvas是否包含alpha (透明度)。默认为 false
})
// 设置渲染的尺寸大小
renderer.setSize(window.innerWidth,window.innerHeight)
// 添加物体
// 创建几何体
let geometry = new BoxGeometry(1,1,1)
let material = new MeshBasicMaterial({color:0xe3c144})
// 根据几何体和材质创建物体
let cube = new Mesh(geometry, material)
// 改变物体的位置 两种设置方式
cube.position.set(1,1,1)
// cube.position.x = 1
// 将物体添加到场景中
scene.add(cube)
renderer.render(scene,camera)
// 将webgl渲染的canvas内容添加到指定容器内
outputBox.value.appendChild(renderer.domElement)
// 因为我是在vue3项目中使用,我想放到指定的容器中去显示,所以用到了ref,你完全可以使用下面这种写法代替。
//document.body.appendChild(renderer.domElement)
此时在页面上就会出现我们绘制的物体了。效果如下:
2.2 加入three.js内置的辅助工具
① 坐标轴辅助器
import {Scene,PerspectiveCamera,WebGLRenderer, BoxGeometry, MeshBasicMaterial,Mesh,AxesHelper} from 'three'
// 坐标轴辅助器
const axesHelper = new AxesHelper(5) // 参数是指坐标轴的长度
//将物体和坐标轴复制器添加到场景中
scene.add(axesHelper)
② 控制器
使用控制器查看3D物体,按住鼠标左键在页面上拖动,就能看到效果,不信你试试?
注:控制器要在循环渲染的动画里才有效果。
// 导入轨道控制器
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
// 使用控制器查看3D物体
const controls = new OrbitControls(camera,renderer.domElement)
// 设置控制器阻尼 必须配合在渲染动画的循环里调用.update()
controls.enableDamping = true
function render() {
controls.update()
renderer.render(scene,camera)
requestAnimationFrame(render)
}
onMounted(()=> {
// 将webgl渲染的canvas内容添加到指定容器内
outputBox.value.appendChild(renderer.domElement)
render()
})
2.3 加入第三方工具
① gsap动画库
安装 gsap - npm
npm install gsap
引入
import gsap from "gsap";
使用
// 导入动画库
import gsap from 'gsap'
// 设置动画
// 动画1
let ani = gsap.to(
cube.position, // 位置变化 移动
{
x:5,
duration:6, // 每次动画的执行时间
delay:2, // 延迟动画的开始时间
ease:"back.inOut(1.7)", // 动画速度
repeat:-1, // 动画重复次数 , -1 表示无限次
yoyo:true, // 往返运动 要配合着repeat 一起使用才会生效
onStart: () => { // 动画开始的回调
console.log('动画开始')
},
onComplete:() => { // 动画结束的回调
console.log('动画结束')
}
}
)
// 动画2
gsap.to(
cube.rotation, // 旋转
{
x:2 * Math.PI, // 旋转角度
duration:5,
repeat:-1
}
)
// 监听单击事件结束或恢复动画
document.addEventListener('click',() => {
// 判断当前动画是否是激活状态,是则暂停,不是则恢复
ani.isActive() ? ani.pause() : ani.resume()
})
② dat.gui UI工具库
安装 dat.gui
npm install --save dat.gui
引入
import * as dat from 'dat.gui';
使用 dat.gui/API
// 导入 dat.gui库 UI工具库
import * as dat from 'dat.gui'
// 初始化UI调节工具
const gui = new dat.GUI()
gui.add(cube.position,'x').min(0).max(5).step(0.01).name('移动X轴').onFinishChange(value => {
console.log('调整后的值为:',value)
})
const params = {color:0x00ff00,fn: () => {
ani.isActive() ? ani.pause() : ani.resume()
}}
gui.addColor(params,"color").onChange((value) => {
cube.material.color.set(value)
})
gui.add(params,'fn').name('物体运动')
let folder = gui.addFolder('物体属性设置')
folder.add(cube.material,"wireframe")
2.4 优化
① 进入全屏和退出全屏
document.addEventListener('dblclick',() => {
const fullScreenElement = document.fullscreenElement
!fullScreenElement ? renderer.domElement.requestFullscreen() : document.exitFullscreen()
})
② 页面尺寸变化时,同步更新渲染画面
// 监听页面变化,更新渲染画面
window.addEventListener('resize',() => {
// 更新摄像头
camera.aspect = window.innerWidth / window.innerHeight
// 更新摄像机的投影矩阵
camera.updateProjectionMatrix()
// 更新渲染器
renderer.setSize(window.innerWidth,window.innerHeight)
// 设置渲染器的像素比
renderer.setPixelRatio(window.devicePixelRatio)
})
2.5 完整代码
BaseDemo.vue
<template>
<div ref="outputBox"></div>
</template>
<script>
import { onMounted,ref } from 'vue'
import {Scene,PerspectiveCamera,WebGLRenderer, BoxGeometry, MeshBasicMaterial,Mesh,AxesHelper} from 'three'
// 导入轨道控制器
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
// 导入动画库
import gsap from 'gsap'
// 导入 dat.gui库 UI工具库
import * as dat from 'dat.gui'
export default {
name: 'BaseDemo',
setup(){
// 元素引用
const outputBox = ref(null)
// 场景
let scene = new Scene()
// 摄像机 这里用的是透视摄像机
// PerspectiveCamera( fov : Number, aspect : Number, near : Number, far : Number )
/**
* 视锥体参数
* fov: 视野角度
* aspect:长宽比
* near:近剪切面
* far:远剪切面
*/
let camera = new PerspectiveCamera(35,window.innerWidth/window.innerHeight,0.1,1000)
// 设置相机位置 两种设置方式
camera.position.z = 18
// camera.position.set(0,20,100)
// 初始化渲染器及渲染参数的配置
let renderer = new WebGLRenderer({
antialias:true, // 抗锯齿
// alpha:true // canvas是否包含alpha (透明度)。默认为 false
})
// 设置渲染的尺寸大小
renderer.setSize(window.innerWidth,window.innerHeight)
// 添加物体
// 创建几何体
let geometry = new BoxGeometry(1,1,1)
let material = new MeshBasicMaterial({color:0xe3c144})
// 根据几何体和材质创建物体
let cube = new Mesh(geometry, material)
// 改变物体的位置
// cube.position.set(1,1,1)
// cube.position.x = 1
// 坐标轴辅助器
const axesHelper = new AxesHelper(5)
//将物体和坐标轴复制器添加到场景中
scene.add(cube,axesHelper)
// 使用控制器查看3D物体
const controls = new OrbitControls(camera,renderer.domElement)
// 设置控制器阻尼 必须配合在渲染动画的循环里调用.update()
controls.enableDamping = true
// 设置动画
// 动画1
let ani = gsap.to(
cube.position, // 位置变化 移动
{
x:5,
duration:6, // 每次动画的执行时间
delay:2, // 延迟动画的开始时间
ease:"back.inOut(1.7)", // 动画速度
repeat:-1, // 动画重复次数 , -1 表示无限次
yoyo:true, // 往返运动 要配合着repeat 一起使用才会生效
onStart: () => { // 动画开始的回调
console.log('动画开始')
},
onComplete:() => { // 动画结束的回调
console.log('动画结束')
}
}
)
// 动画2
gsap.to(
cube.rotation, // 旋转
{
x:2 * Math.PI, // 旋转角度
duration:5,
repeat:-1
}
)
// 初始化UI调节工具
const gui = new dat.GUI()
gui.add(cube.position,'x').min(0).max(5).step(0.01).name('移动X轴').onFinishChange(value => {
console.log('调整后的值为:',value)
})
const params = {color:0x00ff00,fn: () => {
ani.isActive() ? ani.pause() : ani.resume()
}}
gui.addColor(params,"color").onChange((value) => {
cube.material.color.set(value)
})
gui.add(params,'fn').name('物体运动')
let folder = gui.addFolder('物体属性设置')
folder.add(cube.material,"wireframe")
// 监听双击事件 进入全屏和退出全屏
document.addEventListener('dblclick',() => {
const fullScreenElement = document.fullscreenElement
!fullScreenElement ? renderer.domElement.requestFullscreen() : document.exitFullscreen()
})
// 监听页面变化,更新渲染画面
window.addEventListener('resize',() => {
// 更新摄像头
camera.aspect = window.innerWidth / window.innerHeight
// 更新摄像机的投影矩阵
camera.updateProjectionMatrix()
// 更新渲染器
renderer.setSize(window.innerWidth,window.innerHeight)
// 设置渲染器的像素比
renderer.setPixelRatio(window.devicePixelRatio)
})
// 渲染函数
function render() {
controls.update()
renderer.render(scene,camera)
requestAnimationFrame(render)
}
onMounted(()=> {
// 将webgl渲染的canvas内容添加到指定容器内
outputBox.value.appendChild(renderer.domElement)
// document.body.appendChild(renderer.domElement)
render()
})
return {
outputBox
}
}
}
</script>
好啦,本篇就记录这么多。music~