実装結果
git映像
実装サイトURL
実装コード
- ディレクトリ
src
├── App.vue
├── assets
│   └── scss
│       └── main.scss
├── components
│   ├── MovieButton.vue
│   └── MovieModal.vue
├── main.js
└── plugins
    └── vue-youtube.js
- バージョン
"@vue/cli": "4.4.6",
"vue": "^2.6.11",
"vue-youtube": "^1.4.0"
コードの内容を見る
- src/App.vue
<template>
  <div id="app">
    <movie-button
      :btn-num="1"
      :movie-id="movieData[0]"
      @modal-open="modalOpen($event)"
    />
    <movie-button
      :btn-num="2"
      :movie-id="movieData[1]"
      @modal-open="modalOpen($event)"
    />
    <movie-button
      :btn-num="3"
      :movie-id="movieData[2]"
      @modal-open="modalOpen($event)"
    />
    <movie-modal
      :modal-content="modalContent"
      :is-open="isOpen"
      @modal-close="modalClose()"
    />
  </div>
</template>
<script>
import MovieButton from "./components/MovieButton";
import MovieModal from "./components/MovieModal";
export default {
  name: "App",
  components: {
    MovieButton,
    MovieModal
  },
  data() {
    return {
      isOpen: false,
      modalContent: {},
      movieData: ["r8bECyGsw6Q", "2MqjzMeD3Uo", "4Vsi174LRgg"]
    };
  },
  methods: {
    modalOpen(event) {
      console.log(event);
      this.modalContent = event;
      this.isOpen = true;
    },
    modalClose() {
      this.isOpen = false;
    }
  }
};
</script>
<style lang="scss">
# app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>
- src/components/MovieButton.vue
<template>
  <transition name="fade" mode="out-in">
    <div class="modal" v-if="isOpen">
      <div class="modal__overlay" @click="onClick()"></div>
      <div class="modal__body">
        <p>モーダル</p>
        <div class="youtube__wrapper">
          <youtube :video-id="modalContent.movieId" ref="youtube"></youtube>
        </div>
        <button type="button" @click="onClick()">ボタン閉じる</button>
      </div>
    </div>
  </transition>
</template>
<script>
export default {
  props: {
    modalContent: {
      type: Object
    },
    isOpen: {
      type: Boolean
    }
  },
  methods: {
    onClick() {
      this.$emit("modal-close");
    }
  }
};
</script>
<style lang="scss" scoped>
.modal {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}
.modal__overlay {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: #0008;
}
.modal__body {
  position: relative;
  top: 50%;
  right: 0;
  bottom: 0;
  left: 0;
  max-width: 1000px;
  margin: auto;
  background: #fff;
  transform: translateY(-50%);
}
.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.3s;
}
.fade-enter,
.fade-leave-to {
  opacity: 0;
}
</style>
- src/components/MovieModal.vue
<template>
  <button type="button" @click="onClick()">動画{{ btnNum }}</button>
</template>
<script>
export default {
  props: {
    btnNum: {
      default: 0,
      type: Number
    },
    movieId: {
      type: String
    }
  },
  methods: {
    onClick() {
      this.$emit("modal-open", {
        movieId: this.movieId
      });
    }
  }
};
</script>
<style lang="scss" scoped></style>
- src/plugins/vue-youtube.js
import Vue from "vue";
import VueYoutube from "vue-youtube";
Vue.use(VueYoutube);
- src/main.js
import Vue from "vue";
import App from "./App.vue";
import "./plugins/vue-youtube.js";
Vue.config.productionTip = false;
require("@/assets/scss/main.scss");
new Vue({
  render: h => h(App)
}).$mount("#app");
- src/assets/main.scss
.youtube__wrapper {
  position: relative;
  width: 100%;
  margin: 0 auto;
  height: 0;
  padding-bottom: 56.25%;
  overflow: hidden;
  background: #aaa;
  iframe {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
  }
}
実装手順
1. vue-youtubeインストール
$ npm install vue-youtube // or yarn add vue-youtube
2. vue-youtube記載
- src/plugins/vue-youtube.js
import Vue from "vue";
import VueYoutube from "vue-youtube";
Vue.use(VueYoutube);
- src/main.js
import "./plugins/vue-youtube.js";
※Nuxt.jsの場合、pluginsに記載
export default{
  plugins: ["~plugins/vue-youtube.js"]
}
参考リンク
3. ボタン追加
- src/components/MovieButton.vue
<template>
  <button type="button" @click="onClick()">動画{{ btnNum }}</button>
</template>
<script>
export default {
  props: {
    btnNum: {
      default: 0,
      type: Number
    },
    movieId: {
      type: String
    }
  },
  methods: {
    onClick() {
      this.$emit("modal-open", {
        movieId: this.movieId
      });
    }
  }
};
</script>
<style lang="scss" scoped></style>
- src/App.vue
<template>
  <div id="app">
    <movie-button
      :btn-num="1" 
      :movie-id="movieData[0]"
      @modal-open="modalOpen($event)"
    />
    <movie-button
      :btn-num="2"
      :movie-id="movieData[1]"
      @modal-open="modalOpen($event)"
    />
    <movie-button
      :btn-num="3"
      :movie-id="movieData[2]"
      @modal-open="modalOpen($event)"
    />
  </div>
</template>
<script>
import MovieButton from "./components/MovieButton";
export default {
  components: {
    MovieButton
  },
  data() {
    return {
      isOpen: false,
      modalContent: {},
      movieData: ["r8bECyGsw6Q", "2MqjzMeD3Uo", "4Vsi174LRgg"]
    };
  },
  methods: {
    modalOpen(event) {
      this.modalContent = event;
      this.isOpen = true;
    }
  }
};
</script>
movie-buttonについて
- 
btn-num
 ボタンの番号を識別させるためのもの(今回の実装では必要なし)
- 
movie-id
 動画のidで動画の内容を識別させるためのもの(管理しやすくするため、data内のmovieDataに登録)
- 
@modal-open="modalOpen($event)"
 子コンポーネントのクリックイベントを検知して、実行(モーダルを開くためのもの)
参考
4. モーダル実装
- src/components/MovieModal.vue
<template>
  <transition name="fade" mode="out-in">
    <div class="modal" v-if="isOpen">
      <div class="modal__overlay" @click="onClick()"></div>
      <div class="modal__body">
        <p>モーダル</p>
        <div class="youtube__wrapper">
          <youtube :video-id="modalContent.movieId" ref="youtube"></youtube>
        </div>
        <button type="button" @click="onClick()">ボタン閉じる</button>
      </div>
    </div>
  </transition>
</template>
<script>
export default {
  props: {
    modalContent: {
      type: Object
    },
    isOpen: {
      type: Boolean
    }
  },
  methods: {
    onClick() {
      this.$emit("modal-close");
    }
  }
};
</script>
<style lang="scss" scoped>
.modal {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}
.modal__overlay {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: #0008;
}
.modal__body {
  position: relative;
  top: 50%;
  right: 0;
  bottom: 0;
  left: 0;
  max-width: 1000px;
  margin: auto;
  background: #fff;
  transform: translateY(-50%);
}
.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.3s;
}
.fade-enter,
.fade-leave-to {
  opacity: 0;
}
</style>
- src/App.vue
<template>
  <div id="app">
    <movie-modal
      :modal-content="modalContent"
      :is-open="isOpen"
      @modal-close="modalClose()"
    />
  </div>
</template>
<script>
import MovieModal from "./components/MovieModal";
export default {
  name: "App",
  components: {
    MovieModal
  },
  data() {
    return {
      isOpen: false,
      modalContent: {},
      movieData: ["r8bECyGsw6Q", "2MqjzMeD3Uo", "4Vsi174LRgg"]
    };
  },
  methods: {
    modalClose() {
      this.isOpen = false;
    }
  }
};
</script>
- src/assets/main.scss
.youtube__wrapper {
  position: relative;
  width: 100%;
  margin: 0 auto;
  height: 0;
  padding-bottom: 56.25%;
  overflow: hidden;
  background: #aaa;
  iframe {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
  }
}
movie-modalについて
- 
modal-content
 モーダルに送る情報(今回はmovieIdのみ)
- 
is-open
 モーダルが開いているか判定させるもの
- 
@modal-close="modalClose()"
 子コンポーネントのクリックイベントを検知して、実行(モーダルを閉じるためのもの)
参考リンク
+α
1. モーダルを開いたときに自動で再生を行う
PCのみ自動再生を行いたい場合 autoplay = 1を追加
<template>
  <youtube
    :video-id="modalContent.movieId"
    ref="youtube"
    :player-vars="playerVars"
  ></youtube>
</template>
<script>
  export default {
    data() {
      return {
        playerVars: {
          autoplay: 1
        }
      };
    },
  }
</script>
参考リンク
autoplay = 1のみだとSPなどのデバイスで対応することができないため、SPでも対応させたい場合、無音でインライン再生に変更を行うことで自動再生を行うことができる。
<template>
  <youtube
    :video-id="modalContent.movieId"
    ref="youtube"
    :player-vars="playerVars"
    @ready="ready"
  ></youtube>
</template>
<script>
export default {
  data() {
    return {
      playerVars: {
        playsinline: 1
      }
    };
  },
  methods: {
    async fetchYoutube() {
      await (this.isOpen = ture);
      this.$refs.youtube.fetchData();
    },
    ready() {
      const youtubePlayer = this.$refs.youtube.player
      youtubePlayer.mute()
      youtubePlayer.playVideo()
    }
  }
};
</script>
参考リンク
- events - vue-youtube - npm
- iframe 組み込みの YouTube Player API リファレンス | YouTube IFrame Player API
- パラメータ一覧 - iframe 組み込みの YouTube Player API リファレンス
- スマホ(iOS)でも動画を自動再生させよう!(iFrame Player API、videoタグ対応) | 株式会社 エヴォワークス -EVOWORX-
2. 字幕を自動で追加を行う
cc_lang_pref = 1に設定を行うことで字幕を追加できる
 
export default {
  data() {
    return {
      playerVars: {
        cc_lang_pref: 1 // cc_lang_prefを1に行う
      }
    };
  },
}
参考リンク
詰まった部分について
1. youtubeのスタイルが一部ずれる
- vueコンポーネント内に
.youtube__wrapper {
  position: relative;
  width: 100%;
  margin: 0 auto;
  height: 0;
  padding-bottom: 56.25%;
  overflow: hidden;
  background: #aaa;
  iframe {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
  }
}
のような記載を行うと iframe内のコンポーネントにスタイルが当たらないため以下のような状態になる。

また、スタイルを当てていないとモーダルを閉じる時、動画の高さがなくなり、以下のような状態になる。
 
なので、グローバルcssの場所に以下を記載
.youtube__wrapper {
  position: relative;
  width: 100%;
  margin: 0 auto;
  height: 0;
  padding-bottom: 56.25%;
  overflow: hidden;
  background: #aaa;
  iframe {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
  }
}
参考リンク
2. v-ifを用いるとthis.$refsが取得できない
v-ifを用いるとコンポーネントが描画されていない状態でthis.$refsの内容を取得しようとするので、取得できないエラーになる。
なので、async/awaitを用いることになり、コンポーネント描画 → $refs取得を行うことができるようにする
async fetchYoutube() {
  await (this.isOpen = ture); // isOpenフラグがtrueになったら
  this.$refs.youtube.fetchData(); // this.$refs.youtubeの取得を行う
},
参考リンク
3. モーダルが上下中央によらない
.modal__body {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  max-width: 1000px;
  margin: auto;
  background: #fff;
}
のような状態で配置した場合、
 
のような状態で、高さが取得されないため、 position: relativeと top: 50%などで変更を行う
.modal__body {
  position: relative; /* relativeに変更 */
  top: 50%; /* 50%に変更 */
  right: 0;
  bottom: 0;
  left: 0;
  max-width: 1000px;
  margin: auto;
  background: #fff;
  transform: translateY(-50%); /* -50%を追加 */
}

