<wxs src="/wxs/format.wxs" module="fmt" />
<view class="song">
<view class="album">
<image src="{{song.al.picUrl}}" class="poster" mode="widthFix"/>
</view>
<view class="title">{{song.name}}</view>
<view class="author">{{fmt.formatAc(song.ar)}}</view>
<view class="lyric">{{currentLyric}}</view>
<slider
block-size="12"
class="progress"
value="{{audioProgress}}"
bindchange="sliderBindchange"
bindchanging="sliderBindchanging"
/>
<view class="time">
<text>{{fmt.formatTime(currentTime)}}</text>
<text>{{fmt.formatTime(durationTime)}}</text>
</view>
<view class="operation">
<image
wx:if="{{modeNames[playMode]}}"
class="btn mode"
src="{{'./images/play_' + modeNames[playMode] + '.png'}}"
bindtap="changePlayModeAction"
/>
<image
class="btn prev"
src="./images/play_prev.png"
bindtap="prevSong"
/>
<image
class="btn pause"
src="{{ isPlaying ? './images/play_pause.png' : './images/play_resume.png' }}"
bindtap="changePlayerStatus"
/>
<image
class="btn next"
src="./images/play_next.png"
bindtap="nextSong"
/>
<image
class="btn music"
src="./images/play_music.png"
/>
</view>
</view>
import { throttle } from 'underscore'
const audioContext = wx.createInnerAudioContext()
Component({
properties: {
song: {
type: Object,
value: {}
},
lyrics: {
type: Array,
value: []
}
},
data: {
isPlaying: true,
isMoving: false,
currentIndex: 0,
currentTime: 0,
durationTime: 0,
audioProgress: 0,
currentLyric: 0
},
lifetimes: {
attached() {
this.playMusic()
this.setData({
durationTime: this.properties.song.dt
})
}
},
methods: {
changePlayerStatus() {
this.setData({
isPlaying: !this.data.isPlaying
})
if (audioContext.paused) {
audioContext.play()
} else {
audioContext.pause()
}
},
async playMusic() {
audioContext.stop()
audioContext.src = `https://music.163.com/song/media/outer/url?id=${this.properties.song.id}.mp3`
audioContext.autoplay = true
audioContext.onError(err => {
console.log('audioContext error', err);
})
audioContext.onTimeUpdate(throttle(() => {
if (!this.data.isMoving) {
this.setData({
currentTime: audioContext.currentTime * 1000,
audioProgress: this.data.currentTime / this.durationTime * 100
})
this.matchCurrentMusic()
}
}, 1000))
audioContext.onWaiting(() => {
audioContext.pause()
})
audioContext.onCanplay(() => {
if (audioContext.paused) {
audioContext.play()
} else {
this.matchCurrentMusic()
}
})
},
sliderBindchange(e) {
if (!audioContext.buffered) {
return
}
const progress = e.detail.value
this.setData({
audioProgress: progress
})
const currentTime = this.data.durationTime * progress / 100
audioContext.seek(currentTime / 1000)
this.data.isMoving = false
},
sliderBindchanging: throttle(function(e) {
if (this.data.isWaiting) {
return
}
this.data.isMoving = true
this.setData({
currentTime: this.data.durationTime * e.detail.value / 100
})
}, 500),
matchCurrentMusic() {
let currentTime = parseInt(audioContext.currentTime * 1000)
let currentIndex = this.properties.lyrics.length - 1
for (const index in this.properties.lyrics) {
const lyric = this.properties.lyrics[index]
if (lyric.time > currentTime) {
currentIndex = index - 1
if (currentIndex < 0) {
currentIndex = 0
}
}
}
if (this.data.currentIndex !== currentIndex) {
this.data.currentIndex = currentIndex
this.setData({
currentLyric: this.properties.lyrics[currentIndex].lyric
})
}
}
}
})