完整目录:
1、html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
html,
body {
width: 100%;
height: 100%;
overflow: hidden;
margin: 0;
padding: 0;
}
#app {
width: 100%;
height: 100%;
}
</style>
<script type="importmap">
{
"imports": {
"three": "./three/three.module.js",
"three/examples/": "./three/examples/"
}
}
</script>
<script type="module">
import * as THREE from 'three'
import { Stage } from './Stage.js'
window.THREE = THREE;
class HelloWorldDemo {
constructor(stage) {
this.stage = stage;
this.addBox(100);
}
addBox(size) {
var geometry = new THREE.BoxGeometry(size, size, size);
var material = new THREE.MeshBasicMaterial({
color: 0x63e42a,
emissive: 0x072534,
side: THREE.DoubleSide,
})
var cube = new THREE.Mesh(geometry, material);
cube.name = "test_cube"
this.stage.scene.add(cube);
return cube;
}
}
window.onload = () => {
const stage = new Stage("#app", {
// cameraType: "orthographi"
});
stage.run();
window.stage = stage;
new HelloWorldDemo(stage);
};
</script>
<div id="app"></div>
</head>
<body>
</body>
</html>
2、Stage.js
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
export class Stage {
/**
*
* @param {*} container
* @param {*} param
* @param {* } param.disableControl
* @param {* } param.cameraType "perspective" / "orthographi"
* @param {boolean} param.needLight
*/
constructor(container, param) {
this.mouse = new THREE.Vector2()
this.raycaster = new THREE.Raycaster();
param = param || {}
param.cameraType = param.cameraType || "perspective"
param.needLight = param.needLight || true;
this.fuArr = []
this.viceCamera = null
this.tmpTarget = new THREE.Vector3()
this.orbitControls = null
this.cameraHelper = null
this.container = container;
this.initFlag = false;
// 场景
this.scene = new THREE.Scene();
this.scene.name = "moumade";
if (param.needLight) {
// 环境光
var ambient = new THREE.AmbientLight(0xffffff, 0.5);
ambient.name = "ambient";
this.scene.add(ambient);
let dirLight = new THREE.DirectionalLight(0xffffff, 0.5);
dirLight.position.set(100, 100, 100);
dirLight.name = "dirLight";
this.scene.add(dirLight);
}
// 渲染器
this.containerEle = document.querySelector(container);
let vW = this.containerEle.clientWidth;
let vH = this.containerEle.clientHeight;
this.renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: false,
// preserveDrawingBuffer: true,
// failIfMajorPerformanceCaveat: true,
});
// this.renderer.setClearColor(0x33334c, 1.0);
this.renderer.setClearColor(0xf8f8f8, 1.0);
// this.renderer.autoClear = true;
this.renderer.setSize(vW, vH, false);
this.renderer.setPixelRatio(window.devicePixelRatio);
// console.error(vW, vH);
this.containerEle.appendChild(this.renderer.domElement);
if (param.cameraType == "perspective") {
this.camera = new THREE.PerspectiveCamera(45, 1, 1, 500000000)
} else if (param.cameraType == "orthographi") {
this.camera = new THREE.OrthographicCamera(-vW / 2, vW / 2, -vH / 2, vH / 2, 0.1, 10000)
}
this.camera.lookAt(0, 0, 0)
this.camera.name = "camera";
this.scene.add(this.camera);
this.handleResize = this.handleResize.bind(this)
this._loop = this._loop.bind(this)
window.addEventListener("resize", this.handleResize);
this._isRun = true;
// window.addEventListener("pointerdown", (event) => {
// this.mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
// this.mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
// this.raycaster.setFromCamera(this.mouse, this.camera)
// const intersects = this.raycaster.intersectObjects(this.scene.children);
// console.error(intersects[0].point);
// });
this.handleResize();
if (param && param.disableControl) {
} else {
this.initControls();
}
this.camera.position.set(0, 0, 1000)
if (!param.disableAxis) {
// let axis = new THREE.AxesHelper(10000000);
// this.scene.add(axis);
// this.axis = axis;
}
// @ts-ignore
// this.stats = new Stats();
// this.stats.domElement.style.position = 'absolute';
// this.stats.domElement.style.left = '0px';
// this.stats.domElement.style.top = '0px';
// this.stats.domElement.style.width = '100px';
// this.stats.domElement.id = 'stats';
// this.containerEle.appendChild(this.stats.dom);
this._loop()
}
initControls() {
let control = new OrbitControls(this.camera, this.renderer.domElement);
this.control = control
// 使动画循环使用时阻尼或自转 意思是否有惯性
control.enableDamping = true;
//动态阻尼系数 就是鼠标拖拽旋转灵敏度
control.dampingFactor = 0.35;
//是否可以缩放
control.enableZoom = true;
control.zoomSpeed = 0.35;
//是否自动旋转
control.autoRotate = false;
//设置相机距离原点的最远距离
// control.minDistance = 22; //1000
//设置相机距离原点的最远距离
// control.maxDistance = 50; //3000
//是否开启右键拖拽
// control.enablePan = false;
}
handleResize() {
// 获取新的大小
let vpW = this.containerEle.clientWidth;
let vpH = this.containerEle.clientHeight;
// 设置场景
this.renderer.domElement.width = vpW;
this.renderer.domElement.height = vpH;
this.renderer.setSize(this.containerEle.clientWidth, this.containerEle.clientHeight);
// 设置相机
(this.camera).aspect = vpW / vpH;
this.camera.updateProjectionMatrix();
}
run() {
// this._loop()
this._isRun = true;
}
stop() {
this._isRun = false;
}
addViceCamera(viceCamera) {
this.viceCamera = viceCamera
}
// setRender(v: any) {
// this.state = v
// }
onUpdate(fu) {
this.fuArr.push(fu)
}
_loop() {
// this.stats.update()
this.camera.updateProjectionMatrix();
this.camera.updateMatrixWorld()
this._isRun !== false && this.renderer.render(this.scene, this.camera);
this.fuArr.forEach(fun => {
fun()
});
this.control && this.control.update()
requestAnimationFrame(this._loop)
}
}