##
> 背景: 创建出threejs 3D场景后如何使用鼠标操作场景内模型元素?,如拖动,放大缩小,平移,点击触发交互等。。。
#### 安装threejs控制器组件
```js
npm install three-orbitcontrols -D
```
#### 第一步
> 创建盛放场景盒子div
```js
```
#### 第二步
> 引入vue, threejs , three-orbitcontrols
```js
import Vue from 'vue';
import * as THREE from 'three';
import * as OrbitControls from 'three-orbitcontrols'
```
#### 第三步
> 创建基础的data数据
```js
data (): DataType {
return {
id: null,
domW: 0,
domH: 0,
scene: null,
camera: null,
renderer: null,
mesh: null,
controls: null
}
},
```
#### 第四步
> 初始运行创建场景,获取场景宽度高度
```js
mounted () {
// 获取盛放场景div标签
this.id = document.getElementById('threeone')
// 获取盛放标签宽高
this.domW = this.id.offsetWidth
this.domH = this.id.offsetHeight
// 运行创建场景函数
this.init()
},
```
#### 第五步
> 创建场景函数讲解
-- 1. 知识点讲解 ---
a. `new THREE.PerspectiveCamera(45, this.domW / this.domH, 0.01, 20000)`
// 设置相机 45 摄像机视锥体垂直视野角度,
// this.domW / this.domH 摄像机视锥体长宽比
// 0.01 摄像机视锥体近端面(摄像机拍摄最近距离)
// 20000 摄像机拍摄最远距离,超出距离模型场景不显示
b. `new THREE.WebGLRenderer({ antialias: true, alpha: true })`
```js
WebGLRenderer( parameters : Object )
// canvas - 一个供渲染器绘制其输出的canvas 它和下面的domElement属性对应。 如果没有传这个参数,会创建一个新canvas
// context - 可用于将渲染器附加到已有的渲染环境(RenderingContext)中。默认值是null
// precision - 着色器精度. 可以是 "highp", "mediump" 或者 "lowp". 如果设备支持,默认为"highp"
// alpha - canvas是否包含alpha (透明度)。默认为 false
// antialias - 是否执行抗锯齿。默认为false. 避免模型渲染出现锯齿现象
// stencil - 绘图缓存是否有一个至少8位的模板缓存(stencil buffer)。默认为true
// preserveDrawingBuffer -是否保留缓缓存直到手动清除或被覆盖。 默认false.
// depth - 绘图缓存是否有一个至少6位的深度缓存(depth buffer )。 默认是true.
// logarithmicDepthBuffer - 是否使用对数深度缓存。如果要在单个场景中处理巨大的比例差异,就有必要使用。
```
> init 函数讲解
```js
init () {
// 实例化场景函数
this.scene = new THREE.Scene()
// 设置相机
this.camera = new THREE.PerspectiveCamera(45, this.domW / this.domH, 0.01, 20000)
// 摄像机z轴位置
this.camera.position.z = 800
// this.camera.position.y = 100
// 实例化渲染器
this.renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: true
})
// 设置渲染器宽高
this.renderer.setSize(this.domW, this.domH)
//将渲染器append到div容器中
this.id.appendChild(this.renderer.domElement)
// 添加灯光
this.addLight()
// 添加辅助线
this.axisHelper()
// 创建正方体
this.initBox()
// 刷帧渲染动画
this.animate()
// 响应屏幕改变大小函数
this.onWindowResize()
// 场景控制器函数
this.controlsFn()
},
```
#### 第五步
> 添加灯光
```js
addLight () {
// 设置环境光
const ambientLight = new THREE.AmbientLight('#ffffff')
this.scene.add(ambientLight)
// 设置平行光
const light = new THREE.DirectionalLight('#ffffff')
this.scene.add(light)
// 设置点光源
const pointLight = new THREE.PointLight('#ffffff', 0.1, 1000)
pointLight.position.set(300, 300, 300)
this.scene.add(pointLight)
},
```
#### 第六步
> 添加空间辅助线
```js
axisHelper () {
// 空间辅助线函数
const axes:THREE.AxesHelper = new THREE.AxesHelper(800)
this.scene.add(axes)
},
```
#### 第七步
> 创建正方体和网格圆球
-- 1. 创建球体知识点 --
a. `new THREE.SphereGeometry()`
```js
new THREE.SphereGeometry(radius : Float, widthSegments : Integer, heightSegments : Integer, phiStart : Float, phiLength : Float, thetaStart : Float, thetaLength : Float)
// radius — 球体半径,默认为1。
// widthSegments — 水平分段数(沿着经线分段),最小值为3,默认值为8。
// heightSegments — 垂直分段数(沿着纬线分段),最小值为2,默认值为6。
// phiStart — 指定水平(经线)起始角度,默认值为0。
// phiLength — 指定水平(经线)扫描角度的大小,默认值为 Math.PI * 2。
// thetaStart — 指定垂直(纬线)起始角度,默认值为0。
// thetaLength — 指定垂直(纬线)扫描角度大小,默认值为 Math.PI。
// 该几何体是通过扫描并计算围绕着Y轴(水平扫描)和X轴(垂直扫描)的顶点来创建的。 因此,不完整的球体(类似球形切片)可以通过为phiStart,phiLength,thetaStart和thetaLength设置不同的值来创建, 以定义我们开始(或结束)计算这些顶点的起点(或终点)。
```
> initBox 函数讲解
```js
initBox () {
// 添加正方体
const geometry = new THREE.BoxGeometry(200, 200, 200)
// 添加正方体材质
const material = new THREE.MeshBasicMaterial({ color: '#ffffff', map: new THREE.TextureLoader().load('https://www.douchuanwei.com/api/files/img/1.jpeg') })
// 合成正方体mesh网格
this.mesh = new THREE.Mesh(geometry, material)
this.scene.add(this.mesh)
// 创建球体
const geometry2 = new THREE.SphereGeometry(60, 20, 60)
// 创建球体材质
const material2 = new THREE.MeshLambertMaterial({
color: 'red',
wireframe: true
// emissive:0xad1919,
// vertexColors: THREE.NoColors
// specular:0x4488ee,
// shininess: 12
})
// 将球体和材质添加到mesh网格
const mesh2 = new THREE.Mesh(geometry2, material2)
// 球体从中心点向x轴平移250
mesh2.translateX(250)
this.scene.add(mesh2)
// 创建第二个球体
const geometry3 = new THREE.SphereGeometry(60, 20, 60)
const material3 = new THREE.MeshLambertMaterial({
color: 'red', // 红色球体
wireframe: true // 将球体变成网格
// emissive:0xad1919,
// vertexColors: THREE.NoColors
// specular:0x4488ee,
// shininess: 12
})
const mesh3 = new THREE.Mesh(geometry3, material3)
mesh3.translateX(-250)
this.scene.add(mesh3)
},
```
#### 第八步
> 帧渲染动画,让场景动起来
```js
animate () {
window.requestAnimationFrame(this.animate)
// 正方体旋转
this.mesh.rotation.y += 0.02
this.mesh.rotation.x += 0.01
// 场景旋转
this.scene.rotation.y += 0.01
this.renderer.render(this.scene, this.camera)
}
```
#### 第九步
> 启用场景控制器
```js
controlsFn () {
// tslint:disable-next-line
this.controls = new OrbitControls(this.camera, this.renderer.domElement)
},
```
#### controls 知识点
```js
controls.enablePan = false; //禁止右键拖拽
controls.enableZoom = false;//禁止缩放
controls.enableRotate = false; //禁止旋转
controls.minZoom = 0.5; // 最小缩放比例
controls.maxZoom = 2; // 放大最大比例
// 上下旋转范围
controls.minPolarAngle = 0;
controls.maxPolarAngle = Math.PI;
// 左右旋转范围
controls.minAzimuthAngle = -Math.PI * (100 / 180);
controls.maxAzimuthAngle = Math.PI * (100 / 180);
//将其设为true,以自动围绕目标旋转。请注意,如果它被启用,你必须在你的动画循环里调用.update()。
controls.autoRotate = true
// 当.autoRotate为true时,围绕目标旋转的速度将有多快,默认值为2.0,相当于在60fps时每旋转一周需要30秒。
controls.autoRotateSpeed = 2
// 当使用键盘按键的时候,相机平移的速度有多快。默认值为每次按下按键时平移7像素。
controls.keyPanSpeed = 7
// 这一对象包含了用于控制相机平移的按键代码的引用。默认值为4个箭头(方向)键。
controls.keys = {
LEFT: 'ArrowLeft', //left arrow
UP: 'ArrowUp', // up arrow
RIGHT: 'ArrowRight', // right arrow
BOTTOM: 'ArrowDown' // down arrow
}
// 移除所有的事件监听
controls.dispose ()
// 为指定的DOM元素添加按键监听。推荐将window作为指定的DOM元素。
controls.listenToKeyEvents( domElement : HTMLDOMElement )
// 更新控制器。必须在摄像机的变换发生任何手动改变后调用,或如果.autoRotate或.enableDamping被设置时,在update循环里调用。
controls.update ()
```
#### 完整代码示例
```js
import Vue from 'vue';
import * as THREE from 'three';
import * as OrbitControls from 'three-orbitcontrols'
interface DataType {
id: HTMLElement | null | Object | any;
domW: Number | any;
domH: Number | any;
scene: THREE.Scene | null | any;
camera: THREE.Camera | null | any;
renderer: THREE.Renderer | null | any;
mesh: THREE.Mesh | any;
controls: null | any;
}
export default Vue.extend({
name: 'ThreeTwo',
layout: 'threelayout',
data (): DataType {
return {
id: null,
domW: 0,
domH: 0,
scene: null,
camera: null,
renderer: null,
mesh: null,
controls: null
}
},
mounted () {
this.id = document.getElementById('threeone')
this.domW = this.id.offsetWidth
this.domH = this.id.offsetHeight
this.init()
},
methods: {
init () {
this.scene = new THREE.Scene()
this.camera = new THREE.PerspectiveCamera(45, this.domW / this.domH, 0.01, 20000)
this.camera.position.z = 800
// this.camera.position.y = 100
this.renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: true
})
this.renderer.setSize(this.domW, this.domH)
this.id.appendChild(this.renderer.domElement)
this.addLight()
this.axisHelper()
this.initBox()
this.animate()
this.onWindowResize()
this.controlsFn()
},
controlsFn () {
// tslint:disable-next-line
this.controls = new OrbitControls(this.camera, this.renderer.domElement)
},
initBox () {
const geometry = new THREE.BoxGeometry(200, 200, 200)
const material = new THREE.MeshBasicMaterial({ color: '#ffffff', map: new THREE.TextureLoader().load('https://www.douchuanwei.com/api/files/img/1.jpeg') })
this.mesh = new THREE.Mesh(geometry, material)
this.scene.add(this.mesh)
const geometry2 = new THREE.SphereGeometry(60, 20, 60)
const material2 = new THREE.MeshLambertMaterial({
color: 'red',
wireframe: true
// emissive:0xad1919,
// vertexColors: THREE.NoColors
// specular:0x4488ee,
// shininess: 12
})
const mesh2 = new THREE.Mesh(geometry2, material2)
mesh2.translateX(250)
this.scene.add(mesh2)
const geometry3 = new THREE.SphereGeometry(60, 20, 60)
const material3 = new THREE.MeshLambertMaterial({
color: 'red',
wireframe: true
// emissive:0xad1919,
// vertexColors: THREE.NoColors
// specular:0x4488ee,
// shininess: 12
})
const mesh3 = new THREE.Mesh(geometry3, material3)
mesh3.translateX(-250)
this.scene.add(mesh3)
},
addLight () {
const ambientLight = new THREE.AmbientLight('#ffffff')
this.scene.add(ambientLight)
const light = new THREE.DirectionalLight('#ffffff')
this.scene.add(light)
const pointLight = new THREE.PointLight('#ffffff', 0.1, 1000)
pointLight.position.set(300, 300, 300)
this.scene.add(pointLight)
},
axisHelper () {
const axes:THREE.AxesHelper = new THREE.AxesHelper(800)
this.scene.add(axes)
},
onWindowResize () {
window.onresize = () => {
this.domH = this.id.offsetHeight
this.domW = this.id.offsetWidth
this.camera.aspect = this.domW / this.domH
this.camera.updateProjectionMatrix()
this.renderer.setSize(this.domW, this.domH)
}
},
animate () {
window.requestAnimationFrame(this.animate)
this.mesh.rotation.y += 0.02
this.mesh.rotation.x += 0.01
this.scene.rotation.y += 0.01
this.renderer.render(this.scene, this.camera)
}
}
});
```
```
[更多内容请到小豆包》](https://www.douchuanwei.com/)
> 扫码访问小豆包
>
![23_34de43370a343405db1b00359bb895cd.png](https://www.douchuanwei.com/api/files/2022-03-05/png/23_34de43370a343405db1b00359bb895cd.png)
### 扫码关注小豆包公众号
![小豆包公众号.jpg](https://www.douchuanwei.com/api/files/2022-03-05/jpeg/小豆包公众号.jpg)