Three 之 three.js (webgl)CSS2DObject 添加文字按钮等并与OrbitController / html button 等交互冲突的简单使用说明整理
目录
处理 CSS2DRenderer 与 OrbitController 等轨道交互的冲突(导致失效)问题
一、简单介绍
Three js 开发的一些知识整理,方便后期遇到类似的问题,能够及时查阅使用。
本节介绍, three 开发网页 3D 场景, three.js (webgl)在 3D 场景中添加 文本按钮等的UI 方法,并且在添加中如何处理与OrbitController / html button 等交互冲突的处理,保证各自的功能正常使用,这里做简单使用说明整理,如果有不足之处,欢迎指出,或者你有更好的方法,欢迎留言。
二、实现原理
在 Three js 3D 添加 CSS2D 的方法
1、创建 const xx = document.createElement('xxx'),对应设置
2、在以上基础上 const xxCSS2dObject = new CSS2DObject( xx ) ,并添加到 scene 中
3、然后记住创建 const css2DLabelRenderer = new CSS2DRenderer(),并且添加到 document.body.appendChild( this.css2DLabelRenderer.domElement )
4、在 render() 渲染函数中进行渲染 css2DLabelRenderer.render( this.scene, this.camera );
处理 CSS2DRenderer 与 OrbitController 等轨道交互的冲突(导致失效)问题
1、如果 CSS2DRenderer 没有需要交互的使用,可以直接设置 pointerEvents = 'none' (css2DLabelRenderer.domElement.style.pointerEvents = 'none';)
2、如果 CSS2DRenderer 有需要交互的使用,可以把 OrbitControls 控制器 用于事件监听的HTML元素替换(可以换成 CSS2DRenderer 这个)
// new OrbitControls( this.camera, this.renderer.domElement );
new OrbitControls( this.camera, this.css2DLabelRenderer.domElement );
如果 CSS2DRenderer 与 html button 等有交互冲突
1、可以设置该元素的 zIndex 值 大些,试试
style = "z-index: 1000" 或者 btnDiv.style.zIndex = 1000;
三、注意事项
1、CSS2DRenderer 渲染需要实时更新,必要也需要做窗口大小的变换,对应的事件处理
CSS2DLabelRender() {
if (this.css2DLabelRenderer != null) {
this.css2DLabelRenderer.render( this.scene, this.camera );
}
},
CSS2DLabelOnWindowSize() {
if (this.css2DLabelRenderer != null) {
this.css2DLabelRenderer.setSize(window.innerWidth, window.innerHeight);
}
四、效果预览
五、案例实现代码
效果预览基于,以下博文的基础环境实现:
Vue 之 Vue Cli 创建 Three js 工程( 网页 3D )的简单整理(一些注意事项)_仙魁XAN的博客-CSDN博客
1、关键代码(App.vue)
<template>
<div id="container"></div>
<div id="menu" style="position: absolute;left:45%;text-align: center; z-index: 1000">
<button @click="TestClick">TestClick(Click)</button>
</div>
</template>
<script>
import * as THREE from 'three';
import Stats from 'three/examples/jsm/libs/stats.module.js';
import { CSS2DRenderer, CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer.js';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
export default {
name: 'ThreeTest',
components: {},
data() {
return {}
},
mounted() {
this.container;
this.stats;
this.camera;
this.scene;
this.renderer;
this.css2DLabelRenderer;
this.init();
this.animate();
},
methods: {
init() {
this.container = document.createElement('div');
document.body.appendChild(this.container);
this.addSceneCameraAndLight();
this.addObject();
this.initRenderer();
this.addStatus();
this.AddSceneTextCSS2DLabel();
this.initOrbitController();
// window.addEventListener
window.addEventListener('resize', this.onWindowResize);
},
addSceneCameraAndLight(){
this.camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 10000);
this.camera.position.set(0,20,20);
this.scene = new THREE.Scene();
this.scene.background = new THREE.Color(0xf0f0f0);
const light = new THREE.DirectionalLight(0xffffff, 1);
this.scene.add(this.camera);
this.scene.add(light);
},
initRenderer(){
this.renderer = new THREE.WebGLRenderer();
this.renderer.setPixelRatio(window.devicePixelRatio);
this.renderer.setSize(window.innerWidth, window.innerHeight);
this.container.appendChild(this.renderer.domElement);
},
addStatus(){
this.stats = new Stats();
this.container.appendChild(this.stats.dom);
},
// 添加测试物体,并设置他们的层为 0 1 2
addObject(){
const mesh = new THREE.Mesh(new THREE.BoxGeometry(10,10,10),
new THREE.MeshLambertMaterial({color:'#f00'}))
this.scene.add( mesh);
},
initOrbitController(){
// new OrbitControls( this.camera, this.renderer.domElement );
new OrbitControls( this.camera, this.css2DLabelRenderer.domElement );
},
onWindowResize() {
this.camera.aspect = window.innerWidth / window.innerHeight;
this.camera.updateProjectionMatrix();
this.renderer.setSize(window.innerWidth, window.innerHeight);
this.CSS2DLabelOnWindowSize();
},
animate() {
requestAnimationFrame(this.animate);
this.render();
this.stats.update();
},
render() {
this.renderer.render(this.scene, this.camera);
this.CSS2DLabelRender();
},
AddSceneTextCSS2DLabel( ) {
this.css2DLabelRenderer = new CSS2DRenderer();
this.css2DLabelRenderer.setSize( window.innerWidth, window.innerHeight );
this.css2DLabelRenderer.domElement.style.position = 'absolute';
this.css2DLabelRenderer.domElement.style.top = '0px';//信息弹窗界面高度一半
this.css2DLabelRenderer.domElement.style.left = '0px';//信息弹窗界面宽度一半
//this.css2DLabelRenderer.domElement.style.pointerEvents = 'none';
document.body.appendChild( this.css2DLabelRenderer.domElement );
const labelDiv = document.createElement( 'div' );
labelDiv.className = 'label';
labelDiv.identifierName = 'label';
labelDiv.textContent = 'CSS2D Label: Test ...';
labelDiv.style.marginTop = '-1em';
const css2dLabel = new CSS2DObject( labelDiv );
css2dLabel.position.set( 3, 3, 0 );
labelDiv.style.pointerEvents = 'none';//避免HTML标签遮挡三维场景的鼠标事件
this.scene.add( css2dLabel );
const btnDiv = document.createElement( 'button' );
btnDiv.className = 'btn';
btnDiv.identifierName = 'btn';
btnDiv.textContent = 'Btn: Click ...';
btnDiv.style.marginTop = '-1em';
btnDiv.style.zIndex = 1000;
const css2dBtn = new CSS2DObject( btnDiv );
css2dLabel.position.set( 3, 0, 0 );
this.scene.add( css2dBtn );
},
CSS2DLabelRender() {
if (this.css2DLabelRenderer != null) {
this.css2DLabelRenderer.render( this.scene, this.camera );
}
},
CSS2DLabelOnWindowSize() {
if (this.css2DLabelRenderer != null) {
this.css2DLabelRenderer.setSize(window.innerWidth, window.innerHeight);
}
},
TestClick() {
console.log('Click...');
}
},
// beforeDestroy 废弃,使用 beforeUnmount
beforeUnmount() {
this.container = null;
this.stats = null;
this.camera = null;
this.scene = null;
this.renderer = null;
this.theta = null;
this.radius = null;
console.log("beforeUnmount");
}
}
</script>
<style>
#app {
text-align: center;
height: 100%;
}
* {
/*初始化样式*/
margin: 0;
padding: 0;
}
html {
/*用于 获取 屏幕的可视宽高*/
width: 100%;
height: 100%;
overflow: hidden;
}
</style>