【メモ】Vue.jsでthree.js360動画再生

# 経緯
three.jsを使って360動画再生したかった。
Vue.jsだとどうなるのか試したかった。
一旦コードだけ載せて後でキレイに編集したいと思う。
ビデオは[こちら](https://vimeo.com/214402865)のフリーダウンロードのものを使わせていただこうと思う。
（今回は再生試したいだけだし、CrossOriginのためダウロードしたものをプロジェクトのComponentと同じ階層に入れます）

# 仕様

1. 360ビデオを再生したい
2. Start、Pause、Stopしたい
3. URL変更したい

# Projectセットアップ
npm に threejs用ライブラリがあるのでインストール
コントローラ系は別途インストールの必要がありそう
(three-orbit-controlsとかthree-dragcontrols)


```vue

$vue create three_video360
$cd three_video360
$npm install three
$npm install three-orbit-controls
```


# ソースコード

[ayutaya.mp4](https://vimeo.com/214402865)はダウンロード直後の名前にスペースが入ってた関係でリネームした。
Videoタグを生成して埋め込む感じで作った。
Sphereの内側に動画をテクスチャとして貼って、カメラをSphere内に置いて回りを見回す。


```vuejs:Video360.vue

<template>
  <div>
    <div>
      <label for="videourl">URL</label>
      <input
        v-model="videoUrl"
        id="videourl"
        width="500px"
      ><button @click="changeURL()">Change video</button>
      <button
        @click="playVideo()"
        v-bind:disabled="isplaying"
      >
        Play
      </button>
      <button
        @click="pauseVideo()"
        v-bind:disabled="!isplaying"
      >
        Pause
      </button>
      <button @click="stopVideo()">
        Stop
      </button>
    </div>
    <div id="video360player"> </div>
  </div>
</template>

<script>
import * as THREE from 'three'
import * as controls from 'three-orbit-controls'

export default {
  name: 'video360',
  data() {
    const width = 600 //window.innerWidth,
    const height = 400 // window.innerHeight;
    const scene = new THREE.Scene()
    const geometry = new THREE.SphereGeometry(5, 60, 40)
    const video360 = document.createElement('video')
    video360.id = 'video360'
    const texture = new THREE.VideoTexture(video360)
    const material = new THREE.MeshBasicMaterial({ map: texture })
    const sphere = null
    const camera = new THREE.PerspectiveCamera(75, width / height, 1, 1000)
    const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true })

    return {
      width,
      height,
      scene,
      geometry,
      video360,
      texture,
      material,
      sphere,
      camera,
      renderer,

      videoUrl: require('./ayutaya.mp4'),
      isplaying: false,
      currenttime: 0,
      controls: null,
    }
  },
  mounted() {
    (async () => {
      await this.initialize()
    })()
  },
  methods: {
    async initialize() {
      THREE.ImageUtils.crossOrigin = 'anonymous'
      THREE.crossOrigin = 'anonymous'
      // geometry
      this.geometry.scale(-100, 100, 100)
      // video
      this.video360.setAttribute('crossorigin', 'anonymous')
      this.video360.width = this.width
      this.video360.height = this.height
      this.video360.src = this.videoUrl
      this.video360.load()
      // texture
      this.texture.minFilter = THREE.LinearFilter
      // sphere
      this.sphere = new THREE.Mesh(this.geometry, this.material)
      this.scene.add(this.sphere)
      // camera
      this.camera.position.set(0, 0, 0.1)
      this.camera.lookAt(this.sphere.position)
      // renderer
      this.renderer.setSize(this.width, this.height)
      this.renderer.setClearColor({ color: 0x000000 })

      // video player
      document
        .getElementById('video360player')
        .appendChild(this.renderer.domElement)
      this.renderer.render(this.scene, this.camera)
      // 3d mouse controls
      const OrbitControls = controls(THREE)
      this.controls = new OrbitControls(this.camera)

      this.render()
    },
    render() {
      requestAnimationFrame(this.render)
      this.renderer.render(this.scene, this.camera)
      this.controls.update()
      this.currenttime = this.video360.currentTime
    },
    async playVideo() {
      if (!this.video360.src) {
        alert("video url not found")
        return
      }
      if (this.video360.paused) {
        try {
          this.video360.play()
          this.isplaying = true
        } catch (e) {
          console.log(e);
          alert("error occured.");
        }
      }
    },
    async pauseVideo() {
      this.video360.pause()
      this.isplaying = false
    },
    async stopVideo() {
      this.video360.pause()
      this.isplaying = false
      this.video360.currentTime = 0
      this.controls.activate
    },
    async changeURL() {
      console.log('change', this.videoUrl)
      this.video360.src = this.videoUrl
      this.video360.load()
      this.stopVideo()
    },
  },
}
</script>

```


# 完成

いけました。
動画URL変更して再生もできました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431932/08d264a0-25a2-108f-2c2e-6cfd4dba561a.png)

# メモ

videoのplay/pause/stopはasyncじゃないとできない模様。
data()のところで初期化しないといけないもの、init()で初期化しないといけないもの、まだ判別できない。
three.js自体のサンプルソースは大量に転がってるのに、three.js × vue.js はあんまり出てこない。
vue.jsでthree.jsの場合、JavaScriptのソースを自分でvue.jsに当てはめて試しながらやらなきゃいけなくて大変。
これComponent集に追加できないかな。