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:
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