hw-gs-player

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

commit 67d1eb889e25b11b9832a001c0ee8f6e7367dfa0
parent 2948a5bcd54f7c4da2158962de71582d670764b1
Author: Sunny <122193933+Sunny-JP@users.noreply.github.com>
Date:   Sat, 15 Mar 2025 21:36:40 +0900

Merge pull request #7 from Sunny-JP/sub-2

add playback action
Diffstat:
Mpackage-lock.json | 21+++++++++++++++++++++
Mpackage.json | 1+
Msrc/App.vue | 1+
Msrc/components/Player.vue | 98++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
Msrc/style.css | 76++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
5 files changed, 173 insertions(+), 24 deletions(-)

diff --git a/package-lock.json b/package-lock.json @@ -8,6 +8,7 @@ "name": "gs-player", "version": "0.0.0", "dependencies": { + "@vue-youtube/component": "^0.0.6", "@vue-youtube/core": "^0.0.6", "vue": "3.5.13", "vue-router": "^4.5.0" @@ -813,6 +814,26 @@ "vscode-uri": "^3.0.8" } }, + "node_modules/@vue-youtube/component": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@vue-youtube/component/-/component-0.0.6.tgz", + "integrity": "sha512-Ouv7VJy8r//F9gE8Y1H2M0d4IQmOtZh8I79ttgsAGge3a/AdtXzSBVJ7ijhCAH4ZNulkhsN3CO46V/ReW5mwKA==", + "license": "MIT", + "dependencies": { + "@vue-youtube/core": "0.0.6", + "@vue-youtube/shared": "0.0.6", + "vue-demi": "*" + }, + "peerDependencies": { + "@vue/composition-api": "^1.7.2", + "vue": "^2.7.0 || >=3.0.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, "node_modules/@vue-youtube/core": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/@vue-youtube/core/-/core-0.0.6.tgz", diff --git a/package.json b/package.json @@ -9,6 +9,7 @@ "preview": "vite preview" }, "dependencies": { + "@vue-youtube/component": "^0.0.6", "@vue-youtube/core": "^0.0.6", "vue": "3.5.13", "vue-router": "^4.5.0" diff --git a/src/App.vue b/src/App.vue @@ -35,6 +35,7 @@ onBeforeUnmount(() => { <!-- 将来的には多言語対応する.中,韓ではそれぞれNoto sans C,Kを用いる --> <li><router-link to="/" @click="MenuButton = false">Top</router-link></li> <li><router-link to="/" @click="MenuButton = false">My List</router-link></li> + <li><router-link to="/player" @click="MenuButton = false">Player(仮置き)</router-link></li> <hr> <li><router-link to="/about" @click="MenuButton = false">About</router-link></li> <li><router-link to="/terms" @click="MenuButton = false">利用規約</router-link></li> diff --git a/src/components/Player.vue b/src/components/Player.vue @@ -1,30 +1,92 @@ <script setup lang="ts"> -import { usePlayer } from '@vue-youtube/core'; -import { ref } from 'vue'; +import { YoutubeIframe } from '@vue-youtube/component'; +import { ref, computed, onUnmounted } from 'vue'; -const videoId = ref('AvPzD7TE8oA'); -const youtube = ref(); +const videoId = ref('1QkKDY1-NN0'); +const player = ref<any>(null); +const nowTime = ref(0); +const endTime = ref(0); +const title = ref(); +const titleName = ref(); +const intervalId = ref<number | null>(null); +const isPlaying = ref(false); -const { onReady } = usePlayer(videoId, youtube, { - cookie: false, - playerVars: { - mute: 1, - }, -}); +const togglePlay = async () => { + player.value?.togglePlay(); // change play-pause +}; + +// Get total time and video title +const onReady = (event: { target: any }) => { + endTime.value = Math.floor(event.target.getDuration()); + title.value = event.target.getVideoData(); + titleName.value = title.value['title']; +}; + +// When the playback state is changed +const onStateChange = (event: { target: any; data: number }) => { + if (event.data === 1) { // start + isPlaying.value = true; // play-pause-button + intervalId.value = window.setInterval(() => { // Get current time per 250ms + nowTime.value = Math.floor(event.target.getCurrentTime()); + }, 250); + } else if (event.data === 2 || event.data === 0) { // stop (2) or end (0) + isPlaying.value = false; // play-pause-button + if (intervalId.value !== null) { + clearInterval(intervalId.value); + intervalId.value = null; + } + } +}; + +// Format to mm:ss +const formatTime = (time: number) => { + const minutes = Math.floor(time / 60); + const seconds = time % 60; + return `${minutes}:${seconds.toString().padStart(2, '0')}`; +}; + +// Get formatted time with computed +const formattedNowTime = computed(() => formatTime(nowTime.value)); +const formattedEndTime = computed(() => formatTime(endTime.value)); -onReady((event) => { - event.target.playVideo(); +// Clear setInterval when component is destroyed +onUnmounted(() => { + if (intervalId.value !== null) { + clearInterval(intervalId.value); + } }); </script> <template> <div class="container"> - <h1>Top</h1> - <div ref="youtube"></div> - <div class="title">title</div> - <div class="artist">artist</div> - <div class="nowtime">2:13</div> - <div class="endtime">5:34</div> + <youtube-iframe + :video-id="videoId" + :player-vars="{ + autoplay: 1, + iv_load_policy: 3, + loop: 1, + playlist: videoId, + controls: 0, + playsinline: 1, + rel: 0 + }" + ref="player" + @ready="onReady" + @state-change="onStateChange" /> + <div class="info_box"> + <div class="title">{{ titleName }}</div> + <div class="album">album name</div> + <div class="time_container"> + <div class="nowtime">{{ formattedNowTime }}</div> + <div class="endtime">{{ formattedEndTime }}</div> + </div> + </div> + <button @click="togglePlay" class="play-pause-button"> + <div class="play-pause-icon" :class="{ playing: isPlaying }"> + <div class="bar bar-left"></div> + <div class="bar bar-right"></div> + </div> + </button> </div> </template> diff --git a/src/style.css b/src/style.css @@ -64,6 +64,7 @@ body { .hamburger_btn .line_02 { top: 26px; + height: 2.5px; } .hamburger_btn .line_03 { @@ -135,20 +136,83 @@ body { iframe { margin: 10px; width: 80%; - height: 60%; + height: 100%; max-width: 500px; aspect-ratio: 1/1; } +.info_box { + margin: 5px; + width: 80%; + max-width: 500px; +} + .title { - font-size: 30px; + font-size: 1.8rem; } -.artist { - font-size: 16px; +.album { + font-size: 1.1rem; +} + +.time_container { + display: flex; + justify-content: flex-start; + align-items: center; + margin-top: 5px; + font-size: 0.8rem; } -.nowtime, .endtime { - font-size: 13px; + margin-left: auto; + text-align: right; +} + +/* control button */ +/* play-pause button */ +.play-pause-button { + background: none; + border: none; + padding-top: 50px; + padding-left: 10px; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + width: 100%; + height: 60px; +} + +.play-pause-icon { + position: relative; + width: 36px; + height: 40px; +} + +.bar { + position: absolute; + background: white; + width: 17.3px; + height: 40px; + transition: all 0.3s ease-in-out; +} + +.bar-left { + left: 1px; + clip-path: polygon(0% 0%, 100% 26%, 100% 74%, 0% 100%); +} + +.bar-right { + right: 1px; + clip-path: polygon(0% 25%, 100% 50%, 0% 75%); +} + +.playing .bar-left { + clip-path: inset(0 0 0 0); + width: 10px; +} + +.playing .bar-right { + clip-path: inset(0 0 0 0); + width: 10px; } \ No newline at end of file