小tips 新手入门
由于可能接下来的工作需要,卑微页面CV战士不得不参与到three.js的学习中。天赋很差,学得很慢,埋头看了一周官方文档,才可以做一些简单交互。特效及动画的掌握还不是很熟悉,不过还是简单记录一下成果,避免过几天又忘记了。
好了,废话不多说,开始记录。
- 前端框架:vue(当然写这个跟VUE没有什么关系,只是习惯了它的语法)
- js库:threejs 、 tweenjs
上代码
- 首先,需要一个容器,这是用来呈现canvas的。
vue
<template>
<div ref="app"></div>
</template>
- 导入相关(threejs相关内容,GUI、TWEEN)
js
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { GUI } from "three/examples/jsm/libs/lil-gui.module.min.js";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
import TWEEN from "@tweenjs/tween.js";
- 需要先了解threejs必需的内容
- 场景scene------鄙人粗浅的理解为
所见世界
- 相机camera-------
你正在观察的眼睛
- 渲染器renderer--------
呃,就是渲染
- 光PointLight---------
伸手不见五指,五指却又存在。所以眼睛看到的时间需要有光。
- 控制器controls-------
它支持你拖拽缩放你的所见世界
- 那么,在data里先定义它们:
vue
data() {
return {
camera: null, //相机
renderer: null, //渲染器
scene: null, //场景
tween: null, //动画
controls: null, //控制器
loaderGltf: null, //加载器
gui: null, //调试工具
raycaster: null, //射线
width: null,
height: null,
};
这里我有定义一个叫射线的,稍后会用的。
threejs初始化
前期准备工作已经完成,现在可以在vue的mounted钩子里做一些初始化。
vue
//定义画布宽高
this.width = window.innerWidth;
this.height = window.innerHeight;
//初始化场景
this.scene = new THREE.Scene();
//创建一个透视相机 相机的种类官方文档讲的很明白
this.camera = new THREE.PerspectiveCamera(
75,
this.width / this.height,
0.1,
1000
);
//你可以用各种方式去定义相机位置
this.camera.position = new THREE.Vector3(50, 20, 80);
//this.camera.position.x = 50;
//this.camera.position.y = 20;
//this.camera.position.z = 80;
//初始化渲染器
this.renderer = new THREE.WebGLRenderer();
this.renderer.setSize(this.width, this.height);
//将渲染内容放入我们定义的容器
this.$refs.app.appendChild(this.renderer.domElement);
//初始化控制器
this.controls = new OrbitControls(this.camera, this.renderer.domElement);
this.controls.target = new THREE.Vector3(0, 0, 0);
this.controls.update();
做到这里的时候,你可能什么都看不到,但是在浏览器控制台能检测到,你有一个canvas元素存在。或者你给场景scene一个背景色,也能看到canvas世界。
不用着急,我们现在就创建元素
听说猛男粉很流行,那么第一个元素就创建一个粉色的小方块吧
vue
this.geometry = new THREE.BoxGeometry(10, 10, 10);
this.material = new THREE.MeshBasicMaterial({ color: "pink" });
this.cube = new THREE.Mesh(this.geometry, this.material);
this.scene.add(this.cube);//请记得加入你的世界
或者,如果你懂3D建模,你可以做一个你喜欢的模型,然后将它载入,GLTFLoader是一个相关格式的导入loader,当然threejs还支持很多格式。
让它动起来
这时,你的世界
可能看上去有点单调无趣,不用担心,我们第一件事情就是把它动起来。requestAnimationFrame
这个方法是threejs提供的,依据FPS刷新,就会有视觉上的连续动画。我们给它rotation+=0.02
vue
animate() {
requestAnimationFrame(this.animate);
this.cube.rotation.x += 0.02;
this.cube.rotation.y += 0.02;
this.controls.update();
this.renderer.render(this.scene, this.camera);
}
好了,你可以清除的看到粉色小方块在转动,或许这是代码赋予了它生命吧
跟它交互一下
它既然有了生命,那么我们和它互动一下吧,先点click
它一下。
vue
// 鼠标事件监听---点击事件可以有很多写法,怎么开心怎么点
this.renderer.domElement.addEventListener(
"pointerdown",
this.onSceneClick,
false
);
在点击的时候,想到严肃一个问题,点击的是整个画布,所有位置都会触发响应,那么我怎么能确定点击了那个粉色小方块???小朋友我是否有很多问号???????
raycaster射线
- 还记得文章最开始在data里边定义了一个raycaster射线吧
- 射线拾取网格模型步骤
- 坐标转化(鼠标单击的屏幕坐标转标准设备坐标)
- 射线计算(通过鼠标单击位置+相机参数计算射线值)
- 射线交叉计算
- 因为它的定义名叫raycaster,我联想到的是激光,沿着鼠标发射激光,会打中粉色小方块,那么raycaster会告诉我它击中了哪些物体。
vue
// 将鼠标位置归一化为设备坐标
this.mouse.x = (event.clientX / this.width) * 2 - 1;
this.mouse.y = -(event.clientY / this.height) * 2 + 1;
// 使用raycaster检测场景中的对象
this.raycaster.setFromCamera(this.mouse, this.camera);
// 获取与射线相交的第一个对象
const intersects = this.raycaster.intersectObjects([this.cube]);
你可以console.log(intersects),会看到,只要你点了粉色小方块,它里边就会有内容。那么我们成功的捕获到了粉色小方块。
极简交互之抓不住的小方块
现在可以开始我们的正题了,做一个抓不住的小方块。
- 需要以上的全部准备工作。
- 需要使用tweenjs,让它动的时候没那么僵硬。
- 先把动画写1000毫秒,看一下效果。
- 把动画改成1毫秒,那么你抓方块,方块就会跑。
- 开始交互
vue
if (intersects.length > 0) {
// 点中了交互元素
intersects[0].object.material.color.set("#d4e8d6");
const startPosition = intersects[0].object.position; // 初始位置
const startScale = intersects[0].object.scale; //初始缩放
const endPosition = new THREE.Vector3(
Math.random() * 50,
Math.random() * 50,
Math.random() * 50
); // 目标位置
const endScale = new THREE.Vector3(1.5, 1.5, 1.5); //目标缩放
const duration = 1000; // 渐变时间(毫秒)
this.tween = new TWEEN.Tween({
position: startPosition,
scale: startScale,
});
this.tween.to({ position: endPosition, scale: endScale }, duration);
this.tween.onUpdate((object) => {});
this.tween.start();
} else {
// 没有点中交互元素
console.log("没有点击到元素");
}
当duration
是1000的时候,你点粉色小方块的时候,它会慢慢飘走,但是当duration
是1的时候,它应该会瞬移。