refactor(AudioPlayer): 优化音频播放器UI布局和样式
调整音频播放器的布局结构,改进封面、进度条和按钮的样式,提升用户体验
Showing
3 changed files
with
97 additions
and
72 deletions
| 1 | <!-- | 1 | <!-- |
| 2 | * @Date: 2025-04-07 12:35:35 | 2 | * @Date: 2025-04-07 12:35:35 |
| 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com | 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com |
| 4 | - * @LastEditTime: 2025-04-07 16:41:23 | 4 | + * @LastEditTime: 2025-04-07 17:20:54 |
| 5 | * @FilePath: /mlaj/src/components/ui/audioPlayer.vue | 5 | * @FilePath: /mlaj/src/components/ui/audioPlayer.vue |
| 6 | * @Description: 音频播放器组件,支持播放控制、进度条调节、音量控制、播放列表等功能 | 6 | * @Description: 音频播放器组件,支持播放控制、进度条调节、音量控制、播放列表等功能 |
| 7 | --> | 7 | --> |
| 8 | <template> | 8 | <template> |
| 9 | <!-- 音频播放器主容器 --> | 9 | <!-- 音频播放器主容器 --> |
| 10 | - <div class="audio-player bg-white rounded-lg shadow-xl overflow-hidden max-w-3xl mx-auto p-4 sm:p-6"> | 10 | + <div class="audio-player bg-white rounded-lg shadow-xl overflow-hidden max-w-md mx-auto p-4"> |
| 11 | - <!-- 封面与控制区:显示当前播放歌曲的封面、标题、艺术家和播放控制按钮 --> | 11 | + <!-- 封面与歌曲信息 --> |
| 12 | - <div class="flex flex-col sm:flex-row items-center space-y-4 sm:space-y-0 sm:space-x-4"> | 12 | + <div class="flex flex-col items-center mb-6"> |
| 13 | <!-- 歌曲封面 --> | 13 | <!-- 歌曲封面 --> |
| 14 | - <div class="w-32 h-32 sm:w-24 sm:h-24 rounded-lg overflow-hidden"> | 14 | + <div class="w-48 h-48 rounded-lg overflow-hidden mb-4"> |
| 15 | <img :src="currentSong.cover" alt="封面" class="w-full h-full object-cover"> | 15 | <img :src="currentSong.cover" alt="封面" class="w-full h-full object-cover"> |
| 16 | </div> | 16 | </div> |
| 17 | 17 | ||
| 18 | <!-- 歌曲信息 --> | 18 | <!-- 歌曲信息 --> |
| 19 | - <div class="flex-1 text-center sm:text-left"> | 19 | + <div class="text-center"> |
| 20 | - <h3 class="text-xl sm:text-lg font-medium">{{ currentSong.title }}</h3> | 20 | + <h3 class="text-lg font-medium">{{ currentSong.title }}</h3> |
| 21 | <p class="text-sm text-gray-500">{{ currentSong.artist }}</p> | 21 | <p class="text-sm text-gray-500">{{ currentSong.artist }}</p> |
| 22 | </div> | 22 | </div> |
| 23 | 23 | ||
| 24 | + </div> | ||
| 25 | + | ||
| 26 | + <!-- 进度条与时间:显示当前播放时间、总时长和可拖动的进度条 --> | ||
| 27 | + <div class="mt-4 mb-6"> | ||
| 28 | + <div class="flex items-center justify-between text-xs text-gray-400 mb-2"> | ||
| 29 | + <span>{{ formatTime(currentTime) }}</span> | ||
| 30 | + <span>{{ formatTime(duration) }}</span> | ||
| 31 | + </div> | ||
| 32 | + <div class="progress-bar relative h-1 bg-gray-200 rounded-full"> | ||
| 33 | + <input | ||
| 34 | + type="range" | ||
| 35 | + :value="progress" | ||
| 36 | + @input="handleProgressChange" | ||
| 37 | + @change="seekTo" | ||
| 38 | + class="w-full absolute inset-0 appearance-none bg-transparent cursor-pointer" | ||
| 39 | + > | ||
| 40 | + <div | ||
| 41 | + :style="{ width: `${progress}%` }" | ||
| 42 | + class="progress-track absolute left-0 top-0 h-full rounded-full bg-gradient-to-r from-blue-500 to-purple-500 transition-all" | ||
| 43 | + ></div> | ||
| 44 | + </div> | ||
| 45 | + </div> | ||
| 46 | + | ||
| 24 | <!-- 播放控制按钮组:上一首、播放/暂停、下一首 --> | 47 | <!-- 播放控制按钮组:上一首、播放/暂停、下一首 --> |
| 25 | - <div class="flex items-center justify-center space-x-8 sm:space-x-6 w-full sm:w-auto"> | 48 | + <div class="flex items-center space-x-16 mt-6" style="justify-content: space-evenly;"> |
| 26 | <button | 49 | <button |
| 27 | @click="prevSong" | 50 | @click="prevSong" |
| 28 | - class="w-12 h-12 sm:w-10 sm:h-10 flex items-center justify-center rounded-full bg-gray-100 hover:bg-gray-200 transition-colors" | 51 | + class="w-14 h-14 flex items-center justify-center rounded-full bg-gray-100 hover:bg-gray-200 transition-colors" |
| 29 | > | 52 | > |
| 30 | - <font-awesome-icon icon="backward-step" class="text-xl text-gray-600" /> | 53 | + <font-awesome-icon icon="backward-step" class="text-2xl text-gray-600" /> |
| 31 | </button> | 54 | </button> |
| 32 | <button | 55 | <button |
| 33 | @click="togglePlay" | 56 | @click="togglePlay" |
| 34 | :class="{'paused': !isPlaying, 'opacity-50 cursor-not-allowed': isLoading}" | 57 | :class="{'paused': !isPlaying, 'opacity-50 cursor-not-allowed': isLoading}" |
| 35 | :disabled="isLoading" | 58 | :disabled="isLoading" |
| 36 | - class="w-16 h-16 sm:w-14 sm:h-14 flex items-center justify-center rounded-full bg-blue-500 hover:bg-blue-600 transition-colors shadow-lg" | 59 | + class="w-14 h-14 flex items-center justify-center rounded-full bg-blue-500 hover:bg-blue-600 transition-colors shadow-lg" |
| 37 | > | 60 | > |
| 38 | <font-awesome-icon | 61 | <font-awesome-icon |
| 39 | :icon="['fas' , isPlaying ? 'pause' : 'play']" | 62 | :icon="['fas' , isPlaying ? 'pause' : 'play']" |
| 40 | :class="{ 'fa-spin': isLoading }" | 63 | :class="{ 'fa-spin': isLoading }" |
| 41 | - class="text-3xl" | 64 | + class="text-2xl" |
| 42 | /> | 65 | /> |
| 43 | </button> | 66 | </button> |
| 44 | <button | 67 | <button |
| 45 | @click="nextSong" | 68 | @click="nextSong" |
| 46 | - class="w-12 h-12 sm:w-10 sm:h-10 flex items-center justify-center rounded-full bg-gray-100 hover:bg-gray-200 transition-colors" | 69 | + class="w-14 h-14 flex items-center justify-center rounded-full bg-gray-100 hover:bg-gray-200 transition-colors" |
| 47 | > | 70 | > |
| 48 | - <font-awesome-icon icon="forward-step" class="text-xl text-gray-600" /> | 71 | + <font-awesome-icon icon="forward-step" class="text-2xl text-gray-600" /> |
| 49 | </button> | 72 | </button> |
| 50 | </div> | 73 | </div> |
| 51 | - </div> | ||
| 52 | 74 | ||
| 53 | - <!-- 进度条与时间:显示当前播放时间、总时长和可拖动的进度条 --> | 75 | + <!-- 播放列表按钮 --> |
| 54 | - <div class="mt-4"> | 76 | + <div class="flex justify-end mt-4"> |
| 55 | - <div class="flex items-center justify-between text-sm text-gray-500"> | 77 | + <button @click="togglePlaylist" class="text-gray-500 hover:text-gray-700 transition-colors"> |
| 56 | - <span>{{ formatTime(currentTime) }}</span> | 78 | + <font-awesome-icon icon="list" class="text-lg" /> |
| 57 | - <span>{{ formatTime(duration) }}</span> | 79 | + </button> |
| 58 | - </div> | ||
| 59 | - | ||
| 60 | - <div class="progress-bar relative mt-2"> | ||
| 61 | - <input | ||
| 62 | - type="range" | ||
| 63 | - :value="progress" | ||
| 64 | - @input="handleProgressChange" | ||
| 65 | - @change="seekTo" | ||
| 66 | - class="w-full appearance-none bg-gray-200 rounded-full h-1.5 cursor-pointer" | ||
| 67 | - > | ||
| 68 | - <div | ||
| 69 | - :style="{ width: `${progress}%` }" | ||
| 70 | - class="progress-track absolute left-0 top-0 h-full rounded-full bg-gradient-to-r from-blue-500 to-purple-500 transition-all" | ||
| 71 | - ></div> | ||
| 72 | - </div> | ||
| 73 | </div> | 80 | </div> |
| 74 | 81 | ||
| 75 | - <!-- 音量与设置:音量控制滑块和播放列表按钮 --> | 82 | + <!-- 播放列表弹出层 --> |
| 76 | - <div class="flex items-center justify-between mt-6"> | 83 | + <van-popup |
| 77 | - <div class="flex items-center space-x-2"> | 84 | + v-model:show="isPlaylistVisible" |
| 78 | - <!-- <font-awesome-icon :icon="volume === 0 ? 'volume-off' : 'volume-up'" /> | 85 | + position="bottom" |
| 79 | - <input | 86 | + :overlay="false" |
| 80 | - type="range" | 87 | + round |
| 81 | - :value="volume" | 88 | + class="playlist-popup" |
| 82 | - @input="handleVolumeChange" | 89 | + style="height: 100%" |
| 83 | - min="0" | 90 | + > |
| 84 | - max="100" | 91 | + <div class="playlist-header flex justify-between items-center px-4 py-3 border-b sticky top-0 bg-white z-10"> |
| 85 | - step="1" | ||
| 86 | - class="w-24 appearance-none bg-gray-200 rounded-full h-1.5 cursor-pointer" | ||
| 87 | - > --> | ||
| 88 | - </div> | ||
| 89 | - | ||
| 90 | - <div class="flex items-center space-x-4"> | ||
| 91 | - <!-- 播放列表按钮 --> | ||
| 92 | - <button @click="togglePlaylist"><font-awesome-icon icon="list" /></button> | ||
| 93 | - </div> | ||
| 94 | - </div> | ||
| 95 | - | ||
| 96 | - <!-- 播放列表:可切换显示的歌曲列表面板 --> | ||
| 97 | - <div v-show="isPlaylistVisible" class="playlist mt-4 overscroll-contain"> | ||
| 98 | - <div class="playlist-header flex justify-between items-center px-2 py-1 bg-gray-100 rounded-t-lg"> | ||
| 99 | <h4 class="font-medium">播放列表 ({{ songs.length }})</h4> | 92 | <h4 class="font-medium">播放列表 ({{ songs.length }})</h4> |
| 100 | - <button @click="closePlaylist"><font-awesome-icon icon="xmark" /></button> | 93 | + <button @click="closePlaylist" class="text-gray-500 hover:text-gray-700 transition-colors"> |
| 94 | + <font-awesome-icon icon="xmark" /> | ||
| 95 | + </button> | ||
| 101 | </div> | 96 | </div> |
| 102 | - <div class="playlist-items" style="max-height: 16rem; overflow-y: auto; -webkit-overflow-scrolling: touch;"> | 97 | + <div class="playlist-items overflow-y-auto h-full pb-safe"> |
| 103 | <div | 98 | <div |
| 104 | v-for="(song, index) in songs" | 99 | v-for="(song, index) in songs" |
| 105 | :key="song.id" | 100 | :key="song.id" |
| 106 | - :class="{'active': index === currentIndex}" | 101 | + :class="{'active bg-blue-50': index === currentIndex}" |
| 107 | @click="selectSong(index)" | 102 | @click="selectSong(index)" |
| 108 | - class="px-2 py-3 hover:bg-gray-100 transition-colors" | 103 | + class="flex items-center px-4 py-3 hover:bg-gray-50 transition-colors relative" |
| 109 | > | 104 | > |
| 110 | - {{ song.title }} - {{ song.artist }} | 105 | + <div class="absolute top-0 left-0 right-0 h-[1px] bg-gray-200"></div> |
| 106 | + <div class="w-16 h-16 rounded-lg overflow-hidden mr-4 flex-shrink-0"> | ||
| 107 | + <img :src="song.cover" alt="封面" class="w-full h-full object-cover"> | ||
| 108 | + </div> | ||
| 109 | + <div class="flex-1"> | ||
| 110 | + <div class="font-medium">{{ song.title }}</div> | ||
| 111 | + <div class="text-sm text-gray-500">{{ song.artist }}</div> | ||
| 112 | + </div> | ||
| 113 | + <font-awesome-icon | ||
| 114 | + v-if="index === currentIndex" | ||
| 115 | + icon="volume-up" | ||
| 116 | + class="text-blue-500 ml-2" | ||
| 117 | + /> | ||
| 111 | </div> | 118 | </div> |
| 112 | </div> | 119 | </div> |
| 113 | - </div> | 120 | + </van-popup> |
| 114 | </div> | 121 | </div> |
| 115 | </template> | 122 | </template> |
| 116 | 123 | ||
| ... | @@ -151,6 +158,7 @@ onMounted(() => { | ... | @@ -151,6 +158,7 @@ onMounted(() => { |
| 151 | const loadAudio = async () => { | 158 | const loadAudio = async () => { |
| 152 | if (!currentSong.value) return | 159 | if (!currentSong.value) return |
| 153 | isLoading.value = true | 160 | isLoading.value = true |
| 161 | + isPlaylistVisible.value = false // 关闭播放列表 | ||
| 154 | try { | 162 | try { |
| 155 | if (audio.value) { | 163 | if (audio.value) { |
| 156 | audio.value.removeEventListener('timeupdate', updateProgress) | 164 | audio.value.removeEventListener('timeupdate', updateProgress) |
| ... | @@ -334,6 +342,27 @@ watch([isPlaying, currentIndex], () => { | ... | @@ -334,6 +342,27 @@ watch([isPlaying, currentIndex], () => { |
| 334 | .progress-track { | 342 | .progress-track { |
| 335 | height: var(--progress-height); | 343 | height: var(--progress-height); |
| 336 | border-radius: var(--progress-height); | 344 | border-radius: var(--progress-height); |
| 345 | + background: linear-gradient(90deg, #1a1a1a 0%, #4a4a4a 100%); | ||
| 346 | + transition: width 0.3s ease-in-out; | ||
| 347 | +} | ||
| 348 | + | ||
| 349 | +input[type='range'] { | ||
| 350 | + -webkit-appearance: none; | ||
| 351 | + appearance: none; | ||
| 352 | + background: transparent; | ||
| 353 | +} | ||
| 354 | + | ||
| 355 | +input[type='range']::-webkit-slider-thumb { | ||
| 356 | + -webkit-appearance: none; | ||
| 357 | + appearance: none; | ||
| 358 | + width: 0; | ||
| 359 | + height: 0; | ||
| 360 | +} | ||
| 361 | + | ||
| 362 | +input[type='range']::-moz-range-thumb { | ||
| 363 | + width: 0; | ||
| 364 | + height: 0; | ||
| 365 | + border: 0; | ||
| 337 | } | 366 | } |
| 338 | 367 | ||
| 339 | /* 按钮交互动画 */ | 368 | /* 按钮交互动画 */ |
| ... | @@ -363,11 +392,7 @@ button:disabled { | ... | @@ -363,11 +392,7 @@ button:disabled { |
| 363 | } | 392 | } |
| 364 | 393 | ||
| 365 | /* 播放列表样式 */ | 394 | /* 播放列表样式 */ |
| 366 | -.playlist-items { | 395 | + |
| 367 | - border: 1px solid #e5e7eb; | ||
| 368 | - border-top: 0; | ||
| 369 | - border-radius: 0 0 0.375rem 0.375rem; | ||
| 370 | -} | ||
| 371 | 396 | ||
| 372 | .active { | 397 | .active { |
| 373 | background-color: #f3f4f6; | 398 | background-color: #f3f4f6; | ... | ... |
| 1 | <!-- | 1 | <!-- |
| 2 | * @Date: 2025-03-24 13:04:21 | 2 | * @Date: 2025-03-24 13:04:21 |
| 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com | 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com |
| 4 | - * @LastEditTime: 2025-04-07 14:06:35 | 4 | + * @LastEditTime: 2025-04-07 17:17:52 |
| 5 | * @FilePath: /mlaj/src/views/profile/settings/AudioPlayerPage.vue | 5 | * @FilePath: /mlaj/src/views/profile/settings/AudioPlayerPage.vue |
| 6 | * @Description: 音频播放页面 | 6 | * @Description: 音频播放页面 |
| 7 | --> | 7 | --> | ... | ... |
-
Please register or login to post a comment