一、实现效果
- 点击播放按钮歌曲进行播放,点击暂停停止播放
- 显示歌曲当前播放时间和总时间
- 随着歌曲播放进度条变红,点击进度条任意位置都可以到达(快进操作)
- 歌唱哪句歌词高亮,一句结束自动滚动
注意:实现整个效果,需要将每个步骤的代码整合在一起
二、步骤
1.搭建静态页面
<!--pages/music1/index.wxml-->
<view class="container">
<!-- 歌名和演唱者 -->
<view class="header">
<view>三十岁的女人</view>
<view class="author">赵雷</view>
</view>
<!-- 歌词部分,可滚动 -->
<scroll-view class="middle" scroll-y="true">
<view>歌词放置处</view>
<view>歌词放置处</view>
<view>歌词放置处</view>
<view>歌词放置处</view>
<view>歌词放置处</view>
<view>歌词放置处</view>
<view>歌词放置处</view>
<view>歌词放置处</view>
<view>歌词放置处</view>
<view>歌词放置处</view>
<view>歌词放置处</view>
<view>歌词放置处</view>
<view>歌词放置处</view>
<view>歌词放置处</view>
<view>歌词放置处</view>
<view>歌词放置处</view>
<view>歌词放置处</view>
<view>歌词放置处</view>
<view>歌词放置处</view>
<view>末尾</view>
</scroll-view>
<!-- 底部进度条 -->
<view class="footer">
<view class="progress-bar">
<view>00:00</view>
<view class="progress-line">
<view class="progress-bg"></view>
<view class="progress-red"></view>
<view class="progress-dot">
<icon class="iconfont iconyuandian1"></icon>
</view>
</view>
<view>00:00</view>
</view>
</view>
<!-- 操作按钮 -->
<view class="btn-group">
<view class="prev">
<icon class="iconfont iconshangyishou"></icon>
</view>
<view class="play">
<icon class="iconfont iconweibiaoti--"></icon>
</view>
<view class="next">
<icon class="iconfont iconxiayishou"></icon>
</view>
</view>
</view>
@import '/assets/fonts/iconfont.wxss';
.container {
width: 100vw;
height: 100vh;
background: #000;
color: #fff;
}
.header {
padding: 20rpx;
line-height: 30px;
text-align: center;
}
.header .author{
font-size: 16px;
}
.middle{
height: 55vh;
text-align: center;
margin-top: 20px;
font-size: 16px;
}
.middle view{
height: 60rpx;
line-height: 60rpx;
}
.footer{
padding: 10px;
width: 100%;
box-sizing: border-box;
margin-top: 30px;
}
.progress-bar{
display: flex;
justify-content: space-between;
align-items: center;
}
.progress-line{
flex: 1;
margin: 0 5px;
position: relative;
}
.progress-bg{
width: 100%;
background: #fff;
height: 4rpx;
}
.progress-red{
width: 30%;
height:100%;
position: absolute;
background: red;
top: 0;
left: 0;
}
.progress-dot{
position: absolute;
top: -15px;
left: -5px;
}
.btn-group{
display: flex;
justify-content: center;
align-items: center;
}
.play{
margin: 0 5px;
}
.play icon{
font-size: 50px;
}
.prev icon, .next icon{
font-size: 30px;
}
2.点击播放按钮歌曲进行播放,点击暂停停止播放
思路:使用
isplaying来控制样式,false显示播放图标,歌曲暂停播放,true显示暂停图标,歌曲播放
- 2.1放置
audio
,不显示在页面中
<audio src="{{src}}" id="myAudio" >
- 2.2点击播放图标可以切换为暂停图标,并且可以控制歌曲的播放和暂停
<view class="play" bindtap="changeState">
<icon class="iconfont {{isplaying===false?' iconweibiaoti--':'iconzanting'}}"></icon>
</view>
data: {
isplaying: false, //控制
src: 'http://audio01.dmhmusic.com/71_53_T10046221715_128_4_1_0_sdk-cpm/0206/M00/6D/81/ChR47FsrnyWAKVx0AE-m4DXLKqo190.mp3?xcode=039efb32a1d055924b8d829e8d3f68b006abe09', //音乐地址
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function() {
// 获取当前页面的audio
this.audioCtx = wx.createAudioContext('myAudio')
},
changeState() {
if (!this.data.isplaying) {
this.audioCtx.play()//歌曲播放
} else {
this.audioCtx.pause()//歌曲暂停
}
this.setData({
isplaying: !this.data.isplaying
})
},
3.显示歌曲当前播放时间和总时间,随着歌曲播放进度条变红,
点击进度条任意位置都可以到达(快进,后退操作);
思路
- 播放时会触发
bindtimeupdate
,获取当前时间currentTime
和歌曲总时间duration
;- 定义变量
progressPercent
控制progress-red
的宽度,progress-dot
的left;- 获取当前位置
offsetX
占progress-line宽度width
百分比p
,使用this.audioCtx.seek(p在总时间的占比)跳转歌曲的位置(实现快进后退)
<view class="progress-line" bindtap="seek">
<view class="progress-bg"></view>
<view class="progress-red" style="width:{{progressPercent}}%;"></view>
<view class="progress-dot" style="left:calc({{progressPercent}}% - 5px)">
<icon class="iconfont iconyuandian1"></icon>
</view>
</view>
data: {
currentTime: '00:00', //当前时间
duration: "00:00", //总时间
progressPercent: 0, //百分比,控制宽度
width: 0, //获取progress-line的宽度
durationTime:0,//总时间,跳转指定位置使用
},
// 化为时分秒
formatMs2Obj(total) {
var h = this.repairZero(Math.floor(total / 3600))
var m = this.repairZero(Math.floor((total - h * 3600) / 60))
var s = this.repairZero(Math.floor(total - h * 3600 - m * 60))
//ES6 结构 h:h
return {
h,
m,
s
}
},
/**
* 补零
*/
repairZero(num) {
return num < 10 ? ("0" + num) : num
},
timeupdate(e) {
var obj1 = this.formatMs2Obj(e.detail.currentTime)
var obj2 = this.formatMs2Obj(e.detail.duration)
var str1 = obj1.m + ":" + obj1.s
var str2 = obj2.m + ":" + obj2.s
//
if (this.data.currentTime !== str1) {
// 更新当前时间
this.setData({
currentTime: str1,
progressPercent: e.detail.currentTime * 100 / e.detail.duration
})
}
// 赋值总时间,每次总时间一致不用赋值
if (this.data.duration !== str2) {
this.data.durationTime=e.detail.duration//总时间
this.setData({
duration: str2
})
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function(options) {
// 获取节点,获取progress-line宽度width
var query = wx.createSelectorQuery()
var that = this;
query.select('.progress-line').boundingClientRect(function(rect) {
that.setData({
width: rect.width//宽度
})
}).exec();
},
seek(e) {
// 点击白色进度条任意位置,红色进度条到达点击处
// 获取当前位置
var offsetX = e.touches[0].pageX - e.currentTarget.offsetLeft
// 获取当前位置站总宽度的百分比
var p = offsetX / this.data.width
// seek跳转至指定位置,
// this.data.durationTime*p,获取位置百分比在总时间中的占比
this.audioCtx.seek(this.data.durationTime*p)
}
4.歌曲播放哪句歌词高亮,结束后歌词自动滚动(到第六行的时候在滚动)
思路
- 找到歌词lrc(带时间的),存储在easy-mock中(也可以根据需求存储在其他位置),需要在数组中增加分割的字符(此处加的是;)
- 分割歌词,最终目标数组分割成
[{time:" ",text:" "}]
,需要使用split,trim,substr
split() 方法用于把一个字符串分割成字符串数组
trim()去除字符串两端的空格
substr() 方法可在字符串中抽取从 start 下标开始的指定数目的字符
- 循环数组songtext来显示歌词
- 定义变量
activeIndex
控制高亮,activeIndex
等于i- scrolltop设置滚动距离,当i>5的时候才可以滚动
<scroll-view class="middle" scroll-y="true" scroll-top="{{scrolltop}}">
<view wx:for="{{songtext}}" class="{{activeIndex===index?'active':''}}">{{item.text}}</view>
</scroll-view>
data: {
songtext: '', //歌词
activeIndex: 0, //控制高亮
scrolltop: 0 //滚动距离
},
onLoad:function(){
// 获取歌词,请求easy-mock数据
wx.request({
url: 'https://www.easy-mock.com/mock/5d032195cccf932f99d7422e/xcx/songtext',
success: res => {
var arr = res.data.songtext.split(";")
// console.log(arr)
arr = arr.map(r => {
// 去除每一项的空格很重要,不然后面设置当前项是空格的,上一项不是空格仍然高亮不起作用
var arr1 = r.trim().substr(1).split("]")
return {
time: arr1[0],
text: arr1[1]
}
})
this.setData({
songtext: arr
})
}
})
},
timeupdate(e) {
//控制高亮
// 循环数组
for (var i = 0; i < this.data.songtext.length; i++) {
// 当前播放时间等于数组time时activeIndex才改变值
if (this.data.currentTime === this.data.songtext[i].time) {
// 当activeIndex不等于i时更新,当songtext有值为空格时也不更新
if (this.data.activeIndex !== i && this.data.songtext[i].text) {
this.setData({
activeIndex: i,
scrolltop: 30 * (i - 5)
})
if (i > 5) {
this.setData({
// 此处的滚动距离可以为高度*i,到第5行在进行滚动
scrolltop: 30 * (i - 5)
})
}
break
}
}
}
}