先看下效果,如果符合你的需求可以接着往下看,避免浪费大家的时间

这个需求主要有以下功能

1)给模型添加热点,可以通过点击热点弹出模型标签

2)给模型添加Sprite精灵图

3)给模型添加CSS3DSprite模型标签

在写代码之前先了解下 CSS3DSprite 是什么

CSS3精灵模型 CSS3DSprite 对应的HTML标签,可以跟着场景缩放,位置可以跟着场景旋转,但是自身的姿态角度始终平行于canvas画布,不受旋转影响,就像精灵模型一样。CSS3精灵模型 CSS3DSprite尺寸、位置、缩放等渲染规律和CSS3对象模型CSS3DObject基本一致。

一、给模型添加热点

创建 hotspot.js 文件 专门用于创建热点模型

import * as THREE from "three";

import * as TWEEN from "@tweenjs/tween.js";

import { ResourceTracker } from "@/views/modelShow/trackResource.js";

// 在外层定义resMgr和track

let resMgr = new ResourceTracker();

const track = resMgr.track.bind(resMgr);

function createHotPot(scene, hotPosition, hotName) {

const map = new THREE.TextureLoader().load("/textues/point2.png");

const material = new THREE.SpriteMaterial({

map: map,

// color: 0xccffcc, //设置精灵矩形区域颜色

});

const sprite = new THREE.Sprite(material);

sprite.scale.set(2.5, 2.5, 2.5); //0.3,

sprite.position.set(hotPosition.x, hotPosition.y, hotPosition.z);

sprite.name = hotName; //精灵图名称

scene.add(track(sprite));

const pos = {

scale: 2.5, //0.3

};

const spriteTween = new TWEEN.Tween(pos)

.to(

{

scale: 1, //0.2

},

1500

)

.easing(TWEEN.Easing.Quadratic.Out) // 缓动函数

.onUpdate(function () {

sprite.scale.set(pos.scale, pos.scale, pos.scale);

});

spriteTween.yoyo(false); // 是否循环反转,默认值为false,表示不反转

spriteTween.repeat(Infinity); // 重复次数,默认值为0,Infinity表示无限循环

spriteTween.start();

return sprite;

}

export { createHotPot };

二、创建 Sprite 模型精灵

import * as THREE from "three";

import * as TWEEN from "@tweenjs/tween.js";

import {

ResourceTracker

} from "@/views/modelShow/trackResource.js";

// 在外层定义resMgr和track

let resMgr = new ResourceTracker();

const track = resMgr.track.bind(resMgr);

function createSprite(obj, imgPath, spritePosition, spriteName) {

//obj: 添加精灵图的对象, imgPath:精灵图地址 spritePosition:精灵图位置

const texLoader = new THREE.TextureLoader();

let texture = null;

texture = texLoader.load(imgPath);

// 创建精灵材质对象

const spriteMaterial = new THREE.SpriteMaterial({

// color: 0xccffcc, //设置精灵矩形区域颜色

// rotation: Math.PI/4, //旋转精灵对象45度,弧度值

map: texture, //设置精灵纹理贴图

transparent: true,

});

// 精灵图淡入动画

const pos = {

opacity: 0.0, //完全透明

};

new TWEEN.Tween(pos)

.to({ opacity: 1.0 }, 500)

.onUpdate(function (obj) {

spriteMaterial.opacity = obj.opacity;

})

.onComplete(function () {

// 动画结束:关闭允许透明,恢复到模型原来状态

// spriteMaterial.transparent = false;

})

.easing(TWEEN.Easing.Quadratic.InOut)

.start();

// 创建精灵模型对象

const sprite = new THREE.Sprite(spriteMaterial);

// 控制精灵大小

sprite.scale.set(2, 2, 2); //只需要设置x、y两个分量就可以 1

sprite.position.set(spritePosition.x, spritePosition.y, spritePosition.z); //精灵图位置设置

sprite.name = spriteName; //精灵图名称

obj.add(track(sprite)); //精灵图会标注在空对象obj对应的位置

return sprite;

}

export { createSprite };

三、创建CSS3DRenderer渲染器

import { CSS3DRenderer } from "three/examples/jsm/renderers/CSS3DRenderer.js";

// 创建一个css3d渲染器

var labelRenderer3D = new CSS3DRenderer();

labelRenderer3D.setSize(window.innerWidth, window.innerHeight);

labelRenderer3D.domElement.style.position = "absolute";

// 相对标签原位置位置偏移大小

labelRenderer3D.domElement.style.top = "0px";

labelRenderer3D.domElement.style.left = "0px";

// //设置.pointerEvents=none,以免模型标签HTML元素遮挡鼠标选择场景模型

labelRenderer3D.domElement.style.pointerEvents = "none";

document.body.appendChild(labelRenderer3D.domElement);

export { labelRenderer3D };

四、使用

在需要的vue文件引入

添加CSS3DSprite精灵模型

addMarker(sName, x, y, z) {

let tipsData = [{

text: '项目',

tipsVal: '数值',

unit: '单位'

}];

if (sName === '模型1标签') {

tipsData = tipsData.concat(this.model1Data);

} else if (sName === '模型2标签') {

tipsData = tipsData.concat(this.model2Data);

} else if (sName === '模型3标签') {

tipsData = tipsData.concat(this.model3Data);

} else if (sName === '模型4标签') {

tipsData = tipsData.concat(this.model4Data);

} else {

tipsData = tipsData.concat(this.model1Data);

}

let markerDom = document.createElement('div');

markerDom.style.width = '300px';

markerDom.style.padding = "15px 30px";

markerDom.style.color = "#fff";

markerDom.style.fontSize = "22px";

markerDom.style.background = `rgba(25,25,25,0.7) url(${labelBg})no-repeat center center`;

markerDom.style.borderRadius = "10px";

markerDom.style.backgroundSize = "100% 100%";

markerDom.innerHTML = `

${tipsData.map(item=>{

return `

${item.text}

${item.tipsVal}

${item.unit}

`

}).join('')}

`;

markerDom.style.color = '#ffffff';

const marker = new CSS3DSprite(markerDom);

marker.name = sName;

marker.scale.set(0.08, 0.08, 0.08); //根据相机渲染范围控制HTML 3D标签尺寸

marker.rotateY(Math.PI / 2); //控制HTML标签CSS3对象姿态角度

marker.position.set(x, y, z);

this.scene.add(track(marker));

new TWEEN.Tween({

opacity: 0

}).to({

opacity: 1.0

}, 500).onUpdate(function (obj) {

//动态更新div元素透明度

markerDom.style.opacity = obj.opacity;

}).start();

return markerDom;

}

五、注意事项

在实际项目开发中,不可避免会从一个模型页面切换到其他页面,这样来回反复切换,会非常消耗系统性能,甚至导致模型不能正常加载渲染。我也遇到了这个问题,于是查阅了些资料,总算是解决了~

beforeDestroy() {

// 清除所有点击事件

document.body.removeEventListener("click", this.handleClickPage);

try {

this.$refs.modelRef.innerHTML = ''; // 3d容器,下面挂载着 canvas 容器,这里直接清空3d容器子元素

this.scene.clear();

resMgr && resMgr.dispose()

this.renderer.dispose();

this.renderer.forceContextLoss();

this.renderer.content = null;

labelRenderer3D.content = null; // 清除3d渲染内容

cancelAnimationFrame(this.aniFlag) // 去除animationFrame

let gl = this.renderer.domElement.getContext("webgl");

gl && gl.getExtension("WEBGL_lose_context").loseContext();

// console.log(this.renderer.info) // 模型内存占用清空,查看memery字段即可

// console.log(this.scene) // 查看scene ,children为空数组,表示 模型等销毁ok

} catch (e) {

console.log(e)

}

},

这种方法不能彻底清除掉scene场景内的一些geometry、texture等,而且页面离开也不会自动释放内存。重复切换页面,可以看到CPU占用越来越高。 

import * as THREE from 'three/build/three.module'

export class ResourceTracker {

constructor() {

this.resources = new Set();

}

track(resource) {

if (!resource) {

return resource;

}

// handle children and when material is an array of materials or

// uniform is array of textures

if (Array.isArray(resource)) {

resource.forEach(resource => this.track(resource));

return resource;

}

if (resource.dispose || resource instanceof THREE.Object3D) {

this.resources.add(resource);

}

if (resource instanceof THREE.Object3D) {

this.track(resource.geometry);

this.track(resource.material);

this.track(resource.children);

} else if (resource instanceof THREE.Material) {

// We have to check if there are any textures on the material

for (const value of Object.values(resource)) {

if (value instanceof THREE.Texture) {

this.track(value);

}

}

// We also have to check if any uniforms reference textures or arrays of textures

if (resource.uniforms) {

for (const value of Object.values(resource.uniforms)) {

if (value) {

const uniformValue = value.value;

if (uniformValue instanceof THREE.Texture ||

Array.isArray(uniformValue)) {

this.track(uniformValue);

}

}

}

}

}

return resource;

}

untrack(resource) {

this.resources.delete(resource);

}

dispose() {

for (const resource of this.resources) {

if (resource instanceof THREE.Object3D) {

if (resource.parent) {

resource.parent.remove(resource);

}

}

if (resource.dispose) {

resource.dispose();

}

}

this.resources.clear();

}

}

 参考文章:

https://www.cnblogs.com/Hijacku/p/15927784.html

Three.js 内存释放问题_forcecontextloss-CSDN博客

这世界很喧嚣,做你自己就好

推荐阅读

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: