270 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			Vue
		
	
	
	
		
		
			
		
	
	
			270 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			Vue
		
	
	
	
| 
								 | 
							
								<template>
							 | 
						||
| 
								 | 
							
									<view v-if="controls" @click="onClick" class="_contain">
							 | 
						||
| 
								 | 
							
								    <!-- 海报和按钮 -->
							 | 
						||
| 
								 | 
							
										<view class="_poster" :style="'background-image:url('+poster+')'">
							 | 
						||
| 
								 | 
							
											<view class="_button" @tap="_buttonTap">
							 | 
						||
| 
								 | 
							
												<view :class="playing?'_pause':'_play'" />
							 | 
						||
| 
								 | 
							
											</view>
							 | 
						||
| 
								 | 
							
										</view>
							 | 
						||
| 
								 | 
							
								    <!-- 曲名和作者 -->
							 | 
						||
| 
								 | 
							
										<view class="_title">
							 | 
						||
| 
								 | 
							
											<view class="_name">{{name||'未知音频'}}</view>
							 | 
						||
| 
								 | 
							
											<view class="_author">{{author||'未知作者'}}</view>
							 | 
						||
| 
								 | 
							
										</view>
							 | 
						||
| 
								 | 
							
								    <!-- 进度条 -->
							 | 
						||
| 
								 | 
							
								    <slider class="_slider" activeColor="#585959" block-size="12" handle-size="12" :disabled="error" :value="value" @changing="_seeking" @change="_seeked" />
							 | 
						||
| 
								 | 
							
								    <!--播放时间-->
							 | 
						||
| 
								 | 
							
										<view class="_time">{{time||'00:00'}}</view>
							 | 
						||
| 
								 | 
							
									</view>
							 | 
						||
| 
								 | 
							
								</template>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								<script>
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * @fileoverview audio 组件
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								import context from './context'
							 | 
						||
| 
								 | 
							
								export default {
							 | 
						||
| 
								 | 
							
								  data () {
							 | 
						||
| 
								 | 
							
								    return {
							 | 
						||
| 
								 | 
							
								      error: false,
							 | 
						||
| 
								 | 
							
								      playing: false,
							 | 
						||
| 
								 | 
							
								      time: '00:00',
							 | 
						||
| 
								 | 
							
								      value: 0
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  },
							 | 
						||
| 
								 | 
							
								  props: {
							 | 
						||
| 
								 | 
							
								    aid: String,
							 | 
						||
| 
								 | 
							
								    name: String, // 音乐名
							 | 
						||
| 
								 | 
							
								    author: String, // 作者
							 | 
						||
| 
								 | 
							
								    poster: String, // 海报图片地址
							 | 
						||
| 
								 | 
							
								    autoplay: [Boolean, String], // 是否自动播放
							 | 
						||
| 
								 | 
							
								    controls: [Boolean, String], // 是否显示控件
							 | 
						||
| 
								 | 
							
								    loop: [Boolean, String], // 是否循环播放
							 | 
						||
| 
								 | 
							
								    src: String // 源地址
							 | 
						||
| 
								 | 
							
								  },
							 | 
						||
| 
								 | 
							
								  watch: {
							 | 
						||
| 
								 | 
							
								    src (src) {
							 | 
						||
| 
								 | 
							
								      this.setSrc(src)
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  },
							 | 
						||
| 
								 | 
							
								  mounted () {
							 | 
						||
| 
								 | 
							
								    this._ctx = uni.createInnerAudioContext()
							 | 
						||
| 
								 | 
							
								    this._ctx.onError((err) => {
							 | 
						||
| 
								 | 
							
								      this.error = true
							 | 
						||
| 
								 | 
							
								      this.$emit('error', err)
							 | 
						||
| 
								 | 
							
								    })
							 | 
						||
| 
								 | 
							
								    this._ctx.onTimeUpdate(() => {
							 | 
						||
| 
								 | 
							
								      const time = this._ctx.currentTime
							 | 
						||
| 
								 | 
							
								      const min = parseInt(time / 60)
							 | 
						||
| 
								 | 
							
								      const sec = Math.ceil(time % 60)
							 | 
						||
| 
								 | 
							
								      this.time = (min > 9 ? min : '0' + min) + ':' + (sec > 9 ? sec : '0' + sec)
							 | 
						||
| 
								 | 
							
								      if (!this.lastTime) {
							 | 
						||
| 
								 | 
							
								        this.value = time / this._ctx.duration * 100 // 不在拖动状态下
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    })
							 | 
						||
| 
								 | 
							
								    this._ctx.onEnded(() => {
							 | 
						||
| 
								 | 
							
								      if (!this.loop) {
							 | 
						||
| 
								 | 
							
								        this.playing = false
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    })
							 | 
						||
| 
								 | 
							
								    context.set(this.aid, this)
							 | 
						||
| 
								 | 
							
								    this.setSrc(this.src)
							 | 
						||
| 
								 | 
							
								  },
							 | 
						||
| 
								 | 
							
								  beforeDestroy () {
							 | 
						||
| 
								 | 
							
								    this._ctx.destroy()
							 | 
						||
| 
								 | 
							
								    context.remove(this.aid)
							 | 
						||
| 
								 | 
							
								  },
							 | 
						||
| 
								 | 
							
								  onPageShow () {
							 | 
						||
| 
								 | 
							
								    if (this.playing && this._ctx.paused) {
							 | 
						||
| 
								 | 
							
								      this._ctx.play()
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  },
							 | 
						||
| 
								 | 
							
								  methods: {
							 | 
						||
| 
								 | 
							
								    // 设置源
							 | 
						||
| 
								 | 
							
								    setSrc (src) {
							 | 
						||
| 
								 | 
							
								      this._ctx.autoplay = this.autoplay
							 | 
						||
| 
								 | 
							
								      this._ctx.loop = this.loop
							 | 
						||
| 
								 | 
							
								      this._ctx.src = src
							 | 
						||
| 
								 | 
							
								      if (this.autoplay && !this.playing) {
							 | 
						||
| 
								 | 
							
								        this.playing = true
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    },
							 | 
						||
| 
								 | 
							
								    // 播放
							 | 
						||
| 
								 | 
							
								    play () {
							 | 
						||
| 
								 | 
							
								      this._ctx.play()
							 | 
						||
| 
								 | 
							
								      this.playing = true
							 | 
						||
| 
								 | 
							
								      this.$emit('play', {
							 | 
						||
| 
								 | 
							
								        target: {
							 | 
						||
| 
								 | 
							
								          id: this.aid
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      })
							 | 
						||
| 
								 | 
							
								    },
							 | 
						||
| 
								 | 
							
								    // 暂停
							 | 
						||
| 
								 | 
							
								    pause () {
							 | 
						||
| 
								 | 
							
								      this._ctx.pause()
							 | 
						||
| 
								 | 
							
								      this.playing = false
							 | 
						||
| 
								 | 
							
								      this.$emit('pause')
							 | 
						||
| 
								 | 
							
								    },
							 | 
						||
| 
								 | 
							
								    // 设置播放速率
							 | 
						||
| 
								 | 
							
								    playbackRate (rate) {
							 | 
						||
| 
								 | 
							
								      this._ctx.playbackRate = rate
							 | 
						||
| 
								 | 
							
								    },
							 | 
						||
| 
								 | 
							
								    // 移动进度条
							 | 
						||
| 
								 | 
							
								    seek (sec) {
							 | 
						||
| 
								 | 
							
								      this._ctx.seek(sec)
							 | 
						||
| 
								 | 
							
								    },
							 | 
						||
| 
								 | 
							
								    // 内部方法
							 | 
						||
| 
								 | 
							
								    _buttonTap () {
							 | 
						||
| 
								 | 
							
								      if (this.playing) this.pause()
							 | 
						||
| 
								 | 
							
								      else this.play()
							 | 
						||
| 
								 | 
							
								    },
							 | 
						||
| 
								 | 
							
								    _seeking (e) {
							 | 
						||
| 
								 | 
							
								      // 避免过于频繁 setData
							 | 
						||
| 
								 | 
							
								      if (e.timeStamp - this.lastTime < 200) return
							 | 
						||
| 
								 | 
							
								      const time = Math.round(e.detail.value / 100 * this._ctx.duration)
							 | 
						||
| 
								 | 
							
								      const min = parseInt(time / 60)
							 | 
						||
| 
								 | 
							
								      const sec = time % 60
							 | 
						||
| 
								 | 
							
								      this.time = (min > 9 ? min : '0' + min) + ':' + (sec > 9 ? sec : '0' + sec)
							 | 
						||
| 
								 | 
							
								      this.lastTime = e.timeStamp
							 | 
						||
| 
								 | 
							
								    },
							 | 
						||
| 
								 | 
							
								    _seeked (e) {
							 | 
						||
| 
								 | 
							
								      this.seek(e.detail.value / 100 * this._ctx.duration)
							 | 
						||
| 
								 | 
							
								      this.lastTime = undefined
							 | 
						||
| 
								 | 
							
								    },
							 | 
						||
| 
								 | 
							
								    onClick(e) {
							 | 
						||
| 
								 | 
							
								      this.$emit('onClick', e)
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								</script>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								<style>
							 | 
						||
| 
								 | 
							
								/* 顶层容器 */
							 | 
						||
| 
								 | 
							
								._contain {
							 | 
						||
| 
								 | 
							
								  position: relative;
							 | 
						||
| 
								 | 
							
								  display: inline-flex;
							 | 
						||
| 
								 | 
							
								  width: 290px;
							 | 
						||
| 
								 | 
							
								  background-color: #fcfcfc;
							 | 
						||
| 
								 | 
							
								  border: 1px solid #e0e0e0;
							 | 
						||
| 
								 | 
							
								  border-radius: 2px;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/* 播放、暂停按钮 */
							 | 
						||
| 
								 | 
							
								._button {
							 | 
						||
| 
								 | 
							
								  display: flex;
							 | 
						||
| 
								 | 
							
								  align-items: center;
							 | 
						||
| 
								 | 
							
								  justify-content: center;
							 | 
						||
| 
								 | 
							
								  width: 20px;
							 | 
						||
| 
								 | 
							
								  height: 20px;
							 | 
						||
| 
								 | 
							
								  overflow: hidden;
							 | 
						||
| 
								 | 
							
								  background-color: rgb(0, 0, 0, 0.2);
							 | 
						||
| 
								 | 
							
								  border: 1px solid white;
							 | 
						||
| 
								 | 
							
								  border-radius: 50%;
							 | 
						||
| 
								 | 
							
								  opacity: 0.9;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								._play {
							 | 
						||
| 
								 | 
							
								  margin-left: 2px;
							 | 
						||
| 
								 | 
							
								  border-top: 4px solid transparent;
							 | 
						||
| 
								 | 
							
								  border-bottom: 4px solid transparent;
							 | 
						||
| 
								 | 
							
								  border-left: 8px solid white;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								._pause {
							 | 
						||
| 
								 | 
							
								  width: 8px;
							 | 
						||
| 
								 | 
							
								  height: 8px;
							 | 
						||
| 
								 | 
							
								  background-color: white;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/* 海报 */
							 | 
						||
| 
								 | 
							
								._poster {
							 | 
						||
| 
								 | 
							
								  display: flex;
							 | 
						||
| 
								 | 
							
								  align-items: center;
							 | 
						||
| 
								 | 
							
								  justify-content: center;
							 | 
						||
| 
								 | 
							
								  width: 70px;
							 | 
						||
| 
								 | 
							
								  height: 70px;
							 | 
						||
| 
								 | 
							
								  background-color: #e6e6e6;
							 | 
						||
| 
								 | 
							
								  background-size: contain;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/* 标题栏 */
							 | 
						||
| 
								 | 
							
								._title {
							 | 
						||
| 
								 | 
							
								  flex: 1;
							 | 
						||
| 
								 | 
							
								  margin: 4px 0 0 14px;
							 | 
						||
| 
								 | 
							
								  text-align: left;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								._author {
							 | 
						||
| 
								 | 
							
								  width: 45px;
							 | 
						||
| 
								 | 
							
								  font-size: 12px;
							 | 
						||
| 
								 | 
							
								  color: #888;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								._name {
							 | 
						||
| 
								 | 
							
								  width: 140px;
							 | 
						||
| 
								 | 
							
								  font-size: 15px;
							 | 
						||
| 
								 | 
							
								  line-height: 39px;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								._author,
							 | 
						||
| 
								 | 
							
								._name {
							 | 
						||
| 
								 | 
							
								  overflow: hidden;
							 | 
						||
| 
								 | 
							
								  text-overflow: ellipsis;
							 | 
						||
| 
								 | 
							
								  white-space: nowrap;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/* 进度条 */
							 | 
						||
| 
								 | 
							
								._slider {
							 | 
						||
| 
								 | 
							
								  position: absolute;
							 | 
						||
| 
								 | 
							
								  right: 16px;
							 | 
						||
| 
								 | 
							
								  bottom: 8px;
							 | 
						||
| 
								 | 
							
								  width: 140px;
							 | 
						||
| 
								 | 
							
								  margin: 0;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/* 播放时间 */
							 | 
						||
| 
								 | 
							
								._time {
							 | 
						||
| 
								 | 
							
								  margin: 7px 14px 0 0;
							 | 
						||
| 
								 | 
							
								  font-size: 12px;
							 | 
						||
| 
								 | 
							
								  color: #888;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/* 响应式布局,大屏幕用更大的尺寸 */
							 | 
						||
| 
								 | 
							
								@media (min-width: 400px) {
							 | 
						||
| 
								 | 
							
								  ._contain {
							 | 
						||
| 
								 | 
							
								    width: 380px;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  ._button {
							 | 
						||
| 
								 | 
							
								    width: 26px;
							 | 
						||
| 
								 | 
							
								    height: 26px;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  ._poster {
							 | 
						||
| 
								 | 
							
								    width: 90px;
							 | 
						||
| 
								 | 
							
								    height: 90px;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  ._author {
							 | 
						||
| 
								 | 
							
								    width: 60px;
							 | 
						||
| 
								 | 
							
								    font-size: 15px;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  ._name {
							 | 
						||
| 
								 | 
							
								    width: 180px;
							 | 
						||
| 
								 | 
							
								    font-size: 19px;
							 | 
						||
| 
								 | 
							
								    line-height: 55px;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  ._slider {
							 | 
						||
| 
								 | 
							
								    right: 20px;
							 | 
						||
| 
								 | 
							
								    bottom: 10px;
							 | 
						||
| 
								 | 
							
								    width: 180px;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  ._time {
							 | 
						||
| 
								 | 
							
								    font-size: 15px;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								</style>
							 |