three.js
vue.js

Vue.jsでthree.jsを使ってみる

Vue.jsでthree.jsを使ってみるときに色々躓いたので書き方をまとめてみました。

準備

  1. vue init webpack-simple three-vue-sample
  2. とりあえず設定はenterで
  3. npm i && npm i -D three
  4. srcの下にSample.vueを作成

コード

箱を回転させるサンプルを作成。

src/App.vue
<template>
  <sample></sample>
</template>

<script>
import Sample from "./Sample.vue";

export default {
  name: 'app',
  components: { Sample },
}
</script>

<style></style>
src/Sample.vue
<template>
  <div ref="stage"></div>
</template>

<script>
  import * as THREE from 'three';

  export default {

    name: 'sample',

    data () {
      // === scene ===
      const scene = new THREE.Scene ();

      // === renderer ===
      const renderer = new THREE.WebGLRenderer ();
      renderer.setSize( window.innerWidth, window.innerHeight );

      // === camera ===
      const camera = new THREE.PerspectiveCamera (75, window.innerWidth / window.innerHeight, 0.1, 1000);
      camera.position.z = 5;

      // === light ===
      const light = new THREE.DirectionalLight(0xffffff);
      light.position.set(0, 0, 10);

      // === model ===
      const geometry = new THREE.BoxGeometry (1, 1, 1);
      const material = new THREE.MeshStandardMaterial ({ color: 0x00ff00 });
      const cube = new THREE.Mesh (geometry, material);

      return {
        scene: scene,
        renderer: renderer,
        camera: camera,
        light: light,
        cube: cube
      }
    },

    created () {
      // === sceneにmodel,light, cameraを追加 ===
      this.scene.add( this.camera );
      this.scene.add( this.light);
      this.scene.add( this.cube );
    },

    mounted () {
      // === DOMを追加, animate ===
      this.$refs.stage.appendChild(this.renderer.domElement);
      this.animate();
    },

    methods: {

      animate () {
        requestAnimationFrame( this.animate );

        this.cube.rotation.x += 0.05;
        this.cube.rotation.y += 0.05;

        this.renderer.render(this.scene, this.camera);
      }

    }
  }
</script>

<style></style>

解説

  1. data()で各種変数の初期化
    • mixin, componentを利用して分割することも可能、今回は割愛。
  2. created()でsceneに取り込む
    • 双方向バインディングを適用した状態で取り込みたかったため、data()内で取り込まずに、created()で組み立てた。
  3. mounted()でrendererをDOMに埋め込み、animate()を実行
    • $refsはmounted以降でないと利用できないため、DOMへの埋め込みは最後になる。
  4. methods.animate()でアニメーションを行う。

回転速度をvue.jsで制御

templateを少し変更し、回転速度(speed)をvueのデータバインディング配下にする。

model.vue
<template>
  <div>

    <div class="controller">
      <!-- .numberをつけておくことで、valueを自動的に数値に変換してくれる -->
      <input v-model.number="speed" type="number">
    </div>

    <div class="stage">
      <div ref="stage"></div>
    </div>

  </div>
</template>

<script>
  import * as THREE from 'three';

  export default {

    name: 'model',

    data () {

      ...

      return {
        scene: scene,
        renderer: renderer,
        camera: camera,
        light: light,
        cube: cube,

        speed: 0.05 // データバインディングできるようにする
      }
    },

    ...

    methods: {

      animate () {
        requestAnimationFrame( this.animate );

        this.cube.rotation.x += this.rotate; // computed.rotate
        this.cube.rotation.y += this.rotate;

        this.renderer.render(this.scene, this.camera);
      }

    },

    computed: {

      rotate () {
        // inputに何も入力されていないときに''で認識されてしまうため、回避
        if (this.speed === '') {
          return 0;
        } else {
          return this.speed;
        }
      }

  }
</script>

...

フォームの値を変更することで回転速度が変化させることができる。