如下面,有两个渲染器:
-
CSS3DRenderer:用于渲染CSS3D元素 -
WebGLRenderer:用于渲染主要场景
import * as THREE from 'three'
import { loadGLTFModel, loadHDRTexture } from './helpers/loaders';
import { CSS3DObject, CSS3DRenderer, OrbitControls, } from 'three/examples/jsm/Addons.js';
import { MouseEventManager } from './events/mouseEventManager';
import { createLines } from './init/lines';
import { useConnectStore } from '@/store/collect';
import touchScreenComponent from '@/components/touchScreen.vue';
import { createApp } from 'vue';
export class SceneManager {
container: HTMLElement; // 容器
scene: THREE.Scene; // 场景
camera: THREE.PerspectiveCamera; // 相机
renderer: THREE.WebGLRenderer; // 渲染器
control: OrbitControls; // 相机控制
loadingManager: THREE.LoadingManager; // 加载管理器
mouseEventManager: MouseEventManager | null = null; // 鼠标事件管理器
css3DRenderer: CSS3DRenderer; // CSS3D渲染器
constructor(container: HTMLElement, state: { loading: boolean; progress: number }) {
// ......
this.renderer = new THREE.WebGLRenderer({
antialias: true, // 抗锯齿
});
this.css3DRenderer = new CSS3DRenderer();
this.control = new OrbitControls(this.camera, this.css3DRenderer.domElement);
// 初始化加载管理器
// ......
}
async init() {
this.renderer.setSize(window.innerWidth, window.innerHeight); // 设置渲染器尺寸
this.container.appendChild(this.renderer.domElement); // 将渲染器添加到容器中
this.css3DRenderer.setSize(window.innerWidth, window.innerHeight); // 设置 CSS3D 渲染器尺寸
this.css3DRenderer.domElement.style.position = 'absolute'; // 设置 CSS3D 渲染器的定位方式为绝对定位
this.css3DRenderer.domElement.style.top = '0'; // 设置 CSS3D 渲染器的顶部为 0
this.container.appendChild(this.css3DRenderer.domElement); // 将 CSS3D 渲染器添加到容器中
// 设置相机位置
this.camera.position.set(0, 0, 5);
try {
// 加载模型
const model = await loadGLTFModel('/models/test.glb', this.loadingManager);
model && this.scene.add(model.scene);
// 获取名称为 touchScreen 的模型
const touchScreen = model.scene.getObjectByName('touchScreen') as THREE.Mesh;
const div = document.createElement('div');
document.body.appendChild(div);
const app = createApp(touchScreenComponent);
app.mount(div);
// 将 div 转换为 CSS3DObject 对象
const css3DObject = new CSS3DObject(div);
// 设置 CSS3DObject 对象的位置
css3DObject.position.set(0, 0, 0.03);
css3DObject.scale.set(0.01, 0.01, 0.01);
css3DObject.visible = true;
// 将 CSS3DObject 对象添加到模型中
touchScreen.add(css3DObject);
// ......
} catch (error) {
console.error(error);
}
this.animate();
}
animate() {
this.renderer.render(this.scene, this.camera);
this.css3DRenderer.render(this.scene, this.camera);
requestAnimationFrame(this.animate.bind(this));
}
dispose() {
this.renderer.dispose();
this.control.dispose();
}
}
<template>
<div class="w-[65px] h-[45px] bg-black text-white flex justify-center items-center text-[5px]">
<button class="w-[20px] h-[15px] bg-red-500" @click="test">点击</button>
</div>
</template>
<script setup lang="ts">
const test = () => {
console.log('test')
}
</script>
<style scoped></style>
问题描述:
通过 CSS3DObject 将 TouchScreen.vue 转换成 3D模型 并挂载到场景上后,TouchScreen.vue 的点击事件失效了
问题解析和解决:
有两个原因:
-
CSS3DRenderer 的层级比WebGLRenderer 高,也就是说点击事件都被CSS3DRenderer 捕获了this.renderer.setSize(window.innerWidth, window.innerHeight); // 设置渲染器尺寸 this.container.appendChild(this.renderer.domElement); // 将渲染器添加到容器中 this.css3DRenderer.setSize(window.innerWidth, window.innerHeight); // 设置 CSS3D 渲染器尺寸 this.css3DRenderer.domElement.style.position = 'absolute'; // 设置 CSS3D 渲染器的定位方式为绝对定位 this.css3DRenderer.domElement.style.top = '0'; // 设置 CSS3D 渲染器的顶部为 0 this.container.appendChild(this.css3DRenderer.domElement); // 将 CSS3D 渲染器添加到容器中所以我们需要通过
pointer-events:none 设置 CSS3D 渲染器的鼠标事件为不可用this.renderer.setSize(window.innerWidth, window.innerHeight); // 设置渲染器尺寸 this.container.appendChild(this.renderer.domElement); // 将渲染器添加到容器中 this.css3DRenderer.setSize(window.innerWidth, window.innerHeight); // 设置 CSS3D 渲染器尺寸 this.css3DRenderer.domElement.style.position = 'absolute'; // 设置 CSS3D 渲染器的定位方式为绝对定位 this.css3DRenderer.domElement.style.top = '0'; // 设置 CSS3D 渲染器的顶部为 0 this.css3DRenderer.domElement.style.pointerEvents = 'none'; // 设置 CSS3D 渲染器的鼠标事件为不可用 this.container.appendChild(this.css3DRenderer.domElement); // 将 CSS3D 渲染器添加到容器中 -
OrbitControls 的问题:其实和上面同源问题,
CSS3DObject 是叠加在WebGL 渲染的场景之上的,OrbitControls 默认会拦截所有鼠标事件当前 OrbitControls 绑定在了 CSS3DRenderer 的 domElement 上,导致点击事件被 OrbitControls 消费掉了
this.control = new OrbitControls(this.camera, this.css3DRenderer.domElement);需要改成
this.control = new OrbitControls(this.camera, this.renderer.domElement);