基本完成歌曲详情

This commit is contained in:
zilong 2021-10-28 22:58:30 +08:00
parent 2701d0cfe7
commit 25af6cfedb
6 changed files with 219 additions and 35 deletions

View File

@ -22,6 +22,7 @@
"core-js": "^3.6.5", "core-js": "^3.6.5",
"dayjs": "^1.10.7", "dayjs": "^1.10.7",
"element-plus": "^1.1.0-beta.19", "element-plus": "^1.1.0-beta.19",
"lodash": "^4.17.21",
"pubsub-js": "^1.9.3", "pubsub-js": "^1.9.3",
"unplugin-vue-components": "^0.15.6", "unplugin-vue-components": "^0.15.6",
"vue": "^3.2.16", "vue": "^3.2.16",

View File

@ -228,7 +228,7 @@ body {
} }
} }
#wpSongDetail { #wpSongDetail {
z-index: 100;
} }
#wpPlayingList { #wpPlayingList {
// position: absolute; // position: absolute;

View File

@ -0,0 +1,27 @@
<script setup>
import { ref, defineProps, onUnmounted, onDeactivated } from "vue";
import { useRouter } from "vue-router";
const props = defineProps({
album: Object,
onLeave: Function,
});
const router = useRouter();
// console.log(props.artists);
const click = (id) => {
props.onLeave?.();
router.push("/album/" + id);
};
</script>
<template>
<span
@click="click(album.id)"
style="margin-right: 4px; cursor: pointer"
>{{ album.name }}</span
>
</template>
<script></script>
<style></style>

View File

@ -2,14 +2,17 @@
import { ref, defineProps, onUnmounted, onDeactivated } from "vue"; import { ref, defineProps, onUnmounted, onDeactivated } from "vue";
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
const props = defineProps({ artists: Array, onLeave: Function }); const props = defineProps({
const router = useRouter() artists: Array,
onLeave: Function,
});
const router = useRouter();
// console.log(props.artists); // console.log(props.artists);
const click = (id) => { const click = (id) => {
props.onLeave?.() props.onLeave?.();
router.push('/singer/' + id) router.push("/singer/" + id);
} };
</script> </script>
<template> <template>
<template v-for="(ar, idx) of artists" key="idx"> <template v-for="(ar, idx) of artists" key="idx">
@ -22,9 +25,7 @@ const click = (id) => {
</template> </template>
<script> <script>
// export default {
// props: ["artists"],
// };
</script> </script>
<style></style> <style></style>

View File

@ -1,42 +1,105 @@
<script setup> <script setup>
import { ref, onUnmounted, watch } from "vue"; import { ref, onUnmounted, watch, toRaw, nextTick } from "vue";
import { NButton, NIcon } from "naive-ui"; import { NButton, NIcon } from "naive-ui";
import { useStore } from "vuex"; import { useStore } from "vuex";
import svgChevrongDown from "@/assets/svgs/ChevronDown.svg"; import svgChevrongDown from "@/assets/svgs/ChevronDown.svg";
import pubsub from "pubsub-js"; import pubsub from "pubsub-js";
import ArtistsSpan from "@/components/ArtistsSpan.vue";
import AlbumSpan from "@/components/AlbumSpan.vue";
import _ from 'lodash';
const store = useStore(); const store = useStore();
const songInfo = ref(null); const song = ref(null);
const lyric = ref(null); const lyric = ref(null);
const lyArr = ref([]);
const coverAngle = ref(0); const coverAngle = ref(0);
const currTime = ref(0);
const totalTime = ref(0);
const currentLyricLine = ref(false);
const currentLyricIdx = ref(-1);
const lyList = ref('')
let interval; //#region
let interval = [];
watch( watch(
() => store.state.settings.playing, () => [store.state.settings.playing, store.state.showSongDetail],
(val) => { ([playing, showSongDetail]) => {
if(val){ if (playing && showSongDetail) {
interval = setInterval(() => { interval.push(
coverAngle.value += .5 setInterval(() => {
}, 100); coverAngle.value += 0.5;
}, 40)
);
} else { } else {
clearInterval(interval) interval.map((item) => clearInterval(item));
interval = [];
} }
}, },
{ immediate: true } { immediate: true }
); );
//#endregion
//#region
//
const handleLyric = (ly) => {
lyArr.value = [];
const lines = ly.split("\n");
const re = /^\[(\d+):(\d+(.\d+)?)\](.*)$/;
lines.forEach((line) => {
// console.log(line);
// let match = re.exec(line)
// console.log(match);
// console.log(Number(line.replace(re,'$1')))
if (re.test(line))
lyArr.value.push({
time:
Number(line.replace(re, "$1")) * 60 * 1000 +
Number(line.replace(re, "$2")) * 1000,
lyric: line.replace(re, "$4"),
origin: line,
});
});
// lyList.value.scrollTop = 0;
// console.log(lyArr.value);
};
watch(
()=> currentLyricIdx.value,
(val)=>{
if(val>0){
// let elContent = document.getElementsByClassName('ly-content')[0]
let el = document.getElementsByClassName('ly-key-lines')[val]
lyList.value.scrollTop = el.offsetTop - 100
}
},
{
immediate: true,
}
)
//#endregion
//#region //#region
const token = pubsub.subscribe("zp", (msg, data) => { const token = pubsub.subscribe("zp", (msg, data) => {
switch (msg) { switch (msg) {
case "zp.songInfo": case "zp.songInfo":
console.log("SongDetail: 收到歌曲详细信息。", data); // console.log("SongDetail: ", data);
songInfo.value = data; song.value = data;
break; break;
case "zp.lyric": case "zp.lyric":
lyric.value = data; lyric.value = data;
handleLyric(data);
break;
case "zp.progress":
totalTime.value = data.total * 1000;
currTime.value = data.progress * 1000;
currentLyricIdx.value = _.findLastIndex(lyArr.value,(item) => {
return item.time < currTime.value;
});
// console.log(currentLyricIdx.value);
break; break;
} }
}); });
@ -61,8 +124,9 @@ onUnmounted(() => {
</template> </template>
</n-button> </n-button>
</div> </div>
<div id="sdContent" v-if="songInfo"> <div id="sdContent" v-if="song">
<div class="detail"> <div class="detail">
<!-- 唱机 -->
<div class="disk"> <div class="disk">
<div class="styli"> <div class="styli">
<img <img
@ -74,21 +138,58 @@ onUnmounted(() => {
<img class="disk" src="@/assets/images/disk.png" /> <img class="disk" src="@/assets/images/disk.png" />
<img <img
class="cover" class="cover"
:src="songInfo.album.picUrl" :src="song.album.picUrl"
:style="{ transform: 'rotateZ(' + coverAngle + 'deg)' }" :style="{ transform: 'rotateZ(' + coverAngle + 'deg)' }"
/> />
</div> </div>
<div class="pic"></div> <div class="pic"></div>
</div> </div>
<div class="song"> <div class="song">
歌曲 <div class="name">{{ song.name }}</div>
<div class="name"></div> <div class="alias">
<div class="others"></div> {{ song.alias.length > 0 ? song.alias.join(" ") : "" }}
<div class="ly"></div> </div>
<div class="others">
<div class="album">
专辑
<AlbumSpan
:album="song.album"
:onLeave="
() => {
pubsub.publish('zp.toggleSongDetail');
}
"
/>
</div>
<div class="artists">
歌手
<ArtistsSpan
:artists="song.artists"
:onLeave="
() => {
pubsub.publish('zp.toggleSongDetail');
}
"
/>
</div>
</div>
<div class="ly" v-if="lyric" ref="lyList">
<div class="ly-content" >
<div
class="ly-line"
v-for="(line, idx) in lyArr"
key="idx"
:class="{ current: currentLyricIdx == idx, ['ly-key-lines']: true, }"
>
{{ line.lyric }}
</div>
</div>
<div class="ly-right"></div>
</div> </div>
</div> </div>
<div class="comments"></div> <div class="comments"></div>
</div> </div>
</div>
</template> </template>
<script> <script>
@ -115,6 +216,7 @@ export default {};
right: 0; right: 0;
bottom: 64px; bottom: 64px;
background-color: #f6f6f6; background-color: #f6f6f6;
overflow-y: auto;
.styli { .styli {
position: relative; position: relative;
@ -138,7 +240,7 @@ export default {};
height: 50px; height: 50px;
} }
.disk { .disk {
--back-color: #eee; --back-color: #ddd;
--width-num: 340; --width-num: 340;
--padding-num: 10; --padding-num: 10;
--width: calc(var(--width-num) * 1px); --width: calc(var(--width-num) * 1px);
@ -170,7 +272,57 @@ export default {};
} }
.song { .song {
position: relative; position: relative;
width: 400px; width: 330px;
margin-left: 30px;
font-size: 14px;
color: #333;
.name {
font-size: 24px;
font-weight: 500;
margin-top: 10px;
}
.others {
// display: flex;
font-size: 12px;
color: #999;
.album {
flex: 1;
}
.artists {
flex: 1;
}
}
.ly {
position: relative;
margin-top: 15px;
height: 290px;
overflow-y: auto;
// background-image : linear-gradient(180deg,hsla(255,0%,100%,0),rgba(255, 255, 255, 0.521));
.ly-content {
font-size: 14px;
color: #999;
margin-bottom: 100px;
// display: flex;
// flex-direction: column;
// justify-items: center;
.ly-line {
min-height: 32px;
display: flex;
align-items: center;
}
.current {
font-size: 18px;
font-weight: 600;
color: #333;
}
}
}
} }
} }
} }

View File

@ -24,6 +24,7 @@ const info = reactive({
name: "", name: "",
artists: [], artists: [],
album: {}, album: {},
alias: [],
duration: 0, duration: 0,
mv: 0, mv: 0,
}); });
@ -33,9 +34,11 @@ const songInfo = (id, im) => {
getSongDetial(id) getSongDetial(id)
.then((res) => { .then((res) => {
showInfo.value = true; showInfo.value = true;
console.log('SongInfo: 获取歌曲详细信息',res.data.songs[0]);
info.name = res.data.songs[0].name; info.name = res.data.songs[0].name;
info.artists = res.data.songs[0].ar; info.artists = res.data.songs[0].ar;
info.album = res.data.songs[0].al; info.album = res.data.songs[0].al;
info.alias = res.data.songs[0].alia;
info.duration = res.data.songs[0].dt; info.duration = res.data.songs[0].dt;
info.mv = res.data.songs[0].mv; info.mv = res.data.songs[0].mv;
currTime.value = "00:00"; currTime.value = "00:00";
@ -105,9 +108,9 @@ onUnmounted(() => {
//#endregion //#endregion
const toggleSongDetail = () => { const toggleSongDetail = () => {
console.log( // console.log(
"显示/隐藏歌曲详细界面,包括歌词、模拟唱机以及其他操作。" // "/"
); // );
pubsub.publish("zp.toggleSongDetail"); pubsub.publish("zp.toggleSongDetail");
}; };
</script> </script>