musicPlayback.vue 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. <route lang="json5">
  2. {
  3. style: {
  4. navigationStyle: 'custom',
  5. },
  6. }
  7. </route>
  8. <template>
  9. <view class="music-player">
  10. <!-- 返回按钮 -->
  11. <view class="header">
  12. <image
  13. @click="goBack"
  14. style="width: 60rpx; height: 60rpx"
  15. src="@/static/icon/Left.svg"
  16. ></image>
  17. </view>
  18. <view class="player-content">
  19. <!-- 播放图片 -->
  20. <view class="record-player">
  21. <image src="@/static/icon/fi-rr-big-play.svg" alt="Record Player" class="record-image" />
  22. </view>
  23. <!-- 播放按钮 -->
  24. <view class="track-info">
  25. <!-- 歌名 取消收藏 -->
  26. <view class="song-header">
  27. <view class="song-title">{{ song.name }}</view>
  28. <view class="favorite-btn">
  29. <image
  30. @click="cancelHeart"
  31. style="width: 32rpx; height: 32rpx"
  32. :src="
  33. song.favorite
  34. ? '@/static/icon/fi-rr-heart-grey.svg'
  35. : '@/static/icon/fi-rr-heart.svg'
  36. "
  37. ></image>
  38. </view>
  39. </view>
  40. <!-- 进度条时间 -->
  41. <view class="progress-container">
  42. <!-- <view class="progress-bar"> -->
  43. <!-- <view class="progress-current"></view> -->
  44. <input
  45. type="range"
  46. v-model="currentTime"
  47. :max="duration"
  48. step="0.1"
  49. @input="onSliderChange"
  50. class="slider"
  51. />
  52. <!-- </view> -->
  53. <view class="time-info">
  54. <text class="current-time">{{ formatTime(currentTime) }}</text>
  55. <text class="total-time">{{ formatTime(duration) }}</text>
  56. </view>
  57. </view>
  58. <!-- 上一曲下一曲播放 -->
  59. <view class="controls">
  60. <!-- 循环播放 -->
  61. <view class="control-btn" @click="palyCycleBtn">
  62. <image
  63. style="width: 32rpx; height: 32rpx"
  64. src="@/static/icon/fi-rr-refresh.svg"
  65. ></image>
  66. </view>
  67. <!-- 上一曲 -->
  68. <view class="control-btn" @click="previousSongBtn">
  69. <image style="width: 48rpx; height: 32rpx" src="@/static/icon/Group-left.svg"></image>
  70. </view>
  71. <!-- 播放 暂停 -->
  72. <view class="control-paly" @click="togglePlayPause">
  73. <button class="control-btn pause-btn" v-if="isPlaying">
  74. <image style="width: 40rpx; height: 40rpx" src="@/static/icon/Vector.svg"></image>
  75. </button>
  76. <button class="control-btn pause-btn" v-else>
  77. <view class="control-Vector">
  78. <image
  79. style="width: 8rpx; height: 40rpx"
  80. src="@/static/icon/Vector-paly.svg"
  81. ></image>
  82. </view>
  83. <view class="control-Vector">
  84. <image
  85. style="width: 8rpx; height: 40rpx"
  86. src="@/static/icon/Vector-paly.svg"
  87. ></image>
  88. </view>
  89. </button>
  90. </view>
  91. <!-- 下一曲 -->
  92. <view class="control-btn" @click="nextSongBtn">
  93. <image style="width: 48rpx; height: 32rpx" src="@/static/icon/Group-right.svg"></image>
  94. </view>
  95. <!-- -->
  96. <view class="control-btn">
  97. <image
  98. style="width: 32rpx; height: 32rpx"
  99. src="@/static/icon/fi-rr-shuffle.svg"
  100. ></image>
  101. </view>
  102. </view>
  103. </view>
  104. </view>
  105. </view>
  106. </template>
  107. <script setup>
  108. import { ref } from 'vue'
  109. const song = ref({})
  110. const audioSrc = ref('https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3') // 替换为你的音频文件路径
  111. const isPlaying = ref(true) // 用来标识播放状态
  112. const currentTime = ref(0) // 当前播放时间
  113. const duration = ref(0) // 歌曲总时长
  114. const audio = uni.createInnerAudioContext()
  115. // 格式化时间
  116. const formatTime = (time) => {
  117. const minutes = Math.floor(time / 60)
  118. const seconds = Math.floor(time % 60)
  119. return `${minutes}:${seconds < 10 ? '0' : ''}${seconds}`
  120. }
  121. onLoad((query) => {
  122. if (query.songDate) {
  123. song.value = JSON.parse(decodeURIComponent(query.songDate))
  124. }
  125. })
  126. onMounted(() => {
  127. // audio.autoplay = true
  128. // audio.src = audioSrc.value
  129. // 获取音频的总时长
  130. audio.onCanplay(() => {
  131. duration.value = audio.duration
  132. console.log('Total duration:', duration.value) // 用于调试
  133. })
  134. // 更新 currentTime
  135. audio.onTimeUpdate(() => {
  136. currentTime.value = audio.currentTime
  137. })
  138. // 当音频播放完时,自动重置播放
  139. audio.onEnded(() => {
  140. isPlaying.value = false
  141. })
  142. })
  143. // 实现返回逻辑
  144. const goBack = () => {
  145. uni.navigateBack()
  146. }
  147. // 取消收藏
  148. const cancelHeart = () => {
  149. console.log('取消收藏')
  150. }
  151. // 循环播放
  152. const palyCycleBtn = () => {
  153. audio.loop = true
  154. audio.autoplay = true
  155. audio.src = audioSrc.value
  156. isPlaying.value = true
  157. }
  158. // 上一曲
  159. const previousSongBtn = () => {
  160. console.log('上一曲')
  161. }
  162. // 下一曲
  163. const nextSongBtn = () => {
  164. console.log('下一曲')
  165. }
  166. // 实现播放/暂停切换逻辑
  167. // const togglePlayPause = () => {
  168. // if (isPlaying.value) {
  169. // audio.autoplay = true
  170. // audio.src = audioSrc.value
  171. // } else {
  172. // audio.autoplay = false
  173. // audio.src = audioSrc.value
  174. // }
  175. // isPlaying.value = !isPlaying.value
  176. // }
  177. // 切换播放/暂停
  178. const togglePlayPause = () => {
  179. if (isPlaying.value) {
  180. console.log(111)
  181. audio.pause()
  182. } else {
  183. audio.play()
  184. console.log(222)
  185. }
  186. isPlaying.value = !isPlaying.value
  187. }
  188. // 进度条拖动时,更新音频时间
  189. const onSliderChange = () => {
  190. audio.seek(currentTime.value)
  191. }
  192. </script>
  193. <style scoped lang="scss">
  194. .music-player {
  195. height: 100%;
  196. margin: 0 auto;
  197. background-color: #ffffff;
  198. }
  199. .header {
  200. padding: 1rem;
  201. }
  202. .player-content {
  203. height: 100%;
  204. padding: 1rem;
  205. }
  206. // 播放图片
  207. .record-player {
  208. display: flex;
  209. justify-content: center;
  210. margin-bottom: 2rem;
  211. }
  212. .record-image {
  213. width: 620rpx;
  214. height: 640rpx;
  215. }
  216. // 播放
  217. .track-info {
  218. padding: 0 0.5rem;
  219. }
  220. // 歌名
  221. .song-header {
  222. display: flex;
  223. align-items: center;
  224. justify-content: space-between;
  225. margin-bottom: 1.5rem;
  226. }
  227. .song-title {
  228. margin: 0;
  229. font-size: 40rpx;
  230. font-weight: 500;
  231. color: #333;
  232. }
  233. // <!-- 进度条时间 -->
  234. .progress-container {
  235. margin-bottom: 2rem;
  236. }
  237. .progress-bar {
  238. position: relative;
  239. width: 100%;
  240. height: 8rpx;
  241. margin-bottom: 0.5rem;
  242. background-color: #eee;
  243. border-radius: 4rpx;
  244. }
  245. .progress-current {
  246. position: absolute;
  247. top: 0;
  248. left: 0;
  249. width: 30%;
  250. height: 100%;
  251. background-color: #ff8c69;
  252. border-radius: 4rpx;
  253. }
  254. .time-info {
  255. display: flex;
  256. justify-content: space-between;
  257. font-size: 24rpx;
  258. color: #666;
  259. }
  260. // <!-- 上一曲下一曲播放 -->
  261. .controls {
  262. display: flex;
  263. align-items: center;
  264. justify-content: space-between;
  265. padding: 1rem 0;
  266. }
  267. .control-btn {
  268. display: flex;
  269. align-items: center;
  270. justify-content: center;
  271. width: 80rpx;
  272. height: 80rpx;
  273. padding: 0.5rem;
  274. }
  275. .pause-btn {
  276. width: 132rpx;
  277. height: 132rpx;
  278. background: #f88842;
  279. border-radius: 50%;
  280. }
  281. .control-Vector {
  282. width: 20rpx;
  283. }
  284. </style>