当前位置: 首页 > news >正文

【ThreeJs】【HTML载入】Three.js 中的 CSS2DRenderer 与 CSS3DRenderer 全面解析

🎯 Three.js 中的 CSS2DRenderer 与 CSS3DRenderer 全面解析

在 Three.js 项目中,很多时候我们需要把 HTML 元素放进 3D 世界里:
比如信息标签、操作按钮、甚至一个完整的弹窗界面。

Three.js 官方提供了两种方案:

  • CSS2DRenderer
  • CSS3DRenderer

本文将从 区别、使用规则、示例写法、事件交互 四方面来讲解。


1️⃣ 基本区别

对比项CSS2DRendererCSS3DRenderer
渲染方式元素始终面向相机,不受 3D 透视影响元素作为真正的 3D 物体,可旋转、缩放、透视
性能更轻量相对更重,适合复杂 UI
常见用途标签、提示文字、简单弹窗网页窗口、卡片、复杂交互界面
交互特点像 HUD 一样叠加在场景里完全沉浸式,随场景旋转变化
CSS 支持常规 CSS,不能有 3D transform完整 CSS,包括 3D transform、动画

👉 简单一句话:

  • CSS2DRenderer = 信息标签(HUD)
  • CSS3DRenderer = 真实网页在 3D 世界中

2️⃣ 使用规则(怎么选)

  • 规则 1:需要固定朝向相机 → 用 CSS2DRenderer
    例:人物名字、坐标标签、提示文字

  • 规则 2:需要 3D 效果或网页窗口 → 用 CSS3DRenderer
    例:可旋转的网页卡片、嵌入式表单、3D 弹窗

  • 规则 3:性能优先时 → 先考虑 CSS2DRenderer
    例:大量标签(几百个),建议 CSS2DRenderer

  • 规则 4:交互复杂、样式多 → 用 CSS3DRenderer
    例:场景中的操作面板、功能窗口


3️⃣ 示例写法对比

✅ CSS2DRenderer 示例(标签)

// 创建 DOM
const labelDiv = document.createElement('div');
labelDiv.innerHTML = `<div class="bg-black/70 text-white px-3 py-1 rounded-md text-xs">📦 Cube 标签</div>
`;// 转为 CSS2DObject
const label = new THREE.CSS2DObject(labelDiv);
label.position.set(0, 2, 0); // 立方体上方
cube.add(label);

👉 适合:始终朝向相机的 标签 / 标注


✅ CSS3DRenderer 示例(弹窗)

// 创建 DOM
const popupDiv = document.createElement('div');
popupDiv.innerHTML = `<div class="bg-white shadow-lg rounded-xl w-64 p-4 text-gray-800"><h2 class="font-bold text-lg mb-2">立方体信息</h2><p>这是一个 3D 弹窗</p><button class="mt-3 bg-blue-500 text-white px-3 py-1 rounded">点我</button></div>
`;// 转为 CSS3DObject
const popup = new THREE.CSS3DObject(popupDiv);
popup.position.set(3, 0, 0); // 放在场景一侧
scene.add(popup);

👉 适合:带按钮、复杂 UI 的 网页弹窗


4️⃣ 事件绑定与传参

方式一:直接绑定事件

popupDiv.querySelector('button').addEventListener('click', () => {alert('按钮点击了');
});

方式二:使用自定义事件传参(推荐 🚀)

popupDiv.querySelector('button').addEventListener('click', () => {const event = new CustomEvent('popupAction', { detail: { id: cube.uuid } });window.dispatchEvent(event);
});window.addEventListener('popupAction', e => {console.log('传来的对象ID:', e.detail.id);
});

👉 优点:

  • 结构与逻辑解耦
  • 可携带参数(对象 ID、数据等)

5️⃣ 最佳实践总结

用 CSS2DRenderer

  • 标签、HUD、简单提示
  • 性能优先,大量元素
    在这里插入图片描述
<!DOCTYPE html>
<html>
<head><title>GSAP + Three.js + 弹窗 + 控制器 示例</title><style>body { margin: 0; }canvas { display: block; }/* 弹窗样式 */.popup {background: rgba(0, 0, 0, 0.7);color: white;padding: 12px 16px;border-radius: 8px;font-family: sans-serif;text-align: center;width: 160px;pointer-events: auto; /* 允许弹窗接收事件 */}.popup button {margin-top: 8px;padding: 4px 8px;border: none;border-radius: 4px;cursor: pointer;pointer-events: auto; /* 确保按钮可以接收事件 */}</style><!-- Three.js --><script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r134/three.min.js"></script><!-- GSAP --><script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.11.4/gsap.min.js"></script><!-- Three.js 控制器 --><script src="https://cdn.jsdelivr.net/npm/three@0.134.0/examples/js/controls/OrbitControls.js"></script><!-- Three.js CSS2DRenderer --><script src="https://cdn.jsdelivr.net/npm/three@0.134.0/examples/js/renderers/CSS2DRenderer.js"></script>
</head>
<body><script>// 1. Three.js 基础const scene = new THREE.Scene();const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);const renderer = new THREE.WebGLRenderer();renderer.setSize(window.innerWidth, window.innerHeight);document.body.appendChild(renderer.domElement);// 2. 添加控制器const controls = new THREE.OrbitControls(camera, renderer.domElement);// 控制器配置controls.enableDamping = true;controls.dampingFactor = 0.05;controls.enableZoom = true;controls.zoomSpeed = 0.5;controls.enablePan = true;controls.panSpeed = 0.5;controls.maxDistance = 50;controls.minDistance = 2;// 3. 立方体const geometry = new THREE.BoxGeometry(2, 2, 2);const material = new THREE.MeshBasicMaterial({ color: 0x00ff00,wireframe: true });const cube = new THREE.Mesh(geometry, material);scene.add(cube);camera.position.z = 10;// 4. GSAP 动画const tl = gsap.timeline({repeat: -1,yoyo: true});tl.to(cube.position, {x: 5,z: 3,duration: 2,ease: "power2.inOut"}, 0).to(cube.rotation, {x: Math.PI * 2,y: Math.PI * 2,duration: 4,ease: "linear"}, 0).to(cube.scale, {x: 2,y: 2,z: 2,duration: 1.5,ease: "elastic.out(1, 0.3)"});// 5. CSS2DRenderer 用于渲染 HTML 弹窗const labelRenderer = new THREE.CSS2DRenderer();labelRenderer.setSize(window.innerWidth, window.innerHeight);labelRenderer.domElement.style.position = 'absolute';labelRenderer.domElement.style.top = '0px';labelRenderer.domElement.style.pointerEvents = 'none'; // 基础层不接收事件document.body.appendChild(labelRenderer.domElement);// 6. 创建 HTML 弹窗并绑定事件const popupDiv = document.createElement('div');popupDiv.className = 'popup';popupDiv.innerHTML = `<h3>立方体信息</h3><p>这是一个 3D 立方体</p><button id="popupBtn">点我</button>`;// 关键修复:使用JavaScript方式绑定事件,而不是onclick属性const popupBtn = popupDiv.querySelector('#popupBtn');popupBtn.addEventListener('click', () => {// 添加一个立方体缩放效果作为反馈gsap.to(cube.scale, {x: cube.scale.x * 1.2,y: cube.scale.y * 1.2,z: cube.scale.z * 1.2,duration: 0.3,yoyo: true,repeat: 1});alert('按钮点击成功!');});const popupLabel = new THREE.CSS2DObject(popupDiv);popupLabel.position.set(0, 3, 0); // 在立方体上方cube.add(popupLabel); // 弹窗跟随立方体移动// 7. 窗口自适应window.addEventListener('resize', () => {camera.aspect = window.innerWidth / window.innerHeight;camera.updateProjectionMatrix();renderer.setSize(window.innerWidth, window.innerHeight);labelRenderer.setSize(window.innerWidth, window.innerHeight);});// 8. 渲染循环function animate() {requestAnimationFrame(animate);controls.update();renderer.render(scene, camera);labelRenderer.render(scene, camera);}animate();</script>
</body>
</html>

用 CSS3DRenderer

  • 复杂 UI(弹窗、卡片、网页模块)
  • 支持 3D 旋转、缩放、动画

示例

在这里插入图片描述

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>Three.js + CSS3DRenderer + OrbitControls</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r134/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.134.0/examples/js/renderers/CSS3DRenderer.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.134.0/examples/js/controls/OrbitControls.js"></script>
<script src="https://cdn.tailwindcss.com"></script>
<style>body { margin: 0; overflow: hidden; }canvas { display: block; }/* 关键:确保CSS3D元素可以接收事件 */#css-container {pointer-events: auto;}.css-3d-object {transform-style: preserve-3d;pointer-events: auto;}
</style>
</head>
<body>
<!-- 创建一个专门的容器放置CSS3D内容 -->
<div id="css-container"></div><script>// 1️⃣ 场景、相机、渲染器const scene = new THREE.Scene();const camera = new THREE.PerspectiveCamera(60, window.innerWidth/window.innerHeight, 0.1, 1000);camera.position.set(0, 2, 6);// WebGL渲染器(用于3D物体)const renderer = new THREE.WebGLRenderer({ antialias: true });renderer.setSize(window.innerWidth, window.innerHeight);document.body.appendChild(renderer.domElement);// CSS3D渲染器(用于HTML元素)const cssRenderer = new THREE.CSS3DRenderer();cssRenderer.setSize(window.innerWidth, window.innerHeight);cssRenderer.domElement.style.position = 'absolute';cssRenderer.domElement.style.top = '0';cssRenderer.domElement.style.pointerEvents = 'none'; // 让WebGL能接收事件document.getElementById('css-container').appendChild(cssRenderer.domElement);// 2️⃣ OrbitControls - 使用WebGL渲染器的DOM元素作为事件目标const controls = new THREE.OrbitControls(camera, renderer.domElement);controls.enableDamping = true;controls.dampingFactor = 0.05;controls.minDistance = 3;controls.maxDistance = 20;// 3️⃣ 立方体const cube = new THREE.Mesh(new THREE.BoxGeometry(2, 2, 2),new THREE.MeshNormalMaterial());scene.add(cube);// 4️⃣ CSS3D 弹窗 - 创建并配置const createPopup = () => {const div = document.createElement('div');div.className = 'css-3d-object bg-white shadow-xl rounded-xl w-64 p-4 text-gray-800';div.innerHTML = `<h2 class="text-lg font-bold mb-2">📦 立方体信息</h2><p class="text-sm mb-3">可以旋转、缩放场景。</p><button id="scaleBtn" class="bg-blue-500 text-white px-3 py-1 rounded hover:bg-blue-600">放大立方体</button>`;// 绑定点击事件 - 直接在这里绑定div.querySelector('#scaleBtn').addEventListener('click', () => {cube.scale.multiplyScalar(1.2);console.log('立方体已放大,当前大小:', cube.scale.x);});const popup = new THREE.CSS3DObject(div);popup.position.set(3, 1, 0);popup.scale.set(0.01, 0.01, 0.01);return popup;};// 添加弹窗到场景const popup = createPopup();scene.add(popup);// 5️⃣ 窗口大小调整window.addEventListener('resize', () => {camera.aspect = window.innerWidth / window.innerHeight;camera.updateProjectionMatrix();renderer.setSize(window.innerWidth, window.innerHeight);cssRenderer.setSize(window.innerWidth, window.innerHeight);});// 6️⃣ 渲染循环function animate() {requestAnimationFrame(animate);cube.rotation.y += 0.01;controls.update();renderer.render(scene, camera);cssRenderer.render(scene, camera);}animate();
</script>
</body>
</html>
  • 推荐写法

    • 模板字符串 + TailwindCSS,开发快、样式优雅
    • 事件传参用 CustomEvent

一句话结论

轻量信息展示用 CSS2DRenderer,复杂网页交互用 CSS3DRenderer;写法推荐 模板字符串 + TailwindCSS,事件传参推荐 CustomEvent


http://www.dtcms.com/a/402945.html

相关文章:

  • (50)elasticsearch服务部署-有状态负载(华为云平台CCE)
  • 【时序预测-5】FFT、STL、ARIMA频域特征和时序分解
  • Kotlinx Serialization 指南
  • SpringBoot @Scheduled 注解详解
  • layui 表格行级 upload 上传操作
  • 【Unity 入门教程】三、如何设置自定义字体(解决中文乱码问题)
  • STM32开发(FreeRTOS实时操作系统)
  • RocketMQ-生产常见问题汇总
  • 成都网站托管外包施工企业科技宣传片
  • 小厂 Java 面试,难度怎么样?
  • Webpack5 第一节
  • 【深入理解JVM】常见的垃圾回收器
  • 东莞企业建设网站官网有限公司百度一下百度网页版主页
  • 【大模型:知识图谱】--7.Neo4j数据库的导入和导出
  • 数据结构与算法(栈)
  • Coze源码分析-资源库-创建数据库-后端源码-基础设施/数据存储层
  • PySpark 安装教程及 WordCount 实战与任务提交
  • 制作网站的公司八大建筑央企排名
  • zynq纯PL读取XADC
  • 【FastMCP】中间件
  • bigo二面总结
  • 个人网站建设思路省级别网站建设方案
  • 测试自动化教程:Parasoft如何流重定向与单元测试自动化
  • 开源AI大模型、AI智能名片与S2B2C商城小程序在价值观型社群构建与运营中的价值与应用
  • 郑州 网站建设公司阿里企业邮箱收费标准一年多少钱
  • Day03:小程序的常用操作
  • 交互的脉络:小程序事件系统详解
  • 自助建站免费平台深圳建设管理中心网站首页
  • LVS虚拟调度器学习
  • 【LVS入门宝典】LVS-TUN模式原理与配置:跨越网络界限的负载均衡解决方案