120 lines
3.2 KiB
Vue
120 lines
3.2 KiB
Vue
<template>
|
|
<div class="aplayer-lrc">
|
|
<div
|
|
class="aplayer-lrc-contents"
|
|
:style="transformStyle"
|
|
>
|
|
<p
|
|
v-for="(line, index) of lrcLines"
|
|
:key="index"
|
|
:class="{ 'aplayer-lrc-current': index === currentLineIndex }"
|
|
>
|
|
{{ line[1] }}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script lang="ts" setup>
|
|
import { computed, watch, ref } from 'vue'
|
|
import { parseLrc } from '../utils'
|
|
import { StatType } from '@/model/api/music'
|
|
|
|
const props = defineProps<{
|
|
lrcSource: string
|
|
playStat: StatType
|
|
}>()
|
|
|
|
const displayLrc = ref('')
|
|
const currentLineIndex = ref(0)
|
|
|
|
const lrcLines = computed(() => parseLrc(displayLrc.value))
|
|
|
|
const transformStyle = computed(() => {
|
|
return {
|
|
transform: `translateY(${-currentLineIndex.value * 16}px)`,
|
|
webkitTransform: `translateY(${-currentLineIndex.value * 16}px)`,
|
|
}
|
|
})
|
|
|
|
watch(() => props.lrcSource, (lrcSource: string) => {
|
|
currentLineIndex.value = 0
|
|
if (/^https?:\/\//.test(lrcSource)) {
|
|
fetch(lrcSource)
|
|
.then(response => response.text())
|
|
.then(lrc => displayLrc.value = lrc)
|
|
} else {
|
|
displayLrc.value = lrcSource
|
|
}
|
|
}, { immediate: true })
|
|
|
|
watch(() => props.playStat.playedTime, (playedTime: number) => {
|
|
for (let i = 0; i < lrcLines.value.length; i++) {
|
|
const line = lrcLines.value[i]
|
|
const nextLine = lrcLines.value[i + 1]
|
|
if (playedTime >= line[0] && (!nextLine || playedTime < nextLine[0])) {
|
|
currentLineIndex.value = i
|
|
}
|
|
}
|
|
})
|
|
</script>
|
|
<style lang="less" scoped>
|
|
@import "../less/variables";
|
|
.aplayer-lrc {
|
|
position: relative;
|
|
height: @lrc-height;
|
|
text-align: center;
|
|
overflow: hidden;
|
|
margin-bottom: 7px;
|
|
&:before {
|
|
position: absolute;
|
|
top: 0;
|
|
z-index: 1;
|
|
display: block;
|
|
overflow: hidden;
|
|
width: 100%;
|
|
height: 10%;
|
|
content: ' ';
|
|
background: -moz-linear-gradient(top, rgba(255, 255, 255, 1) 0%, rgba(255, 255, 255, 0) 100%);
|
|
background: -webkit-linear-gradient(top, rgba(255, 255, 255, 1) 0%, rgba(255, 255, 255, 0) 100%);
|
|
background: linear-gradient(to bottom, rgba(255, 255, 255, 1) 0%, rgba(255, 255, 255, 0) 100%);
|
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#00ffffff', GradientType=0);
|
|
}
|
|
&:after {
|
|
position: absolute;
|
|
bottom: 0;
|
|
z-index: 1;
|
|
display: block;
|
|
overflow: hidden;
|
|
width: 100%;
|
|
height: 33%;
|
|
content: ' ';
|
|
background: -moz-linear-gradient(top, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.8) 100%);
|
|
background: -webkit-linear-gradient(top, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.8) 100%);
|
|
background: linear-gradient(to bottom, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.8) 100%);
|
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00ffffff', endColorstr='#ccffffff', GradientType=0);
|
|
}
|
|
p {
|
|
font-size: 12px;
|
|
color: #666;
|
|
line-height: 16px;
|
|
height: 16px;
|
|
padding: 0;
|
|
margin: 0;
|
|
transition: all 0.5s ease-out;
|
|
opacity: 0.4;
|
|
overflow: hidden;
|
|
&.aplayer-lrc-current {
|
|
opacity: 1;
|
|
overflow: visible;
|
|
height: initial;
|
|
}
|
|
}
|
|
.aplayer-lrc-contents {
|
|
width: 100%;
|
|
transition: all 0.5s ease-out;
|
|
user-select: text;
|
|
cursor: default;
|
|
}
|
|
}
|
|
</style> |