直接上效果:
体验:
自定义系列>>音乐播放器
功能描述:
1、可播放、暂停内置的一条音乐
2、可控制播放进度
3、音乐封面在播放过程中实现旋转动画
4、背景图片表现为音乐封面的高斯模糊
5、离开页面音乐停止播放
完整代码:
js
const app = getApp()
const innerAudioContext = wx.createInnerAudioContext();
var _animation; // 动画实体
var _animationIndex = 0; // 动画执行次数index(当前执行了多少次)
var _animationIntervalId = -1; // 动画定时任务id,通过setInterval来达到无限旋转,记录id,用于结束定时任务
const _ANIMATION_TIME = 200; // 动画播放一次的时长ms
Page({
data: {
menuButtonBoundingClientRect:{},
systemInfo:{},
clickPlay: true,
playSrc: 'play-normal.png',
preSrc: './pre-normal.png',
nextSrc: './next-normal.png',
animation: '',
isRing: false,
audiolist: [
{
audiosrc: 'https://6e6f-normal-env-4598.tcb.qcloud.la/mp3/04a9_485d_772f_2493d24b7f1b040256002d8283a4cad0.mp3?sign=b4889da8c5373cad5279086359f27fcc&t=1583118236',
audioImage:"cloud://normal-env-t6f-normal-etac-1300924598/meinv/u=2367451530,795833927&fm=26&gp=0.jpg",audioName:"上海滩",
}
],
isPlayAudio: false,
audioSeek: 0,
audioDuration: 0,
showTime1: '00:00',
showTime2: '00:00',
audioTime: 0,
textVoiceMarginTop: '',
viewCircleContainerMarginTop: '',
audioPlayerMarginTop: '',
controlContainerMarginTop: '',
},
go2Home() {
wx.navigateBack({
delta: 1,
})
},
onLoad: function (options) {
var that = this
wx.getSystemInfo({
success: function (res) {
//model中包含着设备信息
console.log(res)
that.setData({
menuButtonBoundingClientRect: wx.getMenuButtonBoundingClientRect(),
systemInfo:res
})
var model = res.model
if (model.search('iPhone X') != -1) {
app.globalData.isIpx = true;
} else {
app.globalData.isIpx = false;
}
}
})
console.log(app.globalData.isIpx)
let isPhone = app.globalData.isIpx;
if (isPhone) {
this.setData({
textVoiceMarginTop: "105rpx",
viewCircleContainerMarginTop: "100rpx",
audioPlayerMarginTop: "200rpx",
controlContainerMarginTop: "100rpx",
})
} else {
this.setData({
textVoiceMarginTop: "60rpx",
viewCircleContainerMarginTop: "80rpx",
audioPlayerMarginTop: "140rpx",
controlContainerMarginTop: "30rpx"
})
}
},
touchPreStart: function () {
var preSrc = this.data.preSrc;
this.setData({
preSrc: './pre-normal.png'
})
},
touchPreEnd: function () {
var preSrc = this.data.preSrc;
this.setData({
preSrc: './pre-normal.png'
})
},
touchNextStart: function () {
var nextSrc = this.data.nextSrc;
this.setData({
nextSrc: './next-normal.png'
})
},
touchNextEnd: function () {
var nextSrc = this.data.nextSrc;
this.setData({
nextSrc: './next-normal.png'
})
},
changePlay: function () {
var playSrc = this.data.playSrc;
var isPlayAudio = this.data.isPlayAudio;
this.playAudio();
if (!isPlayAudio) {
this.startAnimationInterval()
this.setData({
playSrc: './pause-normal.png',
})
} else {
this.stopAnimationInterval()
this.setData({
playSrc: './play-normal.png',
})
}
},
onReady: function () {
_animationIndex = 0;
_animationIntervalId = -1;
this.data.animation = '';
},
onShow: function () {
this.Initialization();
this.loadaudio();
_animation = wx.createAnimation({
duration: _ANIMATION_TIME,
timingFunction: 'linear', // "linear","ease","ease-in","ease-in-out","ease-out","step-start","step-end"
delay: 0,
transformOrigin: '50% 50% 0'
})
},
onHide: function () {
this.isShow = false;
},
/**
* 实现image旋转动画,每次旋转 120*n度
*/
rotateAni: function (n) {
_animation.rotate(5 * (n)).step()
this.setData({
animation: _animation.export()
})
},
/**
* 开始旋转
*/
startAnimationInterval: function () {
var that = this;
if (that.data.isRing) {
return
}
that.setData({
isRing: true
})
that.rotateAni(++_animationIndex); // 进行一次旋转
_animationIntervalId = setInterval(function () {
that.rotateAni(++_animationIndex);
}, _ANIMATION_TIME); // 每间隔_ANIMATION_TIME进行一次旋转
},
/**
* 停止旋转
*/
stopAnimationInterval: function () {
var that = this;
if (!that.data.isRing) {
return
}
that.setData({
isRing: false
})
if (_animationIntervalId > 0) {
clearInterval(_animationIntervalId);
_animationIntervalId = 0;
}
},
//初始化播放器,获取duration
Initialization() {
var t = this;
if (this.data.audiolist[0].audiosrc.length != 0) {
//设置src
innerAudioContext.src = this.data.audiolist[0].audiosrc;
//运行一次
innerAudioContext.play();
innerAudioContext.pause();
innerAudioContext.onCanplay(() => {
//初始化duration
innerAudioContext.duration
setTimeout(function () {
//延时获取音频真正的duration
var duration = innerAudioContext.duration;
var min = parseInt(duration / 60);
var sec = parseInt(duration % 60);
if (min.toString().length == 1) {
min = `0${min}`;
}
if (sec.toString().length == 1) {
sec = `0${sec}`;
}
t.setData({ audioDuration: innerAudioContext.duration, showTime2: `${min}:${sec}` });
}, 1000)
})
}
},
//拖动进度条事件
sliderChange(e) {
var that = this;
innerAudioContext.src = this.data.audiolist[0].audiosrc;
//获取进度条百分比
var value = e.detail.value;
this.setData({ audioTime: value });
var duration = this.data.audioDuration;
//根据进度条百分比及歌曲总时间,计算拖动位置的时间
value = parseInt(value * duration / 100);
//更改状态
this.setData({
audioSeek: value,
isPlayAudio: true,
playSrc: './pause-normal.png',
});
//调用seek方法跳转歌曲时间
innerAudioContext.seek(value);
//播放歌曲
innerAudioContext.play();
this.startAnimationInterval()
},
//播放、暂停按钮
playAudio() {
//获取播放状态和当前播放时间
var isPlayAudio = this.data.isPlayAudio;
var seek = this.data.audioSeek;
innerAudioContext.pause();
//更改播放状态
this.setData({ isPlayAudio: !isPlayAudio })
if (isPlayAudio) {
//如果在播放则记录播放的时间seek,暂停
this.setData({ audioSeek: innerAudioContext.currentTime });
} else {
//如果在暂停,获取播放时间并继续播放
innerAudioContext.src = this.data.audiolist[0].audiosrc;
if (innerAudioContext.duration != 0) {
this.setData({ audioDuration: innerAudioContext.duration });
}
//跳转到指定时间播放
innerAudioContext.seek(seek);
innerAudioContext.play();
}
},
loadaudio() {
var that = this;
//设置一个计步器
this.data.durationIntval = setInterval(function () {
//当歌曲在播放时执行
if (that.data.isPlayAudio == true) {
//获取歌曲的播放时间,进度百分比
var seek = that.data.audioSeek;
var duration = innerAudioContext.duration;
var time = that.data.audioTime;
time = parseInt(100 * seek / duration);
//当歌曲在播放时,每隔一秒歌曲播放时间+1,并计算分钟数与秒数
var min = parseInt((seek + 1) / 60);
var sec = parseInt((seek + 1) % 60);
//填充字符串,使3:1这种呈现出 03:01 的样式
if (min.toString().length == 1) {
min = `0${min}`;
}
if (sec.toString().length == 1) {
sec = `0${sec}`;
}
var min1 = parseInt(duration / 60);
var sec1 = parseInt(duration % 60);
if (min1.toString().length == 1) {
min1 = `0${min1}`;
}
if (sec1.toString().length == 1) {
sec1 = `0${sec1}`;
}
//当进度条完成,停止播放,并重设播放时间和进度条
if (time >= 100) {
innerAudioContext.stop();
that.setData({ audioSeek: 0, audioTime: 0, audioDuration: duration, isPlayAudio: false, showTime1: `00:00`, playSrc: './play-normal.png' });
that.stopAnimationInterval()
return false;
}
//正常播放,更改进度信息,更改播放时间信息
that.setData({ audioSeek: seek + 1, audioTime: time, audioDuration: duration, showTime1: `${min}:${sec}`, showTime2: `${min1}:${sec1}` });
}
}, 1000);
},
onUnload: function () {
innerAudioContext.stop();
this.stopAnimationInterval()
}
})
wxml
<image class="image-background" src="{{audiolist[0].audioImage}}"></image>
<view style="width:{{menuButtonBoundingClientRect.height+20}}px;height:{{menuButtonBoundingClientRect.height-1}}px ;position: fixed;z-index: 11;opacity:0.5;border-radius:{{menuButtonBoundingClientRect.height/2}}px;border:1rpx solid #ededed;top:{{menuButtonBoundingClientRect.top}}px;left:{{systemInfo.windowWidth-menuButtonBoundingClientRect.right}}px" hover-class="hover-home" class="home" hover-start-time="20" hover-stay-time="70" bindtap="go2Home">
</view>
<image style="position: fixed;z-index: 12;width:{{menuButtonBoundingClientRect.height-10}}px;height:{{menuButtonBoundingClientRect.height-10}}px;top:{{menuButtonBoundingClientRect.top+5}}px;left:{{systemInfo.windowWidth-menuButtonBoundingClientRect.right+15}}px" src="../navigationBar/utils/home.png" bindtap="go2Home"></image>
<view class="container">
<text class="text-voice" style="margin-top:{{textVoiceMarginTop}}">{{audiolist[0].audioName}}</text>
<view class="view-circle-container" style="margin-top:{{viewCircleContainerMarginTop}}">
<view class="view-circle"></view>
<image class="image-circle" src="{{audiolist[0].audioImage}}" animation="{{animation}}"></image>
</view>
<view class='audioPlayer' style="margin-top:{{audioPlayerMarginTop}}">
<view class='player'>
<view class='audioControls'>
<view class='flex'>
<view class='bottom'>
<image bindtap="changePlay"
src="{{playSrc}}"></image>
</view>
<view class='slider'>
<slider bindchange='sliderChange' activeColor='red' block-size="12" value='{{audioTime}}' />
</view>
<view class='time'>
{{showTime1}}/{{showTime2}}
</view>
</view>
</view>
</view>
</view>
<view class="control-container" style="margin-top:{{controlContainerMarginTop}}">
<image class="icon-pre" src="{{preSrc}}" bindtouchstart="touchPreStart" bindtouchend="touchPreEnd"></image>
<image class="icon-play" bindtap="changePlay"
src="{{playSrc}}"></image>
<image class="icon-next" src="{{nextSrc}}" bindtouchstart="touchNextStart" bindtouchend="touchNextEnd"></image>
</view>
</view>
wxss
page{
width:100%;
height: 100%;
}
.image-background{
width: 100%;
height: 100%;
filter: blur(10px)
}
.container{
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
position: fixed;
z-index: 10
}
.control-container{
display: flex;
justify-content: center;
align-items: center;
flex-direction: row;
}
.icon-play{
width:80rpx;
height: 80rpx;
}
.icon-pre{
width:60rpx;
height: 60rpx;
margin-right: 80rpx;
}
.icon-next{
width:60rpx;
height: 60rpx;
margin-left: 80rpx;
}
.text-voice{
font-size: 36rpx;
color: white;
}
.image-logo{
height: 60rpx;
width: 60rpx;
}
.view-circle-container{
display: flex;
text-align: center;
justify-content: center;
align-items: center;
}
.view-circle{
width: 624rpx;
height: 624rpx;
border-radius: 317rpx;
background-color: #000000;
opacity: 0.6;
z-index: -1;
/* position: fixed; */
}
.image-circle{
width: 600rpx;
height: 600rpx;
border-radius: 300rpx;
z-index: 100;
position: fixed;
}
.flex{
display: flex;
justify-content: center;
align-items: center
}
.audioPlayer{
width: 100%;
margin-bottom: 30rpx;
box-sizing: border-box;
padding: 20rpx 30rpx;
}
.player{
width: 100%;
height: 100%;
position: relative;
}
.audioBack{
width: 100%;
height: 100%;
}
.audioControls{
width: 100%;
height: 80rpx;
background: black;
opacity: .6;
position: absolute;
bottom: 0;
color: white;
font-size: 6pt;
line-height: 80rpx;
text-align: center;
}
.audioControls .bottom{
width: 60rpx;
height: 100%;
display: flex;
justify-content: center;
align-items: center
}
.audioControls .bottom image{
width: 40rpx;
height: 40rpx;
}
.audioControls .slider{
width: 520rpx;
height: 100%;
}
.slider slider{
width: 95%;
margin-left: 4%;
margin-right: 0;
}
.audioControls .time{
width: 120rpx;
height: 100%;
}
.home{
background: white
}
.hover-home{
background: #ededed
}