Recently, I want to use ThreeJs in Vue 3.0. However, there has been a problem I came across. When I init a new threejs’s object in data property in vue 3.0, such as Scene, Camera and so on, they will be transform into Proxy form. It will lead some attributes in these class can no be changed, because in the source code of threejs, the ‘defineProperty’ of Object is used, an is declared as ‘configurable: false’. Therefore, there will be a problem in console, which is ‘‘get’ on proxy: property ‘modelViewMatrix’ is a read-only and non-configurable data property on the proxy target but the proxy did not return its actual value (expected ‘#’ but got ‘[object Object]’)’
<template>
<div id="app">
<div id="three-earth" ref="three-earth" />
</div>
</template>
<script>
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
export default {
name: 'Earth',
props: {
},
components: {
},
data() {
return {
container: null,
render: null,
camera: null,
scene: null,
raycaster: new THREE.Raycaster(),
mouse: new THREE.Vector2(),
controls: null
}
},
mounted() {
this.initRender()
this.initScene()
this.initCamera()
this.initLight()
this.initModel()
// this.initGui()
this.initControls()
this.animate()
window.onresize = this.onWindowResize
window.addEventListener('click', this.onMouseClick, false)
},
methods: {
initRender: function() {
this.render = new THREE.WebGLRenderer({ antialias: true, alpha: true })
this.render.setSize(this.$refs['three-earth'].offsetWidth, this.$refs['three-earth'].offsetHeight)
this.render.shadowMap.enabled = true
this.render.shadowMap.type = THREE.PCFSoftShadowMap
this.render.setClearColor(0xffffff)
this.container = document.getElementById('three-earth')
this.container.appendChild(this.render.domElement)
},
initCamera: function() {
this.camera = new THREE.PerspectiveCamera(75, this.$refs['three-earth'].offsetWidth / this.$refs['three-earth'].offsetHeight, 0.1, 1000)
this.camera.position.set(0, 40, 100)
this.camera.lookAt(new THREE.Vector3(0, 0, 0))
},
initScene: function() {
this.scene = new THREE.Scene()
},
initLight: function() {
},
initModel: function() {
var helper = new THREE.AxesHelper(10)
this.scene.add(helper)
var s = 25
var cube = new THREE.CubeGeometry(s, s, s)
for (var i = 0; i < 3000; i++) {
var material = new THREE.MeshBasicMaterial({ color: this.randomColor() })
var mesh = new THREE.Mesh(cube, material)
mesh.position.x = 800 * (2.0 * Math.random() - 1.0)
mesh.position.y = 800 * (2.0 * Math.random() - 1.0)
mesh.position.z = 800 * (2.0 * Math.random() - 1.0)
mesh.rotation.x = Math.random() * Math.PI
mesh.rotation.y = Math.random() * Math.PI
mesh.rotation.z = Math.random() * Math.PI
mesh.updateMatrix()
this.scene.add(mesh)
}
},
initStats: function() {
/* this.stats = new Stats()
this.container.appendChild(this.stats) */
},
initControls: function() {
this.controls = new OrbitControls(this.camera, this.render.domElement)
this.controls.enableDamping = true
this.controls.enableZoom = true
this.controls.autoRotate = false
this.controls.minDistance = 50
this.controls.maxDistance = 200
this.controls.enablePan = true
},
animate: function() {
this.threeRender()
// this.stats.update()
this.controls.update()
requestAnimationFrame(this.animate)
},
randomColor: function() {
var arrHex = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f']
var strHex = '#'
var index = ''
for (var i = 0; i < 6; i++) {
index = Math.round(Math.random() * 15)
strHex += arrHex[index]
}
return strHex
},
onMouseClick: function(event) {
this.mouse.x = ((event.clientX - 210) / this.$refs.secenContainer.offsetWidth) * 2 - 1
this.mouse.y = -((event.clientY - 84) / this.$refs.secenContainer.offsetHeight) * 2 + 1
// 通过鼠标点的位置和当前相机的矩阵计算出raycaster
this.raycaster.setFromCamera(this.mouse, this.camera)
// 获取raycaster直线和所有模型相交的数组集合
var intersects = this.raycaster.intersectObjects(this.scene.children)
// 将所有的相交的模型的颜色设置为红色,如果只需要将第一个触发事件,那就数组的第一个模型改变颜色即可
if (intersects.length > 0) {
intersects[0].object.material.color.set(0xff0000)
}
},
onWindowResize: function() {
this.camera.aspect = this.$refs.secenContainer.offsetWidth / this.$refs.secenContainer.offsetHeight
this.camera.updateProjectionMatrix()
this.threeRender()
this.render.setSize(this.$refs.secenContainer.offsetWidth, this.$refs.secenContainer.offsetHeight)
},
threeRender: function() {
this.render.render(this.scene, this.camera)
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
#app{
width: 100%;
height: 100%;
}
#three-earth{
width: 100%;
height: 100%;
}
</style>