效果图
代码全集
html代码
<view class="box1111" wx:else bindtap="show222">
<view class="box233"></view>
<view class="line" wx:if='{{isScroll}}'> </view>
<view class="time2" wx:if='{{isScroll}}'>
{{scrollTime}}</view>
<view>
<view class="time3" bindtap="plays" style="z-index:1000" wx:if='{{isScroll}}'>播放</view>
<scroll-view scroll-y="true" class="scr" scroll-top='{{lyricIndex*30}}' scroll-with-animation bindtouchstart="touchstart" bindtouchend="touchend" bindscroll='scroll'>
<view style="margin-top:150px;">
<view wx:for="{{lyric}}" wx:key="index" class="box1231">
<view style=" position: relative;">
<view style="height:30px;position:relative;">{{item}}</view>
<view style="height:30px" id="hover" class="{{yinyueScrollIndex===index?'over':''}}" style="animation-duration:{{animations[index]-0.2}}s;">{{item}}</view>
<!--副歌词-->
</view>
</view>
<!-- 歌词结束吧歌词顶在固定位置 -->
<view style="height:50vh;"></view>
</view>
</scroll-view>
</view>
</view>
<!-- 进度条 -->
<view class="sss3">
<view class="pro">
<view>{{currentTime}}</view>
<view class="pro1">
<slider bindchange="changed1" min="0" max="{{max}}" bindchanging="changing" value="{{value}}" block-size="12" />
</view>
<view>{{totalTime}}</view>
</view>
</view>
css
.box1111 {
width: 100%;
/* border: 1px solid firebrick; */
text-align: center;
position: relative;
}
.line {
width: 100%;
border-bottom: 1px solid forestgreen;
position: absolute;
top: 220px;
height: 50px;
color: firebrick;
}
.time2 {
position: absolute;
top: 280px;
left: 10px;
color: firebrick;
}
.time3 {
position: absolute;
top: 280px;
right: 10px;
color: firebrick;
}
.box233 {
width: 100%;
height: 100px;
/* border: 1px solid forestgreen; */
}
.scr {
height: 300px;
/* border: 1px solid firebrick; */
}
.font11 {
color: rgb(178, 178, 212);
}
.box1231 {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
text-align: center;
/* border: 1px solid #ccc; */
position: relative;
}
#hover {
position: absolute;
pointer-events: none;
top: 0;
left: 0;
overflow: hidden;
width: 100%;
white-space: nowrap;
}
.over {
animation: test;
animation-timing-function: linear;
/* 速度 */
animation-delay: 0s;
/* 延时 */
/* 动画迭代计数 */
animation-direction: normal;
color: rgb(19, 103, 212);
}
@keyframes test {
0% {
width: 0px;
}
100% {
width: 100px;
}
}
js代码
import dayjs from '../../lib/dayjs.min.js'
const {
default: api
} = require("../../https/api")
// pages/play/play.js
let tiemout1 = '' //全局定时器 控制 多次滚动
let timeout3 = '' //控制获取时间
let bg = wx.getBackgroundAudioManager()
Page({
/**
* 页面的初始数据
*/
data: {
id: [], //获取的id
currentIndex: 0, //获取到的index
// currtentId:[],//随机选择的id
playUrl: '', //播放地址
playList: [], //歌曲详情
totalTime: '', //总时长
max: 0, //滑块最大值
value: 0, //进度条进程
currentTime: 0, //当前时长
show: true, //控制播放与暂停
flag: false, //是否在拖动滑块
flag1: 1, //默认顺序循环
lyric: [], //存放歌词
lyricTime: [], //存放歌曲时间
lyricIndex: 0, //歌词当前行
showlyric: false, //是否显示歌词
isScroll: false, //是否滑动
scrollTime: '', //滚动显示的时间 00:00
scrollIndex: 0, //滚动显示时间的地址
moreTouch: false, //是否多次滑动,
animations: [],//动画时长
yinyueScrollIndex:0//音乐自动滚动地址
},
// 是否开始触摸
touchstart() {
this.setData({
isScroll: true,
moreTouch: true
})
if (tiemout1) {
clearInterval(tiemout1)
}
},
//触摸结束
touchend() {
tiemout1 = setInterval(() => {
this.setData({
moreTouch: false,
isScroll: false
})
clearInterval(tiemout1)
}, 3000);
this.setData({
isScroll: false
})
},
//滚动计算地址
scroll(e) {
let index111 = Math.round(e.detail.scrollTop / 30)
this.setData({
scrollTime: dayjs(this.data.lyricTime[index111] * 1000).format('mm:ss'),
scrollIndex: this.data.lyricTime[index111]
})
// console.log('111',this.data.lyricTime[index111])
},
//是否显示歌词
show222() {
if (this.data.showlyric) {
this.setData({
showlyric: false
})
} else {
this.setData({
showlyric: true
})
}
},
//拖动点击播放
plays() {
console.log(11)
console.log(this.data.scrollIndex)
bg.seek(this.data.scrollIndex)
},
//切换循环
qiehuan() {
if (this.data.flag1 == 3) {
this.setData({
flag1: 0
})
}
this.setData({
flag1: ++this.data.flag1
})
console.log(this.data.flag1)
},
//拖动滑块后
changed1(e) {
// console.log('拖动后', e.detail.value)
let a = e.detail.value
// let b = dayjs(a * 1000).format("mm:ss")
this.setData({
flag: false,
})
bg.seek(a)
},
//拖动滑块中
changing(e) {
// console.log('滑块数字', e.detail.value)
let b = e.detail.value
let a = dayjs(b * 1000).format("mm:ss")
// console.log(a)
this.setData({
currentTime: a,
flag: true
})
},
//播放
play() {
// console.log(this.data.show)
this.setData({
show: true,
flag: false
})
// console.log(this.data.show)
bg.play()
},
//暂停
pause() {
this.setData({
show: false,
flag: true
})
// console.log(this.data.show)
bg.pause();
},
//下一首
xia() {
if (this.data.flag1 === 1) {
if (this.data.currentIndex < this.data.id.length) {
let temp = ++this.data.currentIndex
this.setData({
currentIndex: temp
})
}
if (this.data.currentIndex === this.data.id.length) {
this.setData({
currentIndex: 0
})
}
this.songDetails(this.data.id[this.data.currentIndex])
this.songUrl(this.data.id[this.data.currentIndex])
} else if (this.data.flag1 === 2) {
let a = parseInt(Math.random() * this.data.id.length)
console.log('随机值', a)
this.setData({
currentIndex: a
})
this.songDetails(this.data.id[this.data.currentIndex])
this.songUrl(this.data.id[this.data.currentIndex])
} else {
console.log('单曲')
bg.stop()
this.songDetails(this.data.id[this.data.currentIndex])
this.songUrl(this.data.id[this.data.currentIndex])
}
this.setData({
show: true,
flag: false,
lyricIndex: 0
})
},
// 上一首
shang() {
if (this.data.flag1 === 1) {
if (this.data.currentIndex > 0) {
let temp = --this.data.currentIndex
this.setData({
currentIndex: temp
})
} else if (this.data.currentIndex === '0') {
let temp = this.data.id.length
console.log(this.data.id.length - 1)
this.setData({
currentIndex: this.data.id.length - 1
})
}
this.songDetails(this.data.id[this.data.currentIndex])
this.songUrl(this.data.id[this.data.currentIndex])
} else if (this.data.flag1 === 2) {
let a = parseInt(Math.random() * this.data.id.length)
// console.log('随机值', a)
this.setData({
currentIndex: a
})
this.songDetails(this.data.id[this.data.currentIndex])
this.songUrl(this.data.id[this.data.currentIndex])
} else {
// console.log('单曲')
bg.stop()
this.songDetails(this.data.id[this.data.currentIndex])
this.songUrl(this.data.id[this.data.currentIndex])
}
this.setData({
show: true,
flag: false,
lyricIndex: 0
})
},
// 获取歌曲url
songUrl(a) {
wx.showLoading({
title: '加载中',
})
api.songUrl(a).then(res => {
if (res.code === 200) {
wx.hideLoading()
this.setData({
playUrl: res.data[0].url,
})
bg.src = res.data[0].url
bg.title = "播放音乐"
this.getLyrics(res.data[0].id)
this.songDetails(res.data[0].id)
}
}).catch(err => {
wx.hideLoading()
console.log(err)
})
},
//获取歌词
getLyrics(a) {
api.getLyrics(a).then(res => {
//对请求的数据进行处理
let b = res.lrc.lyric.split('\n')
console.log(b,'拿到歌词处理后')
//存放歌词的临时变量
let c = []
//正则表达式
let a1 = new RegExp('\\[[0-9]*:[0-9]*.[0-9]*\\]', 'g')
//存放处理日期的数组
let lyricTime1 = []
//正则法则提取时间
// let a2 = /[0-9]*:[0-9]*/
b.map(item => {
//处理时间
let arr = item.match(a1)
//排除空数据
if (arr) {
arr = arr[0].replace('[', '').replace(']', '')
//分别截取分、秒 并进行处理 格式120s
let time = Number(arr.split(':')[0] * 60) + Number(arr.split(':')[1].split('.')[0])
//歌词日期处理
lyricTime1.push(time)
//去除日期的歌词处理
let ss = item.replace(a1, '')
c.push(ss)
}
})
let tiemr2 = []
for (let i = 0; i < lyricTime1.length; i++) {
let b=lyricTime1[i+1]-lyricTime1[i]
tiemr2.push(b)
if(lyricTime1.length-1===i){
tiemr2.push(8)
}
}
this.setData({
lyric: c,
lyricTime: lyricTime1,
animations:tiemr2
})
console.log(tiemr2,'时间差')
console.log('歌词', this.data.lyricTime)
// console.log('歌词', this.data.lyric)
}).catch(err => {
console.log(err)
})
},
//获取歌曲详情
songDetails(a) {
api.songDetails(a).then(res => {
// console.log("音乐详情", res)
this.setData({
playList: res.songs[0]
})
// console.log('音乐详情', this.data.playList)
}).catch(err => {
console.log(err)
})
},
//后退
back() {
wx.navigateBack({
delta: 1,
})
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
this.getLyrics()
let a = options.id
let c = a.split(',')
let b = options.index
this.setData({
id: c,
currentIndex: b
})
this.songUrl(this.data.id[this.data.currentIndex])
timeout3 = setInterval(() => {
//文本高亮
if (this.data.moreTouch === false) {
let value111 = parseInt(bg.currentTime)
//对每一行进行处理
for (let i = 0; i < this.data.lyricTime.length; i++) {
//对最后一行进行处理
if (value111 > this.data.lyricTime[this.data.lyricTime.length - 1]) {
//
this.setData({
lyricIndex: this.data.lyricTime.length - 1
})
break
}
//其他行进行处理
if (value111 > this.data.lyricTime[i] && value111 < this.data.lyricTime[i + 1]) {
this.setData({
lyricIndex: i,
})
break
}
}
}
//拖动中文本滚动且高亮
let value111 = parseInt(bg.currentTime)
//对每一行进行处理
for (let i = 0; i < this.data.lyricTime.length; i++) {
//对最后一行进行处理
if (value111 > this.data.lyricTime[this.data.lyricTime.length - 1]) {
//
this.setData({
yinyueScrollIndex: this.data.lyricTime.length - 1
})
break
}
//其他行进行处理
if (value111 > this.data.lyricTime[i] && value111 < this.data.lyricTime[i + 1]) {
this.setData({
yinyueScrollIndex: i,
})
break
}
}
//显示的进度条时间
if (this.data.flag === false) {
let a = dayjs(bg.duration * 1000).format("mm:ss") //总时长
let b = parseInt(bg.duration) //滑块最大值
let c = dayjs(bg.currentTime * 1000).format('mm:ss') //当前时长
let d = parseInt(bg.currentTime) //滑块值
this.setData({
totalTime: a, ///总时长 对数据进行处理后
max: b, //滑块最大数
currentTime: c,
value: d,
})
}
}, 500)
// 监听背景音频自然播放结束事件
bg.onEnded(() => {
this.xia()
})
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
//离开页面 把文本高亮关掉
// clearInterval(timeout2)
clearInterval(timeout3)
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {
}
})