Why is my Three.js animation slower in Vue.js?

廉价感情. 提交于 2020-04-30 06:46:26

问题


I'm trying to recreate this awesome wave simulation: https://codepen.io/cheekymonkey/pen/vMvYNV, in Vue.js. However the animation is really slow when I've recreated it in Vue.js

I've tried making all of the same functions as Vue.js methods and including the variables in the component's data property.

<template>
    <div id="container" style="width:100%; height:100vh;"></div>
</template>

<script>
import * as Three from 'three'
import SimplexNoise from'simplex-noise'

export default {
  name: 'ThreeTest',
  data() {
    return {
      camera: Three.PerspectiveCamera,
      scene: Three.Scene,
      renderer: Three.WebGLRenderer,
      mesh: Three.Mesh,
      noise: SimplexNoise,
      geometry: null,
      factor: 0
    }
  },
  methods: {
    init: function() {
      this.createScene();
      this.createCamera();
      this.createShape();
      this.addSpotlight('#fdffab');
      this.addAmbientLight();
      this.animate();
      window.addEventListener('resize', this.onResize())

    },
    onResize: function() {
      let container = document.getElementById('container');
      this.renderer.setSize(container.clientWidth, container.clientHeight);
      this.camera.aspect = container.clientWidth / container.clientHeight;
      this.camera.updateProjectionMatrix();
    },
    createScene: function() {
      this.scene = new Three.Scene();
      this.renderer = new Three.WebGLRenderer({
        antialias: true,
        alpha: true
      });
      let container = document.getElementById('container');
      this.renderer.setSize(container.clientWidth, container.clientHeight);
      this.renderer.setPixelRatio(window.devicePixelRatio);
      this.renderer.setClearColor(new Three.Color('#fff'));
      //this.render.shadowMap.type = Three.PCFSoftShadowMap;
      this.noise = new SimplexNoise()
      container.appendChild(this.renderer.domElement);
    },
    createCamera: function() {
      this.camera = new Three.PerspectiveCamera(20, container.clientWidth/container.clientHeight, 1, 1000);
      this.camera.position.set(0, 0, 20);
    },
    createShape: function() {
      const seg = 100
      this.geometry = new Three.PlaneGeometry(5, 8, seg, seg)
      const material = new Three.MeshPhysicalMaterial({
        color: '#da0463',
        metalness: 0.6,
        emissive: '#000',
        side: Three.DoubleSide,
        wireframe: true
      })
      this.mesh = new Three.Mesh(this.geometry, material)
      this.mesh.receiveShadow = true
      this.mesh.castShadow = true
      this.mesh.position.set(0, 0, 0)
      this.mesh.rotation.x = -Math.PI / 3
      this.mesh.rotation.z = -Math.PI / 4
      this.scene.add(this.mesh)
    },
    addSpotlight: function(color) {
      const light = new Three.SpotLight(color, 2, 1000)
      light.position.set(0, 0, 30)
      this.scene.add(light)
    },
    addAmbientLight: function() {
      const light = new Three.AmbientLight('#fff', 0.5)
      this.scene.add(light)
    },
    addjustVertices: function() {
      for (let i = 0; i < this.geometry.vertices.length; i++) {
        const vertex = this.geometry.vertices[i]
        const x = vertex.x / 4
        const y = vertex.y / 6
        vertex.z = this.noise.noise2D(x, y + this.factor)
      }
      this.factor += 0.007
      this.geometry.verticesNeedUpdate = true
      this.geometry.computeVertexNormals()
    },
    animate: function() {
        requestAnimationFrame(this.animate);
        this.addjustVertices();
        this.camera.updateProjectionMatrix();
        this.renderer.render(this.scene, this.camera);
    }
  },
  mounted() {
      this.init();
  }
}
</script>

It works, as in the wave animates like it should, but it's much slower. Not sure if this is due to Vue.js or just how I've set it up. Any advice much appreciated!


回答1:


This is occurring because Vue under the hood makes each component attached to the instance reactive. Reactivity in Depth. Open up the dev tools and look at your memory footprint, when I ran your snippet on my machine it is ~300mb as opposed to the codepen example which is ~20mb.

When you console.log(this.scene) what you see are the getters/setters that allow Vue to track objects.

The below snippet pulls the three.js objects out of the Vue data object and attaches them to a vue-static object. This plugin allows you to set certain variables as non-reactive.

<template>
    <div id="container" style="width:100%; height:100vh;"></div>
</template>

<script>
import * as Three from 'three'
import SimplexNoise from'simplex-noise'

export default {
  name: 'ThreeTest',
  static(){
    return {
      scene: new Three.Scene(),
      camera: null,
      renderer: Three.WebGLRenderer,
      mesh: new Three.Mesh,
      noise: SimplexNoise,
      factor:0
    }
  },
  mounted() {
    this.init();
  },
  methods: {
    init: function() {
      this.createScene();
      this.createCamera();
      this.createShape();
      this.addSpotlight('#fdffab');
      this.addAmbientLight();
      this.animate();
      window.addEventListener('resize', this.onResize())
    },
    onResize: function() {
      let container = document.getElementById('container');
      this.renderer.setSize(container.clientWidth, container.clientHeight);
      this.camera.aspect = container.clientWidth / container.clientHeight;
      this.camera.updateProjectionMatrix();
    },
    createScene: function() {

      console.log("TCL: this.$options.bigHairyHorseNuts ",this.bigHairyHorseNuts)

      this.renderer = new Three.WebGLRenderer({
        antialias: true,
        alpha: true
      });
      let container = document.getElementById('container');
      this.renderer.setSize(container.clientWidth, container.clientHeight);
      this.renderer.setPixelRatio(window.devicePixelRatio);
      this.renderer.setClearColor(new Three.Color('#fff'));
      //this.render.shadowMap.type = Three.PCFSoftShadowMap;
      this.noise = new SimplexNoise()
      container.appendChild(this.renderer.domElement);
    },
    createCamera: function() {
      this.camera = new Three.PerspectiveCamera(20, container.clientWidth/container.clientHeight, 1, 1000);
      this.camera.position.set(0, 0, 20);
    },
    createShape: function() {
      const seg = 100
      this.geometry = new Three.PlaneGeometry(5, 8, seg, seg)
      const material = new Three.MeshPhysicalMaterial({
        color: '#da0463',
        metalness: 0.6,
        emissive: '#000',
        side: Three.DoubleSide,
        wireframe: true
      })
      this.mesh = new Three.Mesh(this.geometry, material)
      this.mesh.receiveShadow = true
      this.mesh.castShadow = true
      this.mesh.position.set(0, 0, 0)
      this.mesh.rotation.x = -Math.PI / 3
      this.mesh.rotation.z = -Math.PI / 4
      this.scene.add(this.mesh)
    },
    addSpotlight: function(color) {
      const light = new Three.SpotLight(color, 2, 1000)
      light.position.set(0, 0, 30)
      this.scene.add(light)
    },
    addAmbientLight: function() {
      const light = new Three.AmbientLight('#fff', 0.5)
      this.scene.add(light)
    },
    addjustVertices: function() {
      for (let i = 0; i < this.geometry.vertices.length; i++) {
        const vertex = this.geometry.vertices[i]
        const x = vertex.x / 4
        const y = vertex.y / 6
        vertex.z = this.noise.noise2D(x, y + this.factor)
      }
      this.factor += 0.007
      this.geometry.verticesNeedUpdate = true
      this.geometry.computeVertexNormals()
    },
    animate: function() {
        requestAnimationFrame(this.animate);
        this.addjustVertices();
        this.camera.updateProjectionMatrix();
        this.renderer.render(this.scene, this.camera);
    }
  }
}
</script>


来源:https://stackoverflow.com/questions/58559139/why-is-my-three-js-animation-slower-in-vue-js

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!