three.js 添加html内容、文本

 

需求:

1、在场景内添加html元素并动态更新

2、html内容需跟随场景变化

 

方案:

新加方案:https://www.zhihu.com/question/49929467/answer/118602848

1、在场景内创建一个模型点(多个模型点最好分组,方便管理)

2、获取到模型点

3、创建html元素

4、将html元素绑定到模型位置(将场景的3D坐标转为2D)

5、实时计算元素位置 (显示隐藏)

    animate() {
      // 动画函数
      this.render();
      window.requestAnimationFrame(() => this.animate());
      TWEEN.update();
      this.orbitControls.update();
      this.update(); // 更新点
    },



 LoadBox() {
      // 创建点。
      this.groups = new THREE.Group();
      this.groups.name = "btnlist";
      for (let index = 0; index < this.nowfloat.length; index++) {
        const element = this.nowfloat[index].XYZ;
        console.log(element);
        let floorGeometry1 = new THREE.PlaneGeometry(1, 1, 1);
        var sphereMaterial = new THREE.MeshFaceMaterial({
          color: 0xff00ff,
          // side: THREE.BackSide,
          // visible: false,
        });
        var floorspheres = new THREE.Mesh(floorGeometry1, sphereMaterial);
        floorspheres.position.set(
          element.x,
          element.y + element.s / 2,
          element.z
        );
        floorspheres.name = this.nowfloat[index].id; //default
        this.groups.add(floorspheres);
      }
      this.buffer_scene.add(this.groups);
      // this.render()
    },
    update() {
      // 更新css2D内容位置
      let halfWidth = window.innerWidth/2 ;
      let halfHeight = window.innerHeight/2;
      var btnlist = this.buffer_scene.getObjectByName("btnlist");
      if (!btnlist) return;
      
      btnlist.traverse((e) => {
        var vector = e.position.clone().project(this.buffer_camera);
        var htmls = document.getElementsByClassName(e.name)[0];
        if (htmls) {
          var left = vector.x * halfWidth + halfWidth; // + halfWidth
          var top = -vector.y * halfHeight + halfHeight; //+ halfHeight
          // console.log(vector.x,vector.y)
            if(vector.z >1){
                htmls.style = 'display: none;';
            }else{
            htmls.style = `width:auto;display: flex;position: absolute;left:${
                left
            }px;top:${top}px`;
            }

        }
      });
    },

 

 

 

 

有几种方案

一、直接将文本P成为图片 然后在场景内加载 2D模型将该图片当作模型贴图处理

二、将html内容通过canvas 然后再以模型的方式加载进场景(不做详细解释)

1、创建

    getTextCanvas(text){ 
      // 创建贴图文字
        var width=512, height=256; 
        var canvas = document.createElement('canvas');
        canvas.width = width;
        canvas.height = height;
        var ctx = canvas.getContext('2d');
        ctx.fillStyle = '#008080';
        ctx.fillRect(0, 0, width, height);
        ctx.font = 50+'px " bold';
        ctx.fillStyle = '#FFFFFF';
        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';
        ctx.fillText(text, width/2,height/2); 
        return canvas;
    },

2、加载

        var geometry = new THREE.PlaneGeometry(e.w, e.h, 30 );
        var material = new THREE.MeshBasicMaterial(
          {
            // map:new THREE.CanvasTexture(this.getTextCanvas(e.content)),  // canvas 画图方式
            map:new THREE.TextureLoader().load(`${this.GLOBAL.service}img/043img/btn/${e.content}.png`), 
            } );
        var introduce = new THREE.Mesh( geometry, material );
        introduce.name= "model introduce"
        // plane.location=i.location
        introduce.modeltype= 3 //
        introduce.position.set(x,y,z);
        introduce.lookAt(0,y,0)
        skyBox.add(introduce);

 

三、用CSS2DObject进行处理

1、增加渲染器 CSS2DRenderer  

2、使用 three.js 的 CSS2DObject 模块 进行html内容转换 并绑定模型 

官网案例  

官网示例代码 

一般需要这个功能的肯定都不是新手了,所以内容就不做介绍了,这里只需要关注几点

1、增加渲染器(原有的渲染器不动,新增一个CSS2DRenderer渲染器 ):

  addhtml() {
      // 场景渲染器

      this.renderer = new THREE.WebGLRenderer();
      this.renderer.shadowMapEnabled = true;
      this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
      // this.renderer.gammaOutput = true;
      this.renderer.gammaFactor = 2.2;
      // this.renderer.setClearColor(new THREE.Color(0xcce0ff));

      this.renderer.setSize(window.innerWidth*0.95, window.innerHeight*0.95);
      document
        .getElementById("container")
        .appendChild(this.renderer.domElement);
      // window.addEventListener("resize", () => this.onWindowResize());
      
      this.labelRenderer = new CSS2DRenderer();  // 新增的渲染器
      this.labelRenderer.setSize(window.innerWidth*0.95, window.innerHeight*0.95);
      // this.labelRenderer.domElement.style.position = 'absolute';
      // this.labelRenderer.domElement.style.top = 0;
      this.labelRenderer.domElement.style="pointer-events: auto;position: absolute;top: 0px;"  // 处理新的渲染器
      document.getElementById("container").appendChild(this.labelRenderer.domElement);
   },
    animate() {
      // 渲染
      this.renderer.render(this.scene, this.camera);
      this.labelRenderer.render(this.scene, this.camera);// 加载新渲染器
      window.requestAnimationFrame(() => this.animate());
      TWEEN.update();
    },

 

注意:官网示例只适应于全屏的情况 不全屏的情况请自行调整

2、增加内容容器:

<template>
  <div class="project">
    <div id="text" style="    position: relative;width: 30%;
    height: 100px;
    color: #fff;">
      {{modelnumber}}
    </div>
    <div id="container">
 
  </div>
</template>

 

3、绑定模型(创建模型什么的这里就不解释了):

    // 2D 文本
    // let laberDiv = document.createElement('div');//创建div容器
    let laberDiv = document.getElementById('text');//获取div容器
        // laberDiv.innerHTML=`
        // <div class="leftTip" style="">
        //    ${this.modelnumber}
        // </div>
        // `;
        laberDiv.style.marginTop = '-1em';
    let pointLabel = new CSS2DObject(laberDiv);   
    pointLabel.position.set(0,100,0);  // 相对模型的位置
    console.log(pointLabel)   
    skyBox.add(pointLabel); //绑定到模型

也看到了,你也可以选择直接新建一个容器、但是我尝试的时候内容无法动态变化、如果你不需要动态变化也可以直接新建 

找资料的时候看到一个问题(没有尝试)、创建的这个容器切换场景不会消失、需要注意处理、这个创建出来的不在模型列表内、而是在dom里面、所以遇到这个情况、请尝试直接处理dom

 

效果:

 

注:

前面两种方式是直接在场景内加载一个模型、第三种方式是将元素绑定在模型上

 

posted @ 2020-10-30 12:19  睡到自然醒ccc  阅读(3320)  评论(0编辑  收藏  举报