UNPKG

@vci/quick-three

Version:

quick three

233 lines (218 loc) 8.03 kB
import { addCss, clearScheduledTasks, EventDispatcher, isElement, isEmpty, mergeDeep, release, uuid } from "@vci/helper"; import O3DisposerHelper from "./helper/O3DisposerHelper"; import { remove, update } from "@tweenjs/tween.js"; import { createPerspectiveCamera } from "./creator/cameras/createPerspectiveCamera"; import { PluginHelper } from "./plugins/PluginHelper"; import { PluginLog } from "./plugins/PluginLog"; import { QtPlugin } from "./plugins/QtPlugin"; import { QtEvents } from "./events/QtEvents"; import { AxesHelper, Clock, GridHelper } from "three"; import { createScene } from "./creator/createScene"; // eslint-disable-next-line no-undef const debug = process.env.NODE_ENV === "development"; class QuickThree extends EventDispatcher { constructor(option = {}) { super(); this.option = mergeDeep({ // 开发环境标识 debug: isEmpty(option.debug) ? debug : option.debug, // 实例名称 name: "3D调试控制面板", // 挂载元素 el: null, // dpr dpr: window.devicePixelRatio || 1.5, // 相机 camera: { fov: 50, near: 0.001, far: 1e3, position: [0, 100, 0] }, // 网格辅助 gridHelper: { enabled: true, size: 100, divisions: 10, color: [0x999999, 0x616161] }, // 是否开启坐标轴辅助 axesHelper: { enabled: true, size: 100 }, // 是否启用动画帧 enableAnimate: true, // 是否开启TWEEN.update enableUpdateTween: true, // 插件 plugins: [], // 自定义渲染逻辑 customRender: null }, option); this.init(); } init() { const { name, enableAnimate } = this.option; // 必要属性 this.initProperties(); // 日志 this.log = this.use(PluginLog, PluginLog.namespace, { name }); // 辅助工具 this.helper = this.use(PluginHelper, PluginHelper.namespace); // 安装插件 this.installPlugins(QtPlugin.Location.AfterInitProperties); // 元素 this.initElement(); // Three.js this.initThree(); // 启动动画帧 enableAnimate && this.animate(); setTimeout(() => this.dispatchEvent(QtEvents.AfterMount)); // 安装插件 this.installPlugins(QtPlugin.Location.AfterInit); } // 必要属性 initProperties() { this.frame = -1; Object.defineProperty(this, "debug", { get: () => this.option.debug }); Object.defineProperty(this, "dpr", { get: () => this.option.dpr }); this.things = []; this.plugins = []; this.state = {}; this.tw = {}; this.inter = {}; // three instance this.scenes = new Map(); this.cameras = new Map(); this.lights = new Map(); // debug模式下将实例挂载到windows上 this.debug && (window.qt = this); } // 挂在元素 initElement() { const { option } = this; if (!isElement(this.option.el)) throw new Error(this.log.format(`请传入正确得HTML元素, 当前: ${option.el}`)); const el = document.createElement("div"); this.option.el.appendChild(el); addCss(el, { position: "relative", width: "100%", height: "100%" }); Object.defineProperty(this, "el", { get: () => el }); Object.defineProperty(this, "elWidth", { get: () => this.el.clientWidth }); Object.defineProperty(this, "elHeight", { get: () => this.el.clientHeight }); } // Three.js基础组件 initThree() { const { elWidth, elHeight, option } = this; //---------- 场景 createScene({ qt: this, name: "core" }); //---------- 辅助网格 this.debug && option.gridHelper.enabled && this.scene.add(new GridHelper(option.gridHelper.size, option.gridHelper.divisions, option.gridHelper.color[0], option.gridHelper.color[1])); this.debug && option.axesHelper.enabled && this.scene.add(new AxesHelper(option.axesHelper.size)); //---------- 相机 Object.defineProperty(this, "camera", { set: camera => { this._camera = camera; this.dispatchEvent(QtEvents.ChangeCamera, { camera }); }, get: () => this._camera }); this.camera = createPerspectiveCamera({ qt: this, ...option.camera, aspect: elWidth / elHeight }); //---------- 时钟 this.clock = new Clock(); this.delta = this.clock.getDelta(); } use(Plugin, namespace, option) { if (!Plugin || (Plugin && !Plugin.isQtPlugin)) throw new Error(`${Plugin}是不是一个QtPlugin`); if (this[namespace]) throw new Error(`已存在命名空间为${namespace}的插件`); const plugin = new Plugin(this, mergeDeep(option || {}, { namespace: namespace || `plugin@${uuid()}` })); option && !isEmpty(option.order) && (plugin.order = option.order); this.plugins.push(plugin); this.plugins = this.plugins.sort((a, b) => a.order - b.order); return plugin; } installPlugins(location = QtPlugin.Location.AfterInit) { this.option.plugins.forEach(plugin => { if (plugin.isQtPlugin) location === plugin.location && this.use(plugin, plugin.namespace); else location === (plugin.location || plugin.plugin.location) && this.use(plugin.plugin, plugin.namespace || plugin.plugin.namespace, plugin.option); }); } // 动画帧 animate(timestamp) { this.delta = this.clock.getDelta(); this.dispatchEvent(QtEvents.BeforeAnimateFrame, { delta: this.delta, timestamp }); this.plugins.forEach(plugin => plugin.dispatchEvent(QtPlugin.Events.BeforeRaf, { delta: this.delta, timestamp })); this.things.forEach(thing => thing.animate(this.delta, timestamp)); this.render(this.delta, timestamp); this.option.enableUpdateTween && update(); this.plugins.forEach(plugin => plugin.dispatchEvent(QtPlugin.Events.AfterRaf, { delta: this.delta, timestamp })); this.dispatchEvent(QtEvents.AfterAnimateFrame, { delta: this.delta, timestamp }); this.frame = requestAnimationFrame(this.animate.bind(this)); } render(delta, timestamp) { this.dispatchEvent(QtEvents.BeforeRender, { delta: this.delta, timestamp }); if (this.option.customRender) this.option.customRender(this, delta, timestamp); else this.plugins.forEach(plugin => plugin.dispatchEvent(QtPlugin.Events.Render, { delta, timestamp })); this.dispatchEvent(QtEvents.AfterRender, { delta: this.delta, timestamp }); } // 场景矩阵变换处理 matrixChangedHandle(isWindowResize = false) { this.things.forEach(thing => thing.matrixChange(isWindowResize)); this.dispatchEvent(QtEvents.AfterMatrixChange, { isWindowResize, qt: this }); } // 清空实例化事物 clearThings(force = false, enableCr = true) { const thingsImmortal = this.things.filter(thing => thing.isImmortal); this.things.forEach(thing => thing.destroy(force, enableCr)); this.things = force ? [] : thingsImmortal; this.helper.updateEventO3s(); } // 清除things属性中已经销毁的Thing实例 clearDestroyedThings() { this.things = this.things.filter(t => !t.isDestroyed); this.helper.updateEventO3s(); } // 销毁 destroy() { this.dispatchEvent(QtEvents.BeforeDestroy); // 移除元素 this.el.parentNode && this.el.parentNode.removeChild(this.el); // 清空所有定时器 clearScheduledTasks(this.inter); // 停止帧动画 cancelAnimationFrame(this.frame); // 移除Tween实例 Object.keys(this.tw).forEach(k => { if (this.tw[k]) { remove(this.tw[k]); delete this.tw[k]; } }); // 销毁插件 this.plugins.forEach(plugin => plugin.destroy()); // 清空事物 this.clearThings(true, false); // 销毁场景内所有模型实例 this.scenes.forEach(scene => O3DisposerHelper.DisposeObject3D(scene.children, scene)); this.dispatchEvent(QtEvents.AfterDestroy); this.log.info("QuickThree已销毁"); // 释放对象 release(this); this.isDestroyed = true; } } export { debug }; export default QuickThree;