Threejs入门(二)程序模块化

前言

在上一章中我,我们利用一些基本的类创建了一个简单的程序,并且创建了一个正立方体(虽然看起来像正方形),但是如果你适当的调整相机的位置,是可以让正方形看起来更加像的立体的,有兴趣可以去尝试一下

下面我要讲的是程序的模块化:模块化是一种的软件设计技术,旨在将一个大的程序分解成若干个独立的模块module,每个模块完成一项具体的子功能,它可以提高的代码的可读性复用性可维护性可测试性协同开发,所以开发中使用是很有必要的

我们开发的3D程序一般是嵌套在页面中的,代码肯定也是要和页面其他的功能点相隔离(不分离出来会让代码变得很混乱,并且难以维护),所以我们要为3D程序代码单独创建创建一个模块(组件),为的就是后期的复用方便维护,在使用到的页面直接引用就可以了,提高开发效率

创建应用

书中是用 世界 来进行举例的;我们开发的内容都存在于这个世界之中,所以文件夹以 World 命名 创建的基本步骤:

  1. 初始设置
  2. 创建场景
  3. 创建相机
  4. 创建立方体并将其添加到场景中
  5. 创建渲染器
  6. 渲染场景
初始设置 初始化World 类

首先创建一个 World 文件夹,我们的代码全部存储在这里,在同一层级下创建一个World.js文件,这个文件会将我们所有的功能进行整合,不同模块的参数传递也是通过这个文件进行

js 复制代码
//  src/World.js
class World {
  constructor(container) {}
  render(){}
}
// 导出这个类,方便在main.js中使用
export {World}
js 复制代码
// main.js
import { World } from './src/World.js';
const main = () => {
  const container = document.querySelector('#app')
  const world = new World(container)

  world.render()
}
main()
创建场景

后面一般很少会修改这个文件

书中没有介绍辅助线,我觉得辅助线还是蛮好用的,后面结合OrbitControls使得相机围绕目标进行轨道运动,可以帮助我们更快定位到我们当前所处的位置

注:辅助线红色代表X轴、绿色代表Y轴、蓝色代表Z轴

js 复制代码
// src/World/components/scene.js

import { Color, Scene, AxesHelper } from 'three'
const createScene = () => {
  const scene = new Scene()
  scene.background = new Color('#bedebc') // 画布背景
  
  // 创建辅助线添加到场景
  const axesHelper = new AxesHelper(50) // 设置辅助线长度
  scene.add(axesHelper)
  return scene
}
export {createScene}
创建相机
js 复制代码
// src/World/components/camera.js

import { PerspectiveCamera } from 'three'
const createCamera = () => {
  const fov = 35
  const aspect = 1
  const near = 0.1
  const far = 1000

  const camera = new PerspectiveCamera(fov, aspect, near, far)
  camera.position.set(0,0,20)
  
  return camera
}
export {createCamera}
创建立方体
js 复制代码
// src/World/components/cube.js

import { BoxGeometry, Mesh, MeshBasicMaterial } from 'three'
const createCube = () => {
  const geometry = new BoxGeometry(2,2,2)
  const material = new MeshBasicMaterial({color: 'purple'}) // 紫色
  const cube = new Mesh(geometry, material)

  return cube
}

export {createCube}
设置系统模块
js 复制代码
// src/World/systems/renderer.js

import { WebGLRenderer } from 'three'

const createRenderer = () => {
  const renderer = new WebGLRenderer()
  
  return renderer
}

export {createRenderer}
js 复制代码
// src/World/systems/Resizer.js

class Resizer {
  constructor(container, camera, renderer) {
    camera.aspect = container.clientWidth / container.clientHeight;
    renderer.setSize(container.clientWidth, container.clientHeight);
    renderer.setPixelRatio(window.devicePixelRatio);
  }
}
export {Resizer}

设置World类

首先我们将每个模块都在World.js中导入 我们将设置相机场景渲染器,他们都需要在构造函数中创建,然后在World.render方法中访问, 同时保护好这些组件不要暴露给外界

js 复制代码
// src/World.js
import { createCamera } form './World/components/camera'
import { createScene } form './World/components/scene'
import { createCube } form './World/components/cube'

import { createRenderer } form './World/systems/renderer'
import { Resizer } form './World/systems/Resizer'

class World {
  #camera;
  #scene;
  #renderer;
  constructor(container) {
    this.#camera = createCamera();
    this.#scene = createScene();
    this.#renderer = createRenderer();
    
    const cube = createCube()
    this.#scene.add(cube)
    
    // 将画布添加到容器中
    container.append(this.#renderer.domElement)
    const resizer = new Resizer(container, this.#camera, this.#renderer)
  }
  
  // 渲染场景
  render() {
    this.#renderer.render(this.#scene, this.#camera)
  }
}

按照上面的操作,我们可以得到一个紫色的立方体,下面我们可以通过添加OrbitControls轨道控制器,使用鼠标实现相机位置的变化

js 复制代码
// src/World/systems/controls.js

import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
const createControls = (camera, canvas) => {
  const controls = new OrbitControls(camera, canvas);
  return controls
}

export { createControls }

// src/World.js

})
js 复制代码
// src/World.js
import { createControls } from "./World/systems/controls";
constructor(container) {
  // 在 constructor 中使用轨道控制器
  this.controls = createControls(this.#camera, this.#renderer.domElement)
  // 每次发生变化时都要重新渲染页面
  this.controls.addEventListener('change', () => {
    this.render()
  }
)

这些操作都做好之后,我们会得到一个下面这样的场景,使用鼠标可以旋转这个立方体,并且可以看到空间的辅助线

我们这里看到的立方体或许跟想象的不太一样,是因为我们使用的材质是不需要光照的,后面学习了光照之后,我们会使用MeshStandardMaterial来创建模型的材质,不同材质在不同的灯光下的表现也是不一样的,后面再慢慢探索

轨道控制器

官网链接:threejs.org/docs/?q=Orb...

最后在介绍下轨道控制器:他可以让相机围绕目标进行运动,既然是围绕目标运动,那就必然有一个运动中心,默认的运动中心是(0,0,0),

后面我们可以根据自己的需要进行修改(尤其是多个模型的时候,会经常用到运动中心的修改),下面就说一些轨道控制器常用的方法和属性

js 复制代码
const controls = new OrbitControls(camera, canvas);

// 更改旋转中心的位置,默认(0,0,0)
controls.target.set(10,10,10)

// 增加阻尼(提高旋转操作时的体验,需要调用update())
controls.enableDamping = true
controls.dampingFactor = 0.1

// 自动旋转(需要调用update())
controls.autoRotate = true
controls.autoRotateSpeed = 1

// 移除所有的事件监听,销毁控件controls, 
constrols.dispose()

目前我用到的主要是这些属性,部分属性直接使用是不会生效的,需要在动画循环中调用.update(),这点官网是有介绍的,后面到动画那个篇章会具体的介绍

总结

这一期主要是将各个功能点给分离开来,每一个都可以作为独立的模块,方便后面继续往不同的模块添加新的功能,而不至于让代码变得难以维护。后面的内容也是基于这个结构进行开发的

如果文章中有描述不准确或错误的地方,欢迎各位大佬指正😀😀😀

相关推荐
qq_3643717225 分钟前
Vue 内置组件 keep-alive 中 LRU 缓存淘汰策略和实现
前端·vue.js·缓存
y先森1 小时前
CSS3中的弹性布局之侧轴的对齐方式
前端·css·css3
new出一个对象5 小时前
uniapp接入BMapGL百度地图
javascript·百度·uni-app
你挚爱的强哥6 小时前
✅✅✅【Vue.js】sd.js基于jQuery Ajax最新原生完整版for凯哥API版本
javascript·vue.js·jquery
y先森6 小时前
CSS3中的伸缩盒模型(弹性盒子、弹性布局)之伸缩容器、伸缩项目、主轴方向、主轴换行方式、复合属性flex-flow
前端·css·css3
前端Hardy6 小时前
纯HTML&CSS实现3D旋转地球
前端·javascript·css·3d·html
susu10830189116 小时前
vue3中父div设置display flex,2个子div重叠
前端·javascript·vue.js
IT女孩儿7 小时前
CSS查缺补漏(补充上一条)
前端·css
吃杠碰小鸡8 小时前
commitlint校验git提交信息
前端
虾球xz9 小时前
游戏引擎学习第20天
前端·学习·游戏引擎