| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441 |
- import * as THREE from 'three';
- import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader.js';
- // 导入轨道控制器
- import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
- import { CSS3DRenderer, CSS3DObject } from 'three/examples/jsm/renderers/CSS3DRenderer'
- // import gsap from 'gsap';
- import ResourceTracker from '/@/utils/threejs/ResourceTracker';
- import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
- import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader'
- class UseThree {
- canvasContainer: HTMLCanvasElement;
- CSSCanvasContainer: HTMLCanvasElement | null = null;
- camera: THREE.PerspectiveCamera | null = null;
- scene: THREE.Scene | null = null;
- renderer: THREE.WebGLRenderer | null = null;
- css3dRender: CSS3DRenderer | null = null;
- orbitControls: OrbitControls | null = null;
- giftLoader: THREE.Object3D | null = null;
- animationMixer: THREE.AnimationMixer | null = null;
- animationAction: THREE.AnimationAction | null = null;
- clock: THREE.Clock = new THREE.Clock(); // 计时器
- timeoutId: NodeJS.Timeout | null = null;
- animationId: number = 0
- resourceTracker:ResourceTracker | null = null
- track: any = null
- spriteText: THREE.Sprite | null = null
- constructor(canvasSelector, cssCanvas?) {
-
- this.resourceTracker = new ResourceTracker()
- this.track = this.resourceTracker.track.bind(this.resourceTracker)
- this.animationId = 0
- this.canvasContainer = document.querySelector(canvasSelector);
- //初始化
- this.init(cssCanvas);
- this.animate();
- window.addEventListener('resize', this.resizeRenderer.bind(this));
- // 添加滚动事件,鼠标滚动模型执行动画
- // window.addEventListener('wheel', this.wheelRenderer.bind(this));
- }
- init(cssCanvas?) {
- // 初始化场景
- this.initScene();
- // 初始化环境光
- this.initLight();
- // 初始化相机
- this.initCamera();
- //初始化渲染器
- this.initRenderer();
- // 初始化控制器
- this.initControles();
- if(cssCanvas){
- this.initCSSRenderer(cssCanvas)
- // this.addTextSprite()
- }
- // this.setTestPlane()
- }
- initScene() {
- this.scene = this.track(new THREE.Scene());
- // const axesHelper = new THREE.AxesHelper(100);
- // this.scene?.add(axesHelper);
- // const size = 1000;
- // const divisions = 10;
- // const gridHelper = new THREE.GridHelper( size, divisions );
- // this.scene?.add( gridHelper )
- }
- initLight() {
- const light = this.track(new THREE.AmbientLight(0xffffff, 1));
- light.position.set(0, 1000, 1000);
- (this.scene as THREE.Scene).add(light);
- }
- initCamera() {
- this.camera = this.track(new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.01, 1000));
- this.camera?.position.set(0, 0.2, 0.3);
- // const helper = new THREE.CameraHelper( this.camera);
- // this.scene?.add( helper );
-
- }
- initRenderer() {
- this.renderer = this.track(new THREE.WebGLRenderer({ antialias: true, alpha:true })) as THREE.WebGLRenderer;
- // 设置屏幕像素比
- this.renderer?.setPixelRatio(window.devicePixelRatio);
- // 设置渲染的尺寸
- this.renderer?.setSize(window.innerWidth, window.innerHeight);
- // 色调映射
- this.renderer.toneMapping = THREE.ACESFilmicToneMapping;
- this.renderer.outputEncoding = THREE.sRGBEncoding;
- this.renderer.shadowMap.enabled = true
- this.renderer.shadowMap.type = THREE.PCFSoftShadowMap
- // 曝光程度
- this.renderer.toneMappingExposure = 1;
- this.canvasContainer.appendChild(this.renderer.domElement);
-
- }
- initCSSRenderer(cssCanvas) {
- this.CSSCanvasContainer = document.querySelector(cssCanvas);
- this.css3dRender = this.track(new CSS3DRenderer()) as CSS3DRenderer
- this.css3dRender.setSize((window.innerWidth), (window.innerHeight))
- this.CSSCanvasContainer?.appendChild(this.css3dRender.domElement)
- this.css3dRender.render(this.scene as THREE.Scene, this.camera as THREE.PerspectiveCamera)
- this.orbitControls = this.track(new OrbitControls(this.camera as THREE.Camera, this.css3dRender?.domElement)) as OrbitControls;
- this.orbitControls.update()
- // this.orbitControls.enableZoom = false;
- // // to disable rotation
- // this.orbitControls.enableRotate = false;
- // // to disable pan
- // this.orbitControls.enablePan = false;
- }
- initControles() {
- this.orbitControls = this.track(new OrbitControls(this.camera as THREE.Camera, this.renderer?.domElement)) as OrbitControls;
- this.orbitControls.update()
- // this.orbitControls.enableZoom = false;
- // // to disable rotation
- // this.orbitControls.enableRotate = false;
- // // to disable pan
- // this.orbitControls.enablePan = false;
- }
- setModalCenter(group) {
- var box3 = new THREE.Box3()
-
- // 计算层级模型group的包围盒
- // 模型group是加载一个三维模型返回的对象,包含多个网格模型
- box3.expandByObject(group)
- // 计算一个层级模型对应包围盒的几何体中心在世界坐标中的位置
- var center = new THREE.Vector3()
- box3.getCenter(center)
- // console.log('查看几何体中心坐标', center);
- // 重新设置模型的位置,使之居中。
- group.position.x = group.position.x - center.x
- group.position.y = group.position.y - center.y
- group.position.z = group.position.z - center.z
- };
- setModel(modalName) {
- const a = new Date().getTime() / 1000
- return new Promise(async (resolve, reject) => {
- try {
- const db = window['CustomDB']
- const modalArr = await db.modal.where('modalName').equals(modalName).toArray()
- // debugger
- if(modalArr.length > 0) {
- const modalValue = modalArr[0].modalVal
- try {
- const gltfLoader = new GLTFLoader()
- const dracoLoader = new DRACOLoader();
- dracoLoader.setDecoderPath( '/3D/draco/gltf/' );
- dracoLoader.setDecoderConfig({ type: "js" }); //使用兼容性强的draco_decoder.js解码器
- dracoLoader.preload();
- gltfLoader.setDRACOLoader(dracoLoader);
- gltfLoader.setPath('/3d/glft/')
- gltfLoader.parse(modalValue, '/3d/glft/', (gltf) => {
- const object = this.track(gltf.scene.children[0])
- this.setModalCenter(object)
- object.traverse((obj) => {
- if (obj instanceof THREE.Mesh) {
- obj.material.emissiveIntensity = 1
- obj.material.emissiveMap = obj.material.map
- if(obj.name !== "buxiugangse") {
- obj.receiveShadow = true
- }
- obj.castShadow = true
-
-
- } else if (obj.type === 'Object3D') {
- // const text3D = this.addYFText(obj)
- // const textCQ3D = this.addCQText(obj)
- // gltf.scene.add(text3D)
- // gltf.scene.add(textCQ3D)
- }
- })
- this.scene?.add(object);
- console.log(object);
- resolve(object)
- }, (err)=> {
- console.log(err);
-
- })
-
- } catch (error) {
- console.log(error);
- }
- }
- } catch (error) {
- reject('加载模型出错')
- }
- })
- }
- setTestPlane() {
- const plane = new THREE.Mesh(
- new THREE.PlaneGeometry( 100, 100 ),
- new THREE.MeshPhongMaterial( { color: 0x999999, specular: 0x101010 } )
- );
- plane.rotation.x = - Math.PI / 2;
- plane.position.y = 0.03;
- plane.receiveShadow = true;
- // this.scene?.add( plane );
- }
-
- /* 自定义材质 */
- setCustomMaterial(group){}
- /* 场景环境背景 */
- setEnvMap(hdr) {
- // new RGBELoader().setPath('/public/3D/hdr/').load(hdr + '.jpeg', (texture) => {
- // debugger
- // texture.mapping = THREE.EquirectangularReflectionMapping;
- // (this.scene as THREE.Scene).background = texture;
- // (this.scene as THREE.Scene).environment = texture;
- // });
- new THREE.TextureLoader().setPath('/3D/hdr/').load(hdr + '.jpeg', (texture) => {
- texture.mapping = THREE.EquirectangularReflectionMapping;
- // (this.scene as THREE.Scene).background = new THREE.Color('#00000000');
- (this.scene as THREE.Scene).environment = texture;
- });
- }
- render() {
- const delta = this.clock.getElapsedTime();
- this.resetLookAt()
- this.startMY()
- this.renderer?.render(this.scene as THREE.Object3D, this.camera as THREE.Camera);
- this.css3dRender?.render(this.scene as THREE.Scene, this.camera as THREE.PerspectiveCamera)
- this.animationMixer?.update(delta);
- }
- /* 实时物体刷新朝向 */
- resetLookAt() {
- }
- /* 漫游 */
- startMY() {}
- animate() {
- // this.renderer?.setAnimationLoop(this.render.bind(this));
- if(this.animationId != -1) {
- this.render()
- this.animationId = requestAnimationFrame(this.animate.bind(this))
- }
- }
- resizeRenderer() {
- // 更新相机比例
- (this.camera as THREE.PerspectiveCamera).aspect = window.innerWidth / window.innerHeight;
- // 刷新相机矩阵
- this.camera?.updateProjectionMatrix();
- // 设置场景尺寸
- this.renderer?.setSize(window.innerWidth, window.innerHeight);
- this.css3dRender?.setSize(window.innerWidth, window.innerHeight);
- }
- wheelRenderer(e: WheelEvent) {
- const timeScale = e.deltaY > 0 ? 1 : -1;
- this.animationAction?.setEffectiveTimeScale(timeScale);
- (this.animationAction as THREE.AnimationAction).paused = false;
- this.animationAction?.play();
- if (this.timeoutId) {
- clearTimeout(this.timeoutId);
- }
- this.timeoutId = setTimeout(() => {
- this.animationAction?.halt(0.5);
- }, 500);
- }
- deleteModal() {
- try {
-
- this.resourceTracker && this.resourceTracker.dispose()
- this.renderer?.dispose()
- this.renderer?.forceContextLoss()
- // this.renderer?.domElement = null
- this.renderer = null
- // this.css3dRender?.domElement = null
- this.css3dRender = null;
- cancelAnimationFrame(this.animationId);
- const gl = this.renderer?.domElement.getContext("webgl");
- gl && gl?.getExtension("WEBGL_lose_context")?.loseContext();
- const gl1 = this.css3dRender?.domElement.getContext("webgl");
- gl1 && gl1?.getExtension("WEBGL_lose_context")?.loseContext();
- this.animationId = -1
- this.resourceTracker?.untrack(this.resourceTracker.resources)
- // this.scene = null
- console.log('销毁场景',this.scene);
-
- } catch (error) {
- console.log(error);
- }
- // this.scene?.children.forEach((obj:any) => {
- // if(obj.type === 'Group'){
- // obj.traverse(function(item:any) {
- // if (item.type === 'Mesh') {
- // item.geometry.dispose();
- // item.material.dispose();
- // !!item.clear&&item.clear();
- // }
- // })
- // }else if (obj.material) {
- // obj.material.dispose();
- // }else if (obj.geometry) {
- // obj.geometry.dispose();
- // }
- // this.scene?.remove(obj)
- // !!obj.clear??obj.clear()
- // obj = null;
- // })
- // let gl = this.renderer?.domElement.getContext("webgl");
- // gl && gl?.getExtension("WEBGL_lose_context")?.loseContext();
- // this.renderer?.forceContextLoss();
- // this.renderer?.dispose();
- // this.camera = null;
- // this.orbitControls = null;
- // this.renderer.domElement = null;
- // this.renderer = null;
- // css3dRender.domElement = null;
- // css3dRender = null;
- // model3D.innerHTML = '';
- // css3D.innerHTML = '';
- // model3D = null
- // css3D = null
- // this.stats = null
- // scene = null
- THREE.Cache.clear();
- console.log('3D环境已清理干净');
- }
- /* 创建字体精灵 */
- addTextSprite(message, parameters) {
- if (parameters === undefined) parameters = {};
- var fontface = parameters.hasOwnProperty("fontface") ?
- parameters["fontface"] : "Arial";
- /* 字体大小 */
- var fontsize = parameters.hasOwnProperty("fontsize") ?
- parameters["fontsize"] : 18;
- /* 边框厚度 */
- var borderThickness = parameters.hasOwnProperty("borderThickness") ?
- parameters["borderThickness"] : 4;
- /* 边框颜色 */
- var borderColor = parameters.hasOwnProperty("borderColor") ?
- parameters["borderColor"] : { r: 0, g: 0, b: 0, a: 1.0 };
- /* 背景颜色 */
- var backgroundColor = parameters.hasOwnProperty("backgroundColor") ?
- parameters["backgroundColor"] : { r: 255, g: 255, b: 255, a: 1.0 };
- /* 创建画布 */
- var canvas = document.createElement('canvas');
- var context = canvas.getContext('2d') as CanvasRenderingContext2D;
- // canvas.width = 0
- // canvas.height = 200
- /* 字体加粗 */
- context.font = "Bold " + fontsize + "px " + fontface;
- /* 获取文字的大小数据,高度取决于文字的大小 */
- var metrics = context.measureText(message);
- var textWidth = metrics.width;
- /* 背景颜色 */
- context.fillStyle = "rgba(" + backgroundColor.r + "," + backgroundColor.g + ","
- + backgroundColor.b + "," + backgroundColor.a + ")";
- /* 边框的颜色 */
- context.strokeStyle = "rgba(" + borderColor.r + "," + borderColor.g + ","
- + borderColor.b + "," + borderColor.a + ")";
- context.lineWidth = borderThickness;
- /* 绘制圆角矩形 */
- this.roundRect(context, borderThickness / 2, borderThickness / 2, textWidth + borderThickness, fontsize * 1.4 + borderThickness, 6);
- // this.roundRect(context, borderThickness / 2, borderThickness / 2, 10000, 10000, 6);
- /* 字体颜色 */
- context.fillStyle = "rgba(0, 0, 0, 1.0)";
- context.fillText(message, borderThickness, fontsize + borderThickness);
- // context.fillRect(borderThickness / 2, borderThickness / 2, 10000, 10000)
- /* 画布内容用于纹理贴图 */
- var texture = new THREE.Texture(canvas);
- texture.needsUpdate = true;
- var spriteMaterial = new THREE.SpriteMaterial({ map: texture, transparent:false });
- var sprite = new THREE.Sprite(spriteMaterial);
- /* 缩放比例 */
- sprite.scale.set(10, 5, 1);
- return sprite;
- }
- /* 绘制圆角矩形 */
- roundRect(ctx, x, y, w, h, r) {
- ctx.beginPath();
- ctx.moveTo(x + r, y);
- ctx.lineTo(x + w - r, y);
- ctx.quadraticCurveTo(x + w, y, x + w, y + r);
- ctx.lineTo(x + w, y + h - r);
- ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h);
- ctx.lineTo(x + r, y + h);
- ctx.quadraticCurveTo(x, y + h, x, y + h - r);
- ctx.lineTo(x, y + r);
- ctx.quadraticCurveTo(x, y, x + r, y);
- ctx.closePath();
- ctx.fill();
- ctx.stroke();
- }
- }
- export default UseThree;
|