目录
一、three.js开发环境搭建
1.1、使用parcel搭建开发环境
第一步:创建空项目
第二步:终端输入
bash
npm init
直接一路回车。
第三步:安装parcel
bash
npm install parcel-bundler
接着,通过修改你的package.json来添加这些任务脚本
javascript
"scripts": {
"dev": "parcel src/index.html",
"build": "parcel build src/index.html"
},
第四步:创建src/index.html
第五步:终端输入命令
bash
npm install parcel-bundler -dev
第六步:创建静态文件,引入静态文件
第七步:编写style.css代码
css
* {
margin: 0;
padding: 0;
}
body {
background-color: skyblue;
}
第八步:安装threejs依赖
bash
npm install three --save
第九步:编写main.js代码,看threejs是否安装成功
javascript
import * as THREE from "three"
console.log(THREE);
第十步:启动项目
1.2、使用three.js渲染第一个场景和物体
javascript
import * as THREE from "three"
// 1、创建场景
const scene = new THREE.Scene()
// 2、创建相机
const camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000);
// 3、设置相机位置
camera.position.set(0, 0, 10);
scene.add(camera)
// 添加物体
// 创建几何体
const cubeGeometry = new THREE.BoxGeometry(1, 1, 1);
// 材质
const cubeMaterial = new THREE.MeshBasicMaterial({color: 0xffff00});
// 根据几何体和材质创建物体
const cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
// 将物体添加到场景中
scene.add(cube)
// 初始化渲染器
const renderer = new THREE.WebGLRenderer();
// 设置渲染的尺寸大小
renderer.setSize(window.innerWidth, window.innerHeight);
// 将webgl渲染的canvas内容添加到body
document.body.appendChild(renderer.domElement);
// 使用渲染器,通过相机将场景渲染进来
renderer.render(scene, camera);
1.3、轨道控制器查看物体
javascript
import * as THREE from "three"
// 导入轨道控制器
import {OrbitControls} from 'three/examples/jsm/controls/OrbitControls'
// 1、创建场景
const scene = new THREE.Scene()
// 2、创建相机
const camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000);
// 3、设置相机位置
camera.position.set(0, 0, 10);
scene.add(camera)
// 添加物体
// 创建几何体
const cubeGeometry = new THREE.BoxGeometry(1, 1, 1);
// 材质
const cubeMaterial = new THREE.MeshBasicMaterial({color: 0xffff00});
// 根据几何体和材质创建物体
const cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
// 将物体添加到场景中
scene.add(cube)
// 初始化渲染器
const renderer = new THREE.WebGLRenderer();
// 设置渲染的尺寸大小
renderer.setSize(window.innerWidth, window.innerHeight);
// 将webgl渲染的canvas内容添加到body
document.body.appendChild(renderer.domElement);
// 使用渲染器,通过相机将场景渲染进来
// renderer.render(scene, camera);
// 创建轨道控制器
const controls = new OrbitControls(camera, renderer.domElement);
function render() {
renderer.render(scene, camera);
// 渲染下一帧的时候就会调用render函数
requestAnimationFrame(render)
}
render();
可以拖动了。
二、three.js辅助设置
2.1、添加坐标轴辅助器
javascript
// 添加坐标轴辅助器 5代表轴的线段长度
const axesHelper = new THREE.AxesHelper(5)
scene.add(axesHelper)
红色代表X轴,绿色代表Y轴,蓝色代表Z轴。
2.2、设置物体移动
javascript
// 修改物体的位置
// 参数分别为 x,y,z
cube.position.set(5, 2, 0)
当然你也可以使用直接点的形式:
javascript
cube.position.x = 5
cube.position.y = 2
小案例:模拟物体从左到右循环运动
javascript
function render() {
cube.position.x += 0.01
if (cube.position.x > 5) {
cube.position.x = 0
}
renderer.render(scene, camera);
// 渲染下一帧的时候就会调用render函数
requestAnimationFrame(render)
}
2.3、物体的缩放与旋转
缩放
javascript
// 缩放
// 参数 x轴是3倍,y轴是两倍
cube.scale.set(3, 2, 1)
旋转
javascript
//旋转
// 参数都为弧度,Math.PI / 4代表绕X轴旋转45度
cube.rotation.set(Math.PI / 4, 0, 0)
小案例:模拟物体从左到右循环运动,并按X轴旋转
javascript
function render() {
cube.position.x += 0.01
cube.rotation.x += 0.01
if (cube.position.x > 5) {
cube.position.x = 0
}
renderer.render(scene, camera);
// 渲染下一帧的时候就会调用render函数
requestAnimationFrame(render)
}
2.4、应用requestAnimationFrame
首先,我们的render函数有个默认的参数time,代表每一帧的时间,如果你打印会发现每一帧的时间都不太匀速,比如我们上面这个案例,x轴长度为5,假如我打算1秒运动长度1,匀速就是5秒完成,但打印结果并不是匀速的。
javascript
// 匀速
function render(time) {
let t = time / 1000 % 5;
cube.position.x = t * 1
if (cube.position.x > 5) {
cube.position.x = 0
}
renderer.render(scene, camera);
// 渲染下一帧的时候就会调用render函数
requestAnimationFrame(render)
}
2.5、通过Clock跟踪时间处理动画
javascript
// 设置时钟
const clock = new THREE.Clock();
function render() {
// 获取时钟运行的总时长
let time = clock.getElapsedTime();
console.log("时钟运行总时长:", time);
let deltaTime = clock.getDelta();
console.log("两次获取时间的间隔时间:", deltaTime)
let t = time % 5;
cube.position.x = t * 1
if (cube.position.x > 5) {
cube.position.x = 0
}
renderer.render(scene, camera);
// 渲染下一帧的时候就会调用render函数
requestAnimationFrame(render)
}
2.6、Gsap动画库基本使用与原理
bash
npm install gsap
动画库的作用就解决了我们上面手动计算处理动画的问题。
javascript
// 设置动画
// x轴上移动到5的位置,所花费时间5秒
gsap.to(cube.position, {x: 5, duration: 5, ease: "power1.out"})
// 绕x轴上旋转到360度,所花费时间5秒
gsap.to(cube.rotation, {x: 2 * Math.PI, duration: 5})
function render() {
renderer.render(scene, camera);
// 渲染下一帧的时候就会调用render函数
requestAnimationFrame(render)
}
ease属性(速率):
power1.out:起始阶段平滑地加速,然后以逐渐减速的方式结束。
power1.in:起始阶段缓慢加速,然后以较快的速度向目标值靠近。
power1.inOut:在动画开始和结束时,属性的变化速度较慢,然后在动画的中间阶段达到最快的变化速度。
2.7、Gsap控制动画属性与方法
javascript
// 设置动画
// x轴上移动到5的位置,所花费时间5秒
let animate1 = gsap.to(cube.position, {
x: 5,
duration: 5,
ease: "power1.inOut",
repeat: -1, // 设置重复的次数,无限次循环-1
yoyo: true, // 往返运动
delay: 2, // 延迟2秒运动
onStart: () => {
console.log("动画开始")
},
onComplete: () => {
console.log("动画完成")
}
})
// 绕x轴上旋转到360度,所花费时间5秒
gsap.to(cube.rotation, {x: 2 * Math.PI, duration: 5})
// 点击动画,暂停或恢复
window.addEventListener("click", () => {
if(animate1.isActive()) {
animate1.pause(); // 暂停
} else {
animate1.resume(); // 恢复运动
}
})
2.8、根据尺寸变化实现自适应画面
阻尼效果
javascript
// 设置控制器阻尼,让控制器更有真实效果,必须在动画循环里调用update()
controls.enableDamping = true;
function render() {
controls.update();
renderer.render(scene, camera);
// 渲染下一帧的时候就会调用render函数
requestAnimationFrame(render)
}
自适应画面
javascript
// 监听画面变化,更新渲染画面
window.addEventListener("resize", () => {
// 更新摄像头
camera.aspect = window.innerWidth / window.innerHeight;
// 更新摄像机的投影矩阵
camera.updateProjectMatrix();
// 在更新渲染器
renderer.setSize(window.innerWidth, window.innerHeight)
// 设置渲染器的像素比
renderer.setPixelRatio(window.devicePixelRatio);
})
尽管你更改分辨率,这段代码都会保持画面原样。
2.9、调用js接口控制画布全屏和退出全屏
javascript
// 双击控制屏幕进入全屏,退出全屏
window.addEventListener("dblclick", () => {
const fullScreenElement = document.fullscreenElement; // 页面是否处于全屏
if (!fullScreenElement){
renderer.domElement.requestFullscreen(); // 全屏
} else {
document.exitFullscreen(); // 退出全屏
}
})
2.10、应用图形用户界面更改变量
很多时候我们调试3D图像很麻烦,普遍都是改完代码然后看页面效果,而dat.gui就大大简化了我们的操作问题。
javascript
npm install -save dat.gui
javascript
// 导入dat.gui
import * as dat from 'dat.gui'
const gui = new dat.GUI();
gui.add(cube.position, "x").min(0).max(5).name("移动X轴坐标").onChange((value) => {
console.log("值被修改:", value)
}).onFinishChange((value) => {
console.log("完全停下来触发:", value)
})