|
|
@ -1,10 +1,8 @@ |
|
|
|
<template> |
|
|
|
<view class="container"> |
|
|
|
<!-- 顶部标题栏 --> |
|
|
|
<!-- 主要内容区域 --> |
|
|
|
<!-- 文本显示区域 --> |
|
|
|
<!-- 歌词显示区域 --> |
|
|
|
<scroll-view |
|
|
|
class="content" |
|
|
|
class="lyrics-container" |
|
|
|
:show-scrollbar="false" |
|
|
|
enhanced |
|
|
|
scroll-y |
|
|
@ -12,33 +10,39 @@ |
|
|
|
scroll-with-animation |
|
|
|
:style="{ height: scrollViewHeight + 'px' }" |
|
|
|
> |
|
|
|
<view class="content-wrapper"> |
|
|
|
<view class="article-content"> |
|
|
|
<text |
|
|
|
v-for="(sentence, index) in currentChapter.sentences" |
|
|
|
:key="index" |
|
|
|
:id="`sentence-${index}`" |
|
|
|
class="sentence" |
|
|
|
:class="{ active: index === activeSentenceIndex }" |
|
|
|
> |
|
|
|
{{ sentence.FinalSentence }} |
|
|
|
</text> |
|
|
|
</view> |
|
|
|
<view class="lyrics-content"> |
|
|
|
<text |
|
|
|
v-for="(sentence, index) in currentChapter.sentences" |
|
|
|
:key="index" |
|
|
|
:id="`sentence-${index}`" |
|
|
|
class="lyric-line" |
|
|
|
:class="{ active: index === activeSentenceIndex }" |
|
|
|
> |
|
|
|
{{ sentence.FinalSentence }} |
|
|
|
</text> |
|
|
|
</view> |
|
|
|
</scroll-view> |
|
|
|
<!-- 底部控制栏 --> |
|
|
|
<view class="controls"> |
|
|
|
<!-- 章节信息栏 --> |
|
|
|
<view class="chapter-info-bar"> |
|
|
|
<text class="chapter-title">{{ currentChapter.title }}</text> |
|
|
|
<text class="chapter-count"> |
|
|
|
{{ currentChapterIndex + 1 }}/{{ chapters.length }} |
|
|
|
</text> |
|
|
|
|
|
|
|
<!-- 播放控制区域 --> |
|
|
|
<view class="player-controls"> |
|
|
|
<!-- 控制按钮区域(进度条上方) --> |
|
|
|
<view class="control-icons" v-if="false"> |
|
|
|
<view class="control-icon"> |
|
|
|
<text class="icon-text">⋯</text> |
|
|
|
</view> |
|
|
|
<view class="control-icon-group"> |
|
|
|
<view class="control-icon"> |
|
|
|
<text class="icon-text">♡</text> |
|
|
|
</view> |
|
|
|
<view class="control-icon"> |
|
|
|
<text class="icon-text">🔍</text> |
|
|
|
</view> |
|
|
|
</view> |
|
|
|
</view> |
|
|
|
|
|
|
|
<!-- 进度条区域 --> |
|
|
|
<view class="progress-section"> |
|
|
|
<text class="current-time">{{ formatTime(currentTime) }}</text> |
|
|
|
<text class="time-text">{{ formatTime(currentTime) }}</text> |
|
|
|
<view class="progress-container"> |
|
|
|
<view class="progress-bar" @click="seek"> |
|
|
|
<view class="progress" :style="{ width: progress + '%' }"></view> |
|
|
@ -48,33 +52,25 @@ |
|
|
|
></view> |
|
|
|
</view> |
|
|
|
</view> |
|
|
|
<text class="duration">{{ formatTime(duration) }}</text> |
|
|
|
<text class="time-text">{{ formatTime(duration) }}</text> |
|
|
|
</view> |
|
|
|
|
|
|
|
<!-- 控制按钮区域 --> |
|
|
|
<view class="control-buttons"> |
|
|
|
<!-- 上一章按钮 --> |
|
|
|
<button |
|
|
|
class="control-btn prev-btn" |
|
|
|
@click="prevChapter" |
|
|
|
:disabled="currentChapterIndex === 0" |
|
|
|
> |
|
|
|
<text class="control-icon">⏮</text> |
|
|
|
</button> |
|
|
|
|
|
|
|
<!-- 播放/暂停按钮 --> |
|
|
|
<button class="play-btn" @click="togglePlay"> |
|
|
|
<text class="play-icon">{{ playing ? "⏸️" : "▶️" }}</text> |
|
|
|
</button> |
|
|
|
|
|
|
|
<!-- 下一章按钮 --> |
|
|
|
<button |
|
|
|
class="control-btn next-btn" |
|
|
|
@click="nextChapter" |
|
|
|
:disabled="currentChapterIndex === chapters.length - 1" |
|
|
|
> |
|
|
|
<text class="control-icon">⏭</text> |
|
|
|
</button> |
|
|
|
<!-- 底部控制按钮 --> |
|
|
|
<view class="bottom-controls"> |
|
|
|
<view class="control-btn"> |
|
|
|
<text class="control-icon-text">⟲</text> |
|
|
|
</view> |
|
|
|
<view class="control-btn" @click="prevChapter"> |
|
|
|
<text class="control-icon-text">⏮</text> |
|
|
|
</view> |
|
|
|
<view class="control-btn play-btn" @click="togglePlay"> |
|
|
|
<text class="control-icon-text">{{ playing ? "⏸" : "▶" }}</text> |
|
|
|
</view> |
|
|
|
<view class="control-btn" @click="nextChapter"> |
|
|
|
<text class="control-icon-text">⏭</text> |
|
|
|
</view> |
|
|
|
<view class="control-btn"> |
|
|
|
<text class="control-icon-text">☰</text> |
|
|
|
</view> |
|
|
|
</view> |
|
|
|
</view> |
|
|
|
|
|
|
@ -239,7 +235,7 @@ export default { |
|
|
|
|
|
|
|
if (seekTime >= 0 && seekTime <= this.duration) { |
|
|
|
this.$refs.audioPlayer.seek(seekTime); |
|
|
|
|
|
|
|
|
|
|
|
// 手动拖动后同步句子高亮和滚动位置 |
|
|
|
this.updateSentenceHighlight(seekTime); |
|
|
|
} |
|
|
@ -251,7 +247,7 @@ export default { |
|
|
|
// 根据指定时间找到对应的句子并更新高亮和滚动位置 |
|
|
|
const sentences = this.currentChapter.sentences; |
|
|
|
let targetIndex = -1; |
|
|
|
|
|
|
|
|
|
|
|
// 精确匹配:找到当前时间对应的句子 |
|
|
|
for (let i = 0; i < sentences.length; i++) { |
|
|
|
const sentence = sentences[i]; |
|
|
@ -263,14 +259,15 @@ export default { |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 如果没有精确匹配,尝试找到最接近的句子 |
|
|
|
if (targetIndex === -1 && sentences.length > 0) { |
|
|
|
// 找到时间最接近的句子,而不是简单的比例估算 |
|
|
|
let minDistance = Infinity; |
|
|
|
for (let i = 0; i < sentences.length; i++) { |
|
|
|
const sentence = sentences[i]; |
|
|
|
const sentenceMiddle = (sentence.startTimeInSeconds + sentence.endTimeInSeconds) / 2; |
|
|
|
const sentenceMiddle = |
|
|
|
(sentence.startTimeInSeconds + sentence.endTimeInSeconds) / 2; |
|
|
|
const distance = Math.abs(currentTimeSeconds - sentenceMiddle); |
|
|
|
if (distance < minDistance) { |
|
|
|
minDistance = distance; |
|
|
@ -278,12 +275,12 @@ export default { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 只有当索引真正发生变化时才更新 |
|
|
|
if (targetIndex >= 0 && targetIndex !== this.activeSentenceIndex) { |
|
|
|
const previousIndex = this.activeSentenceIndex; |
|
|
|
this.activeSentenceIndex = targetIndex; |
|
|
|
|
|
|
|
|
|
|
|
// 使用$nextTick确保DOM更新后再计算滚动位置 |
|
|
|
this.$nextTick(() => { |
|
|
|
this.scrollToSentence(targetIndex); |
|
|
@ -293,26 +290,26 @@ export default { |
|
|
|
scrollToSentence(index) { |
|
|
|
// 动态获取句子位置并滚动到可视区域 |
|
|
|
const query = uni.createSelectorQuery().in(this); |
|
|
|
|
|
|
|
|
|
|
|
// 获取目标句子的位置信息 |
|
|
|
query.select(`#sentence-${index}`).boundingClientRect(); |
|
|
|
// 获取滚动容器的位置信息 |
|
|
|
query.select('.article-content').boundingClientRect(); |
|
|
|
|
|
|
|
query.select(".lyrics-content").boundingClientRect(); |
|
|
|
|
|
|
|
query.exec((res) => { |
|
|
|
if (res && res[0] && res[1]) { |
|
|
|
const sentenceRect = res[0]; |
|
|
|
const containerRect = res[1]; |
|
|
|
|
|
|
|
|
|
|
|
// 计算句子相对于容器顶部的位置 |
|
|
|
const sentenceTop = sentenceRect.top - containerRect.top; |
|
|
|
|
|
|
|
|
|
|
|
// 计算滚动位置,让句子显示在可视区域中央 |
|
|
|
const scrollPosition = Math.max( |
|
|
|
0, |
|
|
|
sentenceTop - this.scrollViewHeight / 2 + sentenceRect.height / 2 |
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
// 更新滚动位置 |
|
|
|
this.scrollTop = scrollPosition; |
|
|
|
} else { |
|
|
@ -380,357 +377,214 @@ export default { |
|
|
|
<style> |
|
|
|
.container { |
|
|
|
height: 100vh; |
|
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
|
|
|
background: #f5f5f5; |
|
|
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; |
|
|
|
padding: 30rpx; |
|
|
|
position: relative; |
|
|
|
} |
|
|
|
|
|
|
|
/* 顶部标题栏样式 */ |
|
|
|
.header { |
|
|
|
display: flex; |
|
|
|
justify-content: space-between; |
|
|
|
align-items: center; |
|
|
|
padding: 40rpx 48rpx; |
|
|
|
background: rgba(255, 255, 255, 0.95); |
|
|
|
backdrop-filter: blur(20rpx); |
|
|
|
border-bottom: 2rpx solid rgba(255, 255, 255, 0.2); |
|
|
|
box-shadow: 0 4rpx 40rpx rgba(0, 0, 0, 0.1); |
|
|
|
} |
|
|
|
|
|
|
|
.header-content { |
|
|
|
flex: 1; |
|
|
|
} |
|
|
|
|
|
|
|
.title { |
|
|
|
font-size: 48rpx; |
|
|
|
font-weight: 700; |
|
|
|
color: #2d3748; |
|
|
|
margin-bottom: 8rpx; |
|
|
|
flex-direction: column; |
|
|
|
} |
|
|
|
|
|
|
|
.subtitle { |
|
|
|
font-size: 32rpx; |
|
|
|
color: #718096; |
|
|
|
font-weight: 500; |
|
|
|
/* 顶部标签样式 */ |
|
|
|
.top-label { |
|
|
|
display: flex; |
|
|
|
justify-content: center; |
|
|
|
padding: 40rpx 0 20rpx 0; |
|
|
|
} |
|
|
|
|
|
|
|
.chapter-info { |
|
|
|
background: linear-gradient(135deg, #667eea, #764ba2); |
|
|
|
padding: 16rpx 32rpx; |
|
|
|
.label-content { |
|
|
|
background: #e8e8e8; |
|
|
|
border: 2rpx solid #d0d0d0; |
|
|
|
border-radius: 40rpx; |
|
|
|
box-shadow: 0 8rpx 24rpx rgba(102, 126, 234, 0.3); |
|
|
|
padding: 16rpx 32rpx; |
|
|
|
display: flex; |
|
|
|
align-items: center; |
|
|
|
gap: 16rpx; |
|
|
|
} |
|
|
|
|
|
|
|
.chapter-count { |
|
|
|
.label-number { |
|
|
|
width: 32rpx; |
|
|
|
height: 32rpx; |
|
|
|
background: #666; |
|
|
|
border-radius: 50%; |
|
|
|
display: flex; |
|
|
|
align-items: center; |
|
|
|
justify-content: center; |
|
|
|
color: white; |
|
|
|
font-size: 28rpx; |
|
|
|
font-size: 24rpx; |
|
|
|
font-weight: 600; |
|
|
|
} |
|
|
|
|
|
|
|
.content { |
|
|
|
width: 690rpx; |
|
|
|
background: rgba(255, 255, 255, 0.9); |
|
|
|
backdrop-filter: blur(20rpx); |
|
|
|
border-radius: 32rpx; |
|
|
|
margin: 0 auto; |
|
|
|
margin-bottom: 20rpx; |
|
|
|
.label-text { |
|
|
|
color: #666; |
|
|
|
font-size: 28rpx; |
|
|
|
font-weight: 500; |
|
|
|
} |
|
|
|
|
|
|
|
.content-wrapper { |
|
|
|
padding: 48rpx; |
|
|
|
/* 歌词容器样式 */ |
|
|
|
.lyrics-container { |
|
|
|
flex: 1; |
|
|
|
padding: 0 20rpx; |
|
|
|
margin-bottom: 20rpx; |
|
|
|
width: 710rpx; |
|
|
|
} |
|
|
|
|
|
|
|
.article-content { |
|
|
|
line-height: 1.8; |
|
|
|
font-size: 32rpx; |
|
|
|
color: #2d3748; |
|
|
|
text-align: justify; |
|
|
|
.lyrics-content { |
|
|
|
display: flex; |
|
|
|
flex-direction: column; |
|
|
|
align-items: center; |
|
|
|
padding: 40rpx 0; |
|
|
|
} |
|
|
|
|
|
|
|
.sentence { |
|
|
|
.lyric-line { |
|
|
|
display: block; |
|
|
|
padding: 20rpx; |
|
|
|
margin: 10rpx 0; |
|
|
|
border-radius: 12rpx; |
|
|
|
line-height: 1.8; |
|
|
|
text-align: center; |
|
|
|
font-size: 32rpx; |
|
|
|
color: #999; |
|
|
|
line-height: 1.6; |
|
|
|
margin: 16rpx 0; |
|
|
|
transition: all 0.3s ease; |
|
|
|
} |
|
|
|
|
|
|
|
.sentence.active { |
|
|
|
background-color: rgba(255, 215, 0, 0.2); |
|
|
|
.lyric-line.active { |
|
|
|
color: #333; |
|
|
|
font-weight: 600; |
|
|
|
box-shadow: 0 4rpx 12rpx rgba(255, 215, 0, 0.3); |
|
|
|
transform: translateX(10rpx); |
|
|
|
font-size: 36rpx; |
|
|
|
} |
|
|
|
|
|
|
|
/* 底部控制栏样式 */ |
|
|
|
.controls { |
|
|
|
background: rgba(255, 255, 255, 0.98); |
|
|
|
backdrop-filter: blur(40rpx); |
|
|
|
border-top: 2rpx solid rgba(255, 255, 255, 0.3); |
|
|
|
box-shadow: 0 -16rpx 64rpx rgba(0, 0, 0, 0.12), |
|
|
|
0 -4rpx 16rpx rgba(0, 0, 0, 0.08); |
|
|
|
border-radius: 48rpx 48rpx 0 0; |
|
|
|
padding: 32rpx 40rpx 40rpx; |
|
|
|
/* 播放控制区域 */ |
|
|
|
.player-controls { |
|
|
|
background: white; |
|
|
|
padding: 40rpx 60rpx 60rpx 60rpx; |
|
|
|
display: flex; |
|
|
|
flex-direction: column; |
|
|
|
gap: 24rpx; |
|
|
|
position: fixed; |
|
|
|
bottom: 0; |
|
|
|
left: 0; |
|
|
|
right: 0; |
|
|
|
z-index: 1000; |
|
|
|
} |
|
|
|
|
|
|
|
/* 章节信息栏 */ |
|
|
|
.chapter-info-bar { |
|
|
|
/* 控制图标区域 */ |
|
|
|
.control-icons { |
|
|
|
display: flex; |
|
|
|
justify-content: space-between; |
|
|
|
align-items: center; |
|
|
|
padding: 0 8rpx; |
|
|
|
margin-bottom: 20rpx; |
|
|
|
} |
|
|
|
|
|
|
|
.chapter-title { |
|
|
|
font-size: 28rpx; |
|
|
|
font-weight: 600; |
|
|
|
color: #2d3748; |
|
|
|
flex: 1; |
|
|
|
overflow: hidden; |
|
|
|
text-overflow: ellipsis; |
|
|
|
white-space: nowrap; |
|
|
|
margin-right: 16rpx; |
|
|
|
.control-icon { |
|
|
|
width: 60rpx; |
|
|
|
height: 60rpx; |
|
|
|
display: flex; |
|
|
|
align-items: center; |
|
|
|
justify-content: center; |
|
|
|
border-radius: 50%; |
|
|
|
background: #f0f0f0; |
|
|
|
} |
|
|
|
|
|
|
|
.chapter-count { |
|
|
|
font-size: 24rpx; |
|
|
|
color: #718096; |
|
|
|
font-weight: 500; |
|
|
|
background: rgba(139, 92, 246, 0.1); |
|
|
|
padding: 8rpx 16rpx; |
|
|
|
border-radius: 20rpx; |
|
|
|
backdrop-filter: blur(10rpx); |
|
|
|
.control-icon-group { |
|
|
|
display: flex; |
|
|
|
gap: 40rpx; |
|
|
|
} |
|
|
|
|
|
|
|
.icon-text { |
|
|
|
font-size: 32rpx; |
|
|
|
color: #666; |
|
|
|
} |
|
|
|
|
|
|
|
/* 进度条区域 */ |
|
|
|
.progress-section { |
|
|
|
display: flex; |
|
|
|
align-items: center; |
|
|
|
gap: 20rpx; |
|
|
|
gap: 24rpx; |
|
|
|
margin-bottom: 16rpx; |
|
|
|
} |
|
|
|
|
|
|
|
.current-time, |
|
|
|
.duration { |
|
|
|
.time-text { |
|
|
|
font-size: 24rpx; |
|
|
|
color: #718096; |
|
|
|
color: #666; |
|
|
|
font-weight: 500; |
|
|
|
font-family: "Courier New", monospace; |
|
|
|
min-width: 80rpx; |
|
|
|
text-align: center; |
|
|
|
} |
|
|
|
|
|
|
|
/* 控制按钮区域 */ |
|
|
|
.control-buttons { |
|
|
|
display: flex; |
|
|
|
align-items: center; |
|
|
|
justify-content: center; |
|
|
|
gap: 32rpx; |
|
|
|
} |
|
|
|
|
|
|
|
/* 播放按钮 */ |
|
|
|
.play-btn { |
|
|
|
width: 96rpx; |
|
|
|
height: 96rpx; |
|
|
|
border-radius: 50%; |
|
|
|
border: none; |
|
|
|
background: linear-gradient(135deg, #8b5cf6, #a855f7); |
|
|
|
color: white; |
|
|
|
display: flex; |
|
|
|
align-items: center; |
|
|
|
justify-content: center; |
|
|
|
cursor: pointer; |
|
|
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); |
|
|
|
box-shadow: 0 12rpx 40rpx rgba(139, 92, 246, 0.4), |
|
|
|
0 6rpx 20rpx rgba(0, 0, 0, 0.15); |
|
|
|
position: relative; |
|
|
|
overflow: hidden; |
|
|
|
z-index: 2; |
|
|
|
} |
|
|
|
|
|
|
|
/* 控制按钮(上一章/下一章) */ |
|
|
|
.control-btn { |
|
|
|
width: 88rpx; |
|
|
|
height: 88rpx; |
|
|
|
border-radius: 50%; |
|
|
|
border: none; |
|
|
|
background: rgba(139, 92, 246, 0.08); |
|
|
|
color: #8b5cf6; |
|
|
|
display: flex; |
|
|
|
align-items: center; |
|
|
|
justify-content: center; |
|
|
|
cursor: pointer; |
|
|
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); |
|
|
|
box-shadow: 0 4rpx 16rpx rgba(139, 92, 246, 0.15); |
|
|
|
backdrop-filter: blur(20rpx); |
|
|
|
position: relative; |
|
|
|
overflow: hidden; |
|
|
|
} |
|
|
|
|
|
|
|
.play-btn::before { |
|
|
|
content: ""; |
|
|
|
position: absolute; |
|
|
|
top: 0; |
|
|
|
left: 0; |
|
|
|
right: 0; |
|
|
|
bottom: 0; |
|
|
|
background: linear-gradient( |
|
|
|
135deg, |
|
|
|
rgba(255, 255, 255, 0.2), |
|
|
|
rgba(255, 255, 255, 0.05) |
|
|
|
); |
|
|
|
border-radius: 50%; |
|
|
|
opacity: 0; |
|
|
|
transition: opacity 0.3s ease; |
|
|
|
} |
|
|
|
|
|
|
|
.play-btn:hover { |
|
|
|
transform: scale(1.08); |
|
|
|
box-shadow: 0 18rpx 56rpx rgba(139, 92, 246, 0.5), |
|
|
|
0 9rpx 28rpx rgba(0, 0, 0, 0.2); |
|
|
|
} |
|
|
|
|
|
|
|
.play-btn:hover::before { |
|
|
|
opacity: 1; |
|
|
|
} |
|
|
|
|
|
|
|
.play-btn:active { |
|
|
|
transform: scale(0.96); |
|
|
|
transition: transform 0.1s ease; |
|
|
|
} |
|
|
|
|
|
|
|
/* 进度条容器 */ |
|
|
|
.progress-container { |
|
|
|
flex: 1; |
|
|
|
position: relative; |
|
|
|
} |
|
|
|
|
|
|
|
.progress-bar { |
|
|
|
height: 12rpx; |
|
|
|
background: rgba(139, 92, 246, 0.12); |
|
|
|
border-radius: 6rpx; |
|
|
|
height: 8rpx; |
|
|
|
background: #e0e0e0; |
|
|
|
border-radius: 4rpx; |
|
|
|
position: relative; |
|
|
|
cursor: pointer; |
|
|
|
overflow: hidden; |
|
|
|
backdrop-filter: blur(20rpx); |
|
|
|
box-shadow: inset 0 2rpx 6rpx rgba(0, 0, 0, 0.1); |
|
|
|
} |
|
|
|
|
|
|
|
.progress { |
|
|
|
height: 100%; |
|
|
|
background: linear-gradient(90deg, #8b5cf6 0%, #a855f7 50%, #c084fc 100%); |
|
|
|
border-radius: 6rpx; |
|
|
|
transition: width 0.2s cubic-bezier(0.4, 0, 0.2, 1); |
|
|
|
box-shadow: 0 4rpx 16rpx rgba(139, 92, 246, 0.3); |
|
|
|
position: relative; |
|
|
|
} |
|
|
|
|
|
|
|
.progress::after { |
|
|
|
content: ""; |
|
|
|
position: absolute; |
|
|
|
top: 0; |
|
|
|
left: 0; |
|
|
|
right: 0; |
|
|
|
bottom: 0; |
|
|
|
background: linear-gradient( |
|
|
|
90deg, |
|
|
|
rgba(255, 255, 255, 0.3) 0%, |
|
|
|
rgba(255, 255, 255, 0.1) 100% |
|
|
|
); |
|
|
|
border-radius: 6rpx; |
|
|
|
background: #333; |
|
|
|
border-radius: 4rpx; |
|
|
|
transition: width 0.2s ease; |
|
|
|
} |
|
|
|
|
|
|
|
.progress-thumb { |
|
|
|
position: absolute; |
|
|
|
top: 50%; |
|
|
|
width: 28rpx; |
|
|
|
height: 28rpx; |
|
|
|
background: white; |
|
|
|
border: 4rpx solid #8b5cf6; |
|
|
|
width: 24rpx; |
|
|
|
height: 24rpx; |
|
|
|
background: #333; |
|
|
|
border-radius: 50%; |
|
|
|
transform: translate(-50%, -50%); |
|
|
|
box-shadow: 0 6rpx 20rpx rgba(139, 92, 246, 0.4), |
|
|
|
0 3rpx 6rpx rgba(0, 0, 0, 0.1); |
|
|
|
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); |
|
|
|
cursor: pointer; |
|
|
|
} |
|
|
|
|
|
|
|
.progress-thumb:hover { |
|
|
|
transform: translate(-50%, -50%) scale(1.2); |
|
|
|
box-shadow: 0 8rpx 24rpx rgba(139, 92, 246, 0.5), |
|
|
|
0 4rpx 8rpx rgba(0, 0, 0, 0.15); |
|
|
|
} |
|
|
|
|
|
|
|
/* 控制按钮交互效果 */ |
|
|
|
.control-btn::before { |
|
|
|
content: ""; |
|
|
|
position: absolute; |
|
|
|
top: 0; |
|
|
|
left: 0; |
|
|
|
right: 0; |
|
|
|
bottom: 0; |
|
|
|
background: linear-gradient( |
|
|
|
135deg, |
|
|
|
rgba(139, 92, 246, 0.1), |
|
|
|
rgba(168, 85, 247, 0.05) |
|
|
|
); |
|
|
|
border-radius: 50%; |
|
|
|
opacity: 0; |
|
|
|
transition: opacity 0.3s ease; |
|
|
|
} |
|
|
|
|
|
|
|
.control-btn:hover { |
|
|
|
background: rgba(139, 92, 246, 0.15); |
|
|
|
transform: scale(1.1); |
|
|
|
box-shadow: 0 8rpx 32rpx rgba(139, 92, 246, 0.25); |
|
|
|
} |
|
|
|
|
|
|
|
.control-btn:hover::before { |
|
|
|
opacity: 1; |
|
|
|
/* 无损标识 */ |
|
|
|
.quality-label { |
|
|
|
display: flex; |
|
|
|
justify-content: center; |
|
|
|
margin-bottom: 20rpx; |
|
|
|
} |
|
|
|
|
|
|
|
.control-btn:active { |
|
|
|
transform: scale(0.95); |
|
|
|
transition: transform 0.1s ease; |
|
|
|
.quality-text { |
|
|
|
font-size: 24rpx; |
|
|
|
color: #999; |
|
|
|
} |
|
|
|
|
|
|
|
.control-btn:disabled { |
|
|
|
opacity: 0.3; |
|
|
|
cursor: not-allowed; |
|
|
|
transform: none; |
|
|
|
box-shadow: 0 2rpx 6rpx rgba(139, 92, 246, 0.1); |
|
|
|
/* 底部控制按钮 */ |
|
|
|
.bottom-controls { |
|
|
|
display: flex; |
|
|
|
justify-content: center; |
|
|
|
align-items: center; |
|
|
|
gap: 60rpx; |
|
|
|
} |
|
|
|
|
|
|
|
.control-btn:disabled:hover { |
|
|
|
background: rgba(139, 92, 246, 0.08); |
|
|
|
transform: none; |
|
|
|
box-shadow: 0 2rpx 6rpx rgba(139, 92, 246, 0.1); |
|
|
|
.control-btn { |
|
|
|
width: 80rpx; |
|
|
|
height: 80rpx; |
|
|
|
display: flex; |
|
|
|
align-items: center; |
|
|
|
justify-content: center; |
|
|
|
border-radius: 50%; |
|
|
|
background: transparent; |
|
|
|
cursor: pointer; |
|
|
|
transition: all 0.2s ease; |
|
|
|
} |
|
|
|
|
|
|
|
.control-btn:disabled::before { |
|
|
|
opacity: 0; |
|
|
|
.control-btn.play-btn { |
|
|
|
background: #333; |
|
|
|
color: white; |
|
|
|
} |
|
|
|
|
|
|
|
/* 控制图标样式 */ |
|
|
|
.control-icon { |
|
|
|
.control-icon-text { |
|
|
|
font-size: 32rpx; |
|
|
|
color: #666; |
|
|
|
line-height: 1; |
|
|
|
filter: drop-shadow(0 2rpx 4rpx rgba(0, 0, 0, 0.1)); |
|
|
|
} |
|
|
|
|
|
|
|
.play-icon { |
|
|
|
font-size: 42rpx; |
|
|
|
line-height: 1; |
|
|
|
margin-left: 3rpx; |
|
|
|
filter: drop-shadow(0 3rpx 6rpx rgba(0, 0, 0, 0.2)); |
|
|
|
.control-btn.play-btn .control-icon-text { |
|
|
|
color: white; |
|
|
|
font-size: 36rpx; |
|
|
|
} |
|
|
|
|
|
|
|
.control-btn:active { |
|
|
|
transform: scale(0.95); |
|
|
|
} |
|
|
|
</style> |
|
|
|