【3D】vue中使用threejs的CSS2DRenderer的问题

957 阅读5分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第6天,点击查看活动详情

前言

  • 直接看5.5、你以为解决了吗?

[ 因为three.js更新了版本,引入和使用方式都变了,新版本下CSS2DRenderer的使用已经没有前面四点提及的问题,前四点仅做记录。 ]

  • 直接看5.5、你以为解决了吗?
  • 直接看5.5、你以为解决了吗?

一、引用与使用

1、配置webpack.base.conf文件

往CSS2DRenderer中导入THREE,配置webpack.base.conf文件如下:

{
	test: require.resolve("three/examples/js/renderers/CSS2DRenderer"),
	use: "imports-loader?THREE=three"
}

说明:

当报错THREE未定义时,因为CSS2DRenderer中用了很多THREE对象,而THREE对象若不导入系统不知道是啥。

如上配置,将three导入three/examples/js/renderers/CSS2DRenderer,THREE就等于three暴露出的变量。

2、导入CSS2DRenderer包

import 'three/examples/js/renderers/CSS2DRenderer'

注意:

此处需要使用CSS2DRenderer中的两个对象CSS2DRenderer和CSS2DObject。

3、CSS2DObject和CSS2DRenderer的使用

detailCSS2D = new THREE.CSS2DObject(detailDiv)
labelRenderer = new THREE.CSS2DRenderer()

然后警告,如下:

  • export 'CSS2DObject' (imported as 'THREE') was not found in 'three'
  • export 'CSS2DRenderer' (imported as 'THREE') was not found in 'three'

但不影响正常使用。

警告说明:在three中没有发现导出CSS2DObject和CSS2DRenderer。

二、部分关键代码

{
	test: require.resolve("three/examples/js/controls/OrbitControls"),
	use: "imports-loader?THREE=three"
},
{
	test: require.resolve("three/examples/js/controls/OrbitControls"),
	use: "exports-loader?THREE.OrbitControls"
},
{
	test: require.resolve("three/examples/js/postprocessing/EffectComposer"),
	use: "imports-loader?THREE=three"
},
{
	test: require.resolve("three/examples/js/postprocessing/EffectComposer"),
	use: "exports-loader?THREE.EffectComposer"
},
{
	test: require.resolve("three/examples/js/renderers/CSS2DRenderer"),
	use: "imports-loader?THREE=three"
},
// 经测试,下面两个配置作用不大,去掉或者加上同样报警告,但是不影响使用
{
	test: require.resolve("three/examples/js/renderers/CSS2DRenderer"),
	use: "exports-loader?THREE.CSS2DObject"
},
{
	test: require.resolve("three/examples/js/renderers/CSS2DRenderer"),
	use: "exports-loader?THREE.CSS2DRenderer"
}

import * as THREE from 'three'
import OrbitControls from 'three/examples/js/controls/OrbitControls'
import EffectComposer from 'three/examples/js/postprocessing/EffectComposer'
import 'three/examples/js/renderers/CSS2DRenderer'

controls = new OrbitControls(camera, renderer.domElement)
composer = new EffectComposer(renderer)
detailCSS2D = new THREE.CSS2DObject(detailDiv)
labelRenderer = new THREE.CSS2DRenderer()

三、遗留问题

通常webpack中引入three.js相关包,参考OrbitControls和EffectComposer的引入:

  • 1、在配置文件给它们导入THREE,再导出对应使用对象THREE.OrbitControls和THREE.EffectComposer。
  • 2、导入时,将其赋值给一个对象(可以这么描述吧)
import OrbitControls from 'three/examples/js/controls/OrbitControls'
import EffectComposer from 'three/examples/js/postprocessing/EffectComposer'
  • 3、使用的时候直接new这个对象即可
new OrbitControls()和new EffectComposer()

但是!!

对于CSS2DRenderer同上引入:

{
	test: require.resolve("three/examples/js/renderers/CSS2DRenderer"),
	use: "imports-loader?THREE=three"
},
{
	test: require.resolve("three/examples/js/renderers/CSS2DRenderer"),
	use: "exports-loader?THREE.CSS2DObject"
},
{
	test: require.resolve("three/examples/js/renderers/CSS2DRenderer"),
	use: "exports-loader?THREE.CSS2DRenderer"
}

import {CSS2DObject, CSS2DRenderer} from 'three/examples/js/renderers/CSS2DRenderer'

detailCSS2D = new CSS2DObject(detailDiv)
labelRenderer = new CSS2DRenderer()

无法正常使用,报错如下:

Uncaught TypeError: __WEBPACK_IMPORTED_MODULE_7_three_examples_js_renderers_CSS2DRenderer__.CSS2DObject is not a constructor

只能按照一、引用与使用中的三步骤引入,才可以正常使用,但是控制台有警告如下:

warnings @ webpack-internal:///./node_modules/webpack-dev-server/client/index.js?http://localhost:8088:153
webpack-internal:///./node_modules/webpack-dev-server/client/index.js?http://localhost:8088:153 ./src/components/view/demoTest/index.js
876:26-43 "export 'CSS2DObject' (imported as 'THREE') was not found in 'three'
 @ ./src/components/view/demoTest/index.js
 @ ./src/components/view ^\.\/.*$
 @ ./src/http/auth/index.js
 @ ./src/http/index.js
 @ ./src/main.js
 @ multi (webpack)-dev-server/client?http://localhost:8088 webpack/hot/dev-server event-source-polyfill babel-polyfill ./src/main.js
 
warnings @ webpack-internal:///./node_modules/webpack-dev-server/client/index.js?http://localhost:8088:153
webpack-internal:///./node_modules/webpack-dev-server/client/index.js?http://localhost:8088:153 ./src/components/view/demoTest/index.js
885:26-45 "export 'CSS2DRenderer' (imported as 'THREE') was not found in 'three'
 @ ./src/components/view/demoTest/index.js
 @ ./src/components/view ^\.\/.*$
 @ ./src/http/auth/index.js
 @ ./src/http/index.js
 @ ./src/main.js
 @ multi (webpack)-dev-server/client?http://localhost:8088 webpack/hot/dev-server event-source-polyfill babel-polyfill ./src/main.js

所以我只好直接引入整个js文件,再使用其中的变量。

警告总比报错好吧……

求解:

1、报错怎么解决啊啊啊

Uncaught TypeError: __WEBPACK_IMPORTED_MODULE_7_three_examples_js_renderers_CSS2DRenderer__.CSS2DObject is not a constructor

2、警告怎么屏蔽啊啊啊

export 'CSS2DObject' (imported as 'THREE') was not found in 'three'

四、其他问题

换了新项目,同样的代码,CSS2DRenderer的DIV死活不显示了,查看元素display被设置成了none,可是我明明修改成了block,见鬼了。

注释掉CSS2DRenderer段代码div就显示了,于是怀疑是CSS2DRenderer源码的问题,查看对比果然不一样,再看three.js版本,原来的100正常,现在的108不正常。

临时处理方法:

修改文件package.json如下:

"three": "^0.100.0",
package-lock.json
"three": {
  "version": "0.100.0",
  "resolved": "https://registry.npmjs.org/three/-/three-0.100.0.tgz",
  "integrity": "sha512-/lN2rdE1OqIwJr4/HcSaOisiCY0uVA0sqPpbCG5nil2uICEdS0LfGwSVYTtZDsIpR76r3++h5H3Hzg5D+SJBRQ=="
},

保存修改文件后,运行命令cnpm install --save即可。

五、新版本下CSS2DRenderer的使用

5.1、更新版本前关键代码如下:

<div id="test" class="test">{{label.name}}</div>

.test {
  display: none;
  width: 140px;
  font-size: 14px;
  text-align: center;
  line-height: 30px;
  background: linear-gradient(180deg,rgba(0,180,220,0.3),rgba(0,180,220,0.1),rgba(0,180,220,0.1),rgba(0,180,220,0.3));
  box-shadow: 0 0 2rem rgba(0,180,220,.1) inset;
  color: #eee;
  cursor: pointer;
}

import 'three/examples/js/renderers/CSS2DRenderer' // 引入包

labelRenderer = new CSS2DRenderer() // 创建labelRenderer

let labelDiv= document.getElementById('test')
labelDiv.style.display = 'block'
let labelCSS2D = new CSS2DObject(labelDiv) // 创建labelCSS2D

5.2、代码说明:

页面上一个div,样式设置先display: none;隐藏,然后代码中获取div,设置为display = 'block',加到CSS2DObject中通过CSS2DRenderer渲染。

5.3、效果:

在three.js更新版本之前这么写正常,但是一旦升级了three.js的版本,div死活不出现了。

5.4、解决:

方法一:

降低three.js的版本,当然这是没办法的办法,毕竟都在往前走降低版本很多新功能就用不上了。

方法二:

看到有人说谷歌浏览器display: none;不支持,应该写成display: "";

然后我照做,果然div显示了,但是是因为浏览器根本不支持display: "";从头到尾就没有隐藏div!!!

切换火狐浏览器也是同样的效果,所以说去他的谷歌不支持display: none;查了官方文档也没说不支持。

方法三:

猜想是不是样式控制优先级的原因,把class中的display: none;放到style里面试试……

果然!!!正解啊!!!

修改代码如下:

<div id="test" style="display: none;">{{label.name}}</div>

.test {
  /* display: none; */
  width: 140px;
  font-size: 14px;
  text-align: center;
  line-height: 30px;
  background: linear-gradient(180deg,rgba(0,180,220,0.3),rgba(0,180,220,0.1),rgba(0,180,220,0.1),rgba(0,180,220,0.3));
  box-shadow: 0 0 2rem rgba(0,180,220,.1) inset;
  color: #eee;
  cursor: pointer;
}

5.5、你以为解决了吗?

我更新了three.js版本,又有新的问题了。

它出现了,可是它不隐藏了。。。

新版本下正确使用CSS2DRenderer的关键代码:

<div id="test" style="display: none;">{{label.name}}</div>

.test {
  /* display: none; */
  width: 140px;
  font-size: 14px;
  text-align: center;
  line-height: 30px;
  background: linear-gradient(180deg,rgba(0,180,220,0.3),rgba(0,180,220,0.1),rgba(0,180,220,0.1),rgba(0,180,220,0.3));
  box-shadow: 0 0 2rem rgba(0,180,220,.1) inset;
  color: #eee;
  cursor: pointer;
}

import { CSS2DRenderer, CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer' // 引入包

initRenderLabel () {
  // 创建labelRenderer div3D为当前场景挂载的div
  labelRenderer = new CSS2DRenderer()
  labelRenderer.setSize(div3D.clientWidth, div3D.clientHeight)
  labelRenderer.domElement.style.position = 'absolute'
  div3D.appendChild(labelRenderer.domElement)
}

let labelCSS2D = scene.getObjectByName('test')
  if (labelCSS2D === undefined) {
    let labelDiv = document.getElementById('test')
    // labelDiv.style.display = 'block'
    labelCSS2D = new CSS2DObject(labelDiv)
    labelCSS2D.name = 'test'
    scene.add(labelCSS2D)
  } else {
    labelCSS2D.visible = true
}

关键点:

1、隐藏div

此处必须通过style隐藏,不能通过class隐藏。

2、引入相关包

此处从jsm文件夹中引入,无需额外的配置即可使用。

3、显示和隐藏

通过对CSS2DObject创建的对象控制其在场景中visible属性,从而控制显示和隐藏。