three.js极简交互之抓不住的小方块

小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必需的内容
  1. 场景scene------鄙人粗浅的理解为所见世界
  2. 相机camera-------你正在观察的眼睛
  3. 渲染器renderer--------呃,就是渲染
  4. 光PointLight---------伸手不见五指,五指却又存在。所以眼睛看到的时间需要有光。
  5. 控制器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射线吧
  • 射线拾取网格模型步骤
    1. 坐标转化(鼠标单击的屏幕坐标转标准设备坐标)
    2. 射线计算(通过鼠标单击位置+相机参数计算射线值)
    3. 射线交叉计算
  • 因为它的定义名叫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),会看到,只要你点了粉色小方块,它里边就会有内容。那么我们成功的捕获到了粉色小方块。

极简交互之抓不住的小方块

现在可以开始我们的正题了,做一个抓不住的小方块。

  1. 需要以上的全部准备工作。
  2. 需要使用tweenjs,让它动的时候没那么僵硬。
  3. 先把动画写1000毫秒,看一下效果。
  4. 把动画改成1毫秒,那么你抓方块,方块就会跑。
  5. 开始交互
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的时候,它应该会瞬移。

这可能是一个简单的交互闭环,threejs的内容很多,这里连入门都不算,点滴记录,慢慢分享。欢迎大佬们指点~~~~
相关推荐
好名字08212 分钟前
前端取Content-Disposition中的filename字段与解码(vue)
前端·javascript·vue.js·前端框架
隐形喷火龙13 分钟前
element ui--下拉根据拼音首字母过滤
前端·vue.js·ui
m0_7482411226 分钟前
Selenium之Web元素定位
前端·selenium·测试工具
风无雨32 分钟前
react杂乱笔记(一)
前端·笔记·react.js
鑫~阳44 分钟前
快速建站(网站如何在自己的电脑里跑起来) 详细步骤 一
前端·内容管理系统cms
egekm_sefg1 小时前
webrtc学习----前端推流拉流,局域网socket版,一对多
前端·学习·webrtc
m0_748234341 小时前
前端工作中问题点拆分
前端
艾斯特_1 小时前
JavaScript甘特图 dhtmlx-gantt
前端·javascript·甘特图
北海天空1 小时前
reactHooks到底钩到了什么?
前端·react.js
兩尛1 小时前
HTML-CSS(day01)
前端·html