Threejs进阶之五:使用CSS2DRenderer给模型添加HTML标签
这一节给场景中的模型添加标签,想实现的效果是,通过鼠标点击场景中摩托车的某个部位,则在场景中出现一个标签,并在标签上显示该部位的信息。最终的效果图如下:
要实现上面的效果,需要用到CSS 2D渲染器,先来了解下CSS 2D渲染器
CSS2DRenderer(CSS 2D渲染器)
CSS2DRenderer(CSS 2D渲染器)可以把HTML元素作为标签标注到三维场景中,CSS2DRenderer是CSS3DRenderer(CSS 3D渲染器)的简化版本,它唯一支持的变换是位移。通过CSS2DRenderer我们可以将三维物体和基于HTML的标签相结合,来更好的表达场景中物体的信息。
构造函数
CSS2DRenderer()
方法
.getSize () 返回一个包含有渲染器宽和高的对象。
.render ( scene : Scene, camera : Camera ) 使用camera渲染scene。
.setSize (width : Number, height : Number) 将渲染器的尺寸调整为(width, height).
使用CSS2DRenderer
CSS2DRenderer是threejs提供的扩展库,在threejs文件包目录examples/jsm/renderers/,文件夹下面可以找到CSS2DRenderer.js扩展库。
CSS2DRenderer.js提供了两个类CSS2DRenderer(CSS2D渲染器)和CSS2DObject(CSS2D模型对象)
要使用CSS2DRenderer,我们需要先将其引入
// 引入CSS2渲染器CSS2DRenderer和CSS2模型对象CSS2DObject
import {
CSS2DRenderer,CSS2DObject }
from 'three/examples/jsm/renderers/CSS2DRenderer'
创建HTML标签
在vue文件的模板中添加如下代码,都是基础的HTML语音,这里不再细说
div id="label">
div style="position: relative;
width: 139px;
height: 167px;
color: #ffffff;
">
img src="../assets/images/info.png" alt="" style="width: 100%;
position: absolute;
left: 0px;
top: 0px;
">
div style="position: absolute;
left: 48px;
top: 4px;
font-size: 12px;
">
div style="font-size: 14px;
font-weight: 400;
">
span id="txtName">
车身/span>
/div>
div style="margin-top: 8px;
margin-left: -25px;
">
span style="color: #fff;
font-weight: 300;
">
材质:/span>
span style="font-weight: 400;
margin-left: 10px;
" id="txtMaterial">
金属/span>
/div>
div style="margin-top: 8px;
margin-left: -25px;
">
span style="color: #fff;
font-weight: 300;
">
颜色:/span>
span style="font-weight: 400;
margin-left: 2px;
" id="txtColor">
红色/span>
/div>
/div>
/div>
/div>
添加上述代码后,我们创建的标签出现在了浏览器页面的左下方,这是典型的浏览器div的排列属性,但是这不是我们想要的,我们想要将这个div标签放到三维场景中去展示。这就涉及到了世界坐标xyz与屏幕坐标的转换,这是一个很麻烦的事情。好在threejs的扩展库CSS2DRenderer.js已经帮我们考虑到了这一点,给我们提供了CSS2DObject 对象
CSS2DObject 对象
通过CSS2DObject对象,可以把HTML元素转化为一个类似threejs网格模型的对象,即把CSS2DObject当成threejs的一个模型一样去设置位置.position或添加到场景中;
const div = document.querySelector('#label')
const tag = new CSS2DObject(div)
可以通过.position属性设置标签模型对象的xyz坐标,调整其在三维世界中的位置。
tag.position.set(0,2,0)
将HTML元素通过CSS2DObject转换后,我们就可以把HTML元素对应的CSS2模型对象添加到其它模型对象或三维场景中。和添加其他对象没什么区别 添加到场景
scene.add(tag)
添加到组中
const group = new THREE.Group()
group.add(tag)
添加到Mesh中
mesh.add(tag)
创建CSS2DRenderer
创建css2dRenderer对象
// 创建一个CSS2渲染器CSS2DRenderer
const css2dRenderer= new CSS2DRenderer()
CSS2渲染器CSS2DRenderer和WebGL渲染器WebGLRenderer相似,它们都有下面几个属性和方法,用法也类似,都有.domElement、.setSize()、.render()
.setSize设置大小
css2dRenderer.setSize (window.innerWidth,window.innerHeight)
设置位置
css2dRenderer.domElement.style.position = 'absolute'
css2dRenderer.domElement.style.top = '0px'
将CSS2Renderer.domElement添加到body中
document.body.appendChild(css2dRenderer.domElement)
渲染HTML标签
渲染HTML标签使用.render()方法,和WebGLRenderer的render方法一样,要写在rander()函数中
css2dRenderer.render(this.scene,this.camera)
理解了上面的知识点后,我们来编写代码
创建initCSS2DRenderer()初始化函数、调用及渲染
initCSS2DRenderer() {
const div = document.querySelector('#label')
// HTML元素转换为threejs的css2d模型对象
const tag = new CSS2DObject(div)
tag.position.set(0,1.3,0);
this.scene.add(tag)
css2dRenderer = new CSS2DRenderer()
css2dRenderer.setSize (window.innerWidth,window.innerHeight)
css2dRenderer.domElement.style.position = 'absolute'
css2dRenderer.domElement.style.top = '0px'
// css2dRenderer.domElement.style.zIndex = -1
document.body.appendChild(css2dRenderer.domElement)
}
在init()中调用initCSS2DRenderer()函数
// 初始化
init() {
// 初始化场景
this.initScene()
// 初始化灯光
this.initLight()
// 初始化Mesh
this.initMesh()
// 初始化地面
this.initFloor()
// 初始化GUI
this.initGUI()
this.initCSS2DRenderer()
// 初始化相机
this.initCamera()
// 初始化渲染器
this.initRender()
// 初始化轨道控制器
this.initControls()
window.addEventListener('resize',this.onWindowResize.bind(this))
}
在render()中渲染HTML标签
render() {
this.renderer.render(this.scene,this.camera)
css2dRenderer.render(this.scene,this.camera)
this.controls.update()
}
完成以后刷新浏览器,可以看到创建的标签已经添加到了三维场景中
解决HTML标签遮挡画布问题
在initCSS2DRenderer()函数中添加下面一句代码
css2dRenderer.domElement.style.pointerEvents = 'none'
现在我们刷新浏览器,转动鼠标就不受影响了
实现鼠标选中模型弹出标签效果
要实现鼠标点选模型弹出标签效果,我们需要用到之前将过的光线投射Raycaster,对Raycaster不了解的小伙伴可以看我前面写的文章Threejs入门之二十一:使用Raycaster实现物体与用户的交互里面有详细的介绍,这里就不细讲了。
修改initCSS2DRenderer()函数
我们将initCSS2DRenderer()函数中获取div及创建tag对象的代码删除,将其放到initListener()函数中
initCSS2DRenderer() {
// const div = document.querySelector('#label')
// // HTML元素转换为threejs的css2d模型对象
// const tag = new CSS2DObject(div)
// tag.position.set(0,1.3,0);
// this.scene.add(tag)
css2dRenderer = new CSS2DRenderer()
css2dRenderer.setSize (window.innerWidth,window.innerHeight)
css2dRenderer.domElement.style.position = 'absolute'
css2dRenderer.domElement.style.top = '0px'
css2dRenderer.domElement.style.pointerEvents = 'none'
document.body.appendChild(css2dRenderer.domElement)
}
创建initListener()函数
创建initListener()函数,在该函数里面实现鼠标点击事件的监听,并通过Raycaster与模型的焦点判断选中的物体
获取label标签并创建CSS2DObject对象
const that = this
const div = document.querySelector('#label')
const txtName = document.querySelector('#txtName')
const txtMaterial = document.querySelector('#txtMaterial')
const txtColor = document.querySelector('#txtColor')
const tag = new CSS2DObject(div)
创建鼠标点击的监听事件
this.renderer.domElement.addEventListener('click',function(event){
const px = event.offsetX;
const py = event.offsetY;
const x = (px / window.innerWidth) * 2 - 1;
const y = -(py / window.innerHeight) * 2 + 1;
const raycaster = new THREE.Raycaster();
raycaster.setFromCamera(new THREE.Vector2(x, y), that.camera);
const intersects = raycaster.intersectObjects(motorModel.children)
if(intersects.length >
0) {
intersects[0].object.add(tag)
selectObj = intersects[0].object
}
else {
if(selectObj) {
//把原来选中模型对应的标签和发光描边隐藏
selectObj.remove(tag) //从场景移除
}
}
}
)
获取选中模型的位置,并将其赋值给tag标签
通过.geometry.attributes.position
可以获取物体的位置信息,我们在选中物体的同时,获取该物体的位置信息,并赋值给tag标签
在if(intersects.length >
0) {
}
代码段,添加如下代码
const pos = selectObj.geometry.attributes.position
tag.position.set(pos.getX(0),pos.getY(0),pos.getZ(0))
tag.position.y = 1;
根据点选的模型位置不同,显示不同的信息
我们可以在点选物体是,通过判断.name属性来修改各个模型的信息,具体代码如下
if(selectObj.name === "网格457") {
txtName.innerHTML='车身'
txtMaterial.innerHTML = '合金'
}
else if (selectObj.name === "网格457_2") {
// 座位
txtName.innerHTML='车座'
txtMaterial.innerHTML = '真皮'
}
else if (selectObj.name === "网格457_4") {
// 车把
txtName.innerHTML='车把'
txtMaterial.innerHTML = '真皮'
}
else if (selectObj.name === "网格457_3" || selectObj.name === "网格457_5" || selectObj.name === "网格457_6") {
// 车架
txtName.innerHTML='车架'
txtMaterial.innerHTML = '铝合金'
}
else if (selectObj.name === "网格457_7") {
// 轮胎
txtName.innerHTML='轮胎'
txtMaterial.innerHTML = '橡胶'
}
else {
}
txtColor.innerHTML = selectObj.material.color.getHexString()
在鼠标点击模型意外的区域是,使用.remove()方法将tag移除
initListener()函数的完整代码如下
initListener() {
const that = this
const div = document.querySelector('#label')
const txtName = document.querySelector('#txtName')
const txtMaterial = document.querySelector('#txtMaterial')
const txtColor = document.querySelector('#txtColor')
// div.style.top='-240px'
const tag = new CSS2DObject(div)
this.renderer.domElement.addEventListener('click',function(event){
const px = event.offsetX;
const py = event.offsetY;
const x = (px / window.innerWidth) * 2 - 1;
const y = -(py / window.innerHeight) * 2 + 1;
const raycaster = new THREE.Raycaster();
raycaster.setFromCamera(new THREE.Vector2(x, y), that.camera);
const intersects = raycaster.intersectObjects(motorModel.children)
if(intersects.length >
0) {
intersects[0].object.add(tag)
selectObj = intersects[0].object
const pos = selectObj.geometry.attributes.position
tag.position.set(pos.getX(0),pos.getY(0),pos.getZ(0))
tag.position.y = 1;
if(selectObj.name === "网格457") {
txtName.innerHTML='车身'
txtMaterial.innerHTML = '合金'
}
else if (selectObj.name === "网格457_2") {
// 座位
txtName.innerHTML='车座'
txtMaterial.innerHTML = '真皮'
}
else if (selectObj.name === "网格457_4") {
// 车把
txtName.innerHTML='车把'
txtMaterial.innerHTML = '真皮'
}
else if (selectObj.name === "网格457_3" || selectObj.name === "网格457_5" || selectObj.name === "网格457_6") {
// 车架
txtName.innerHTML='车架'
txtMaterial.innerHTML = '铝合金'
}
else if (selectObj.name === "网格457_7") {
// 轮胎
txtName.innerHTML='轮胎'
txtMaterial.innerHTML = '橡胶'
}
else {
}
txtColor.innerHTML = selectObj.material.color.getHexString()
}
else {
if(selectObj) {
selectObj.remove(tag) //从场景移除
}
}
}
)
}
刷新浏览器,查看效果
ok,今天内容有点多,已经晚上一点了,就写到这里吧,有问题评论区留言,喜欢的小伙伴点赞关注收藏哦!
我的博客即将同步至开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=2r6ku3l1vyyo4
声明:本文内容由网友自发贡献,本站不承担相应法律责任。对本内容有异议或投诉,请联系2913721942#qq.com核实处理,我们将尽快回复您,谢谢合作!
若转载请注明出处: Threejs进阶之五:使用CSS2DRenderer给模型添加HTML标签
本文地址: https://pptw.com/jishu/291381.html