微信小程序 音乐播放代码(播放方式,歌词滚动) (更新优化版:添加文字颜色过渡)
效果图:wxml:<view class="container flex-align"><view style="text-align:center;position:relative;" class="box"><view style="color:white;">{{songs.name}}</view><view style="color
·
自动滚动时,歌词太长造成了很长的偏移,如何解决:
每次滚动的时候,获取需要展示的那一行歌词的dom节点高度位置,直接滚动到当前节点的位置就可以了。
公式:需要滚动的位置 = dom节点位置 - 歌词区域高度 / 2 - dom节点的高度/2。
类似功能:滚动到中部
项目源码:
https://download.csdn.net/download/qq_42740797/15542837
效果图:
wxml:
<view class="container flex-align">
<view style="text-align:center;position:relative;" class="box">
<view style="color:white;">{{songs.name}}</view>
<view style="color:#ddd;" class="f12">{{songs.ar[0].name}}</view>
<image src="../../images/play/download.png" style="height:60rpx;width:60rpx;" class="download" bindtap="download" />
</view>
<view class="flex-1 flex-center" wx:if="{{isLrc==false}}" bindtap="isLrc">
<image src="{{songs.al.picUrl}}" class="img {{isPlay===false?'pause':''}}" />
</view>
<!--歌词-->
<view wx:else class="flex-1 flex-center z" bindtap="isLrc" style="position:relative;" >
<view wx:if='{{isScroll}}' class="line flex-box" style="width:100%;color:white;border:1px solid rgba(244,244,244,0.2);">
<view style="margin:0rpx 20rpx;" class="f12 flex-center">{{locationShowTime}}</view>
<view class="flex-1" style="color:red;height:2rpx;width:100%;background:green;"></view>
<view style="margin:0rpx 20rpx;" class="flex-center"><image src="../../images/play/locationTime.png" style="height:40rpx;width:40rpx;z-index:999;" catchtap="playScroll"/></view>
</view>
<scroll-view scroll-y style="height:600rpx;" scroll-with-animation scroll-top="{{locationIndex*27}}" bindscroll="scroll" bindtouchstart="touchstart" bindtouchend="touchend">
<view style="height:284rpx;"></view>
<view wx:for="{{lrc}}" wx:key="index" class="f14" style="text-align:center;margin:20rpx 0rpx;color:#ccc;"><text style="position: relative;">{{item}}<text wx:if="{{location===index}}" class="hover" style="animation: test {{timeLen}}s;">{{item}}</text></text></view>
<view style="height:300rpx;"></view>
</scroll-view>
</view>
<!--进度条-->
<view class="flex-box box" style="color:white;">
<view class="flex-center">{{nowTime}}</view>
<view class="flex-1">
<slider min="{{min}}" max="{{max}}" block-size="12" value="{{value}}" bindchange="slider" bindchanging="sliderNow"></slider>
</view>
<view class="flex-center">{{totalTime}}</view>
</view>
<!--播放控件-->
<view class="flex-box menu">
<view>
<image wx:if="{{categoryActive===0}}" bindtap="toggleCategory" src="../../images/play/list.png" style="height:60rpx;width:60rpx;" />
<image wx:if="{{categoryActive===1}}" bindtap="toggleCategory" src="../../images/play/rand.png" style="height:60rpx;width:60rpx;" />
<image wx:if="{{categoryActive===2}}" bindtap="toggleCategory" src="../../images/play/single.png" style="height:60rpx;width:60rpx;" />
</view>
<view>
<image src="../../images/play/back.png" style="height:80rpx;width:80rpx;" bindtap="back" />
</view>
<view>
<image wx:if="{{isPlay===false}}" bindtap="togglePlay" src="../../images/play/play.png" style="height:100rpx;width:100rpx;" />
<image wx:else bindtap="togglePlay" src="../../images/play/pause.png" style="height:100rpx;width:100rpx;" />
</view>
<view>
<image src="../../images/play/next.png" style="height:80rpx;width:80rpx;" bindtap="next" />
</view>
<view>
<image wx:if="{{isCollect===false}}" bindtap="toggleCollect" src="../../images/play/collect.png" style="height:60rpx;width:60rpx;" class="move" />
<image wx:else bindtap="toggleCollect" src="../../images/play/collectActive.png" style="height:60rpx;width:60rpx;" class="move" />
</view>
</view>
</view>
<!--背景图片-->
<view class="bgImage">
<image src="{{songs.al.picUrl}}" />
</view>
js:
import api from "../../http/api"
import dayjs from "../../lib/dayjs.min.js"
let bg=wx.getBackgroundAudioManager();//背景音频管理器
let timeId='';//定时器
let lineTimeId='';//水平线定时器
let isDelete=false;//是否删除开启的定时器
Page({
data: {
index:-1,//下标 歌曲信息
ids:[],//歌曲id
songs:{},//歌曲详情
musics:{},//歌曲url
lrc:{},//歌曲歌词
//控件属性
isSlider:false,//是否正在拖动进度条
nowTime:"00:00",//目前进度时间
totalTime:"00:00",//总长度时间
min:0,//进度条开始
max:0,//进度条结束
value:0,//进度条
category:[],//播放方式
categoryActive:0,//当前播放方式 0:顺序播放 1:随机播放 2:单曲循环
isPlay:true,//是否播放
isCollect:true,//是否收藏
//以下歌词
isLrc:true,//是否显示歌词
lrcArr:[],//歌词定位数组 时间
location:0,//歌词滚动位置
locationIndex:0,//
locationValue:0,//歌词滚动具体位置
locationTime:0,//歌词定位时间
locationShowTime:'00:00',//歌词定位显示时间
isScroll:false,//滚动显示水平线
timeLen:-1,//文字过渡时间
},
//歌词触碰开始
touchstart(e){
console.log("触摸开始",e);
this.setData({
isScroll:true
});
isDelete=false;
if(lineTimeId){
clearTimeout(lineTimeId);
lineTimeId='';
}
},
//歌词触碰结束
touchend(e){
isDelete=true;
console.log("触摸结束",e);
if(lineTimeId!='')return;
lineTimeId=setTimeout(()=>{
if(isDelete===true){
this.setData({
isScroll:false
});
lineTimeId='';
}
},4000);
},
//歌词滚动
scroll(e){
if(this.data.isScroll){
let i=parseInt(e.detail.scrollTop/27);
if(!this.data.lrcArr[i])return;//空白区域,没有时间不执行
console.log("滚动",e.detail.scrollTop,this.data.lrcArr[i]);//歌词的间隔区间为27
this.setData({
locationTime:this.data.lrcArr[i],
locationShowTime:dayjs(this.data.lrcArr[i]*1000).format("mm:ss")
});
}
},
//歌词拖动播放
playScroll(e){
console.log("拖动播放",e);
let value=this.data.locationTime;
bg.seek(value);
this.setData({
isScroll:false,
isPlay:true
});
this.update();
},
//切换是否显示歌词
isLrc(e){
this.setData({
isLrc:!this.data.isLrc
});
},
//下载音乐
download(e){
console.log(e);
wx.setClipboardData({
data: this.data.musics.url,
success:res=>{
console.log(res.data);
wx.hideToast();
wx.showToast({
title: '已复制链接',
})
}
})
},
//进度条拖动
sliderNow(e){
console.log(e);
let nowTime=dayjs(e.detail.value*1000).format('mm:ss');
this.setData({
nowTime:nowTime,
isSlider:true
});
},
//进度条
slider(e){
console.log(e);
let value=Number(e.detail.value);
let nowTime=dayjs(e.detail.value*1000).format('mm:ss');
this.setData({
value:value,
nowTime:nowTime,
isSlider:false
});
console.log(typeof(value),value);
bg.seek(value);
},
//上一首
back(){
let index;
if(this.data.categoryActive===0){//顺序播放
index=this.data.index-1;
if(index<0){//第一首处理
index=this.data.ids.length-1;
}
}else if(this.data.categoryActive===1){//随机播放
index=parseInt(Math.random()*this.data.ids.length);
}else{//单曲循环
index=this.data.index;
bg.stop();
}
console.log(index);
this.setData({
index:index,
isPlay:true
});
this.getData();//重新获取音乐
},
//下一首
next(){
let index;
if(this.data.categoryActive===0){//顺序播放
index=this.data.index+1;
if(index===this.data.ids.length){//最后一首处理
index=0;
}
}else if(this.data.categoryActive===1){//随机播放
index=parseInt(Math.random()*this.data.ids.length);
}else{//单曲循环
index=this.data.index;
bg.stop();
}
console.log(index);
this.setData({
index:index,
isPlay:true
});
this.getData();//重新获取音乐
},
//播放音乐
playMusic(){
console.log("播放",this.data.musics.url,this.data.songs.name);
bg.src=this.data.musics.url;
bg.title=this.data.songs.name;
//设置进度条
console.log("进度条",bg.duration);
},
//切换播放方式
toggleCategory(e){
// console.log(e);
let i=this.data.categoryActive+1;
if(i>2)i=0;
this.setData({
categoryActive:i
});
},
//切换播放
togglePlay(e){
console.log(e);
this.setData({
isPlay:!this.data.isPlay
});
if(this.data.isPlay){
bg.play();
}else{
bg.pause();
}
},
//切换收藏
toggleCollect(e){
console.log(e);
this.setData({
isCollect:!this.data.isCollect
});
},
//获取音乐数据
getData(){
let id=this.data.ids[this.data.index];
console.log(id);
//获取歌曲详情
api.songDetail(id).then(res=>{
console.log("歌曲详情:",res);
this.setData({
songs:res.songs[0]
});
//获取音乐url
api.songUrl(id).then(res=>{
console.log("歌曲音乐url:",res);
this.setData({
musics:res.data[0]
});
this.playMusic();//播放音乐
wx.hideLoading();
}).catch(err=>{
console.log(err);
});
}).catch(err=>{
console.log(err);
});
//获取歌词
api.lyric(id).then(res=>{
console.log("歌曲歌词 :",res);
let str=res.lrc.lyric;
let lrcArr=[];
let arr=[];
str=str.split(/\n/g);
str.map(item=>{
let i=item.match(new RegExp("\\[[0-9]*:[0-9]*.[0-9]*\\]","g"));
if(i){
i=i[0].replace('[','').replace(']','')
let time=Number(i.split(':')[0]*60)+Number(i.split(':')[1].split('.')[0]);//毫秒:+Number(i.split(':')[1].split('.')[1]); 01:12.232 ['01','12.232'] ['12','232']
// console.log(time,dayjs(time).format('mm:ss'));
lrcArr.push(time);
arr.push(item.replace(new RegExp("\\[(.*)\\]","g"),""));
}
});
//去空
let a1=[],a2=[];
for(let i=0;i<arr.length;i++){
if(arr[i]&&lrcArr[i]){//当前是否有歌词
a1.push(arr[i]);
a2.push(lrcArr[i]);
}
}
arr=a1,lrcArr=a2;
console.log(arr);
console.log(lrcArr);
this.setData({
lrc:arr,
lrcArr:lrcArr
});
wx.hideLoading();
}).catch(err=>{
console.log(err);
});
},
//定时器更新
update(){
if(!this.data.isPlay||this.data.isSlider)return;
let nowTime=bg.currentTime;
let totalTime=bg.duration;
let value=bg.currentTime;
let max=bg.duration;
if(nowTime&&totalTime){//都有数据
//处理歌词当前位置
// let len=0;//歌词排除为空的下标
for(let i=0;i<this.data.lrcArr.length;i++){
if(nowTime>this.data.lrcArr[this.data.lrcArr.length-1]){//最后的歌词
this.setData({
location:this.data.lrcArr.length
});
break;
}
console.log(nowTime,this.data.lrcArr[i]);
if(nowTime>=this.data.lrcArr[i]&&nowTime<this.data.lrcArr[i+1]){
console.log("歌词滚动");
this.setData({
location:i
});
break;
}
}
//设置滚动
if(this.data.isScroll===false){
if(this.data.locationIndex!=this.data.location){
this.setData({
timeLen:this.data.lrcArr[this.data.location+1]-this.data.lrcArr[this.data.location]
});
}
this.setData({
locationIndex:this.data.location
});
}
//处理显示
totalTime=dayjs(totalTime*1000).format('mm:ss');
nowTime=dayjs(nowTime*1000).format('mm:ss');
console.log("时间2:",totalTime,nowTime);
this.setData({
nowTime:nowTime,
totalTime:totalTime,
max:max,
value:value
});
}
},
onLoad: function (options) {
console.log(options.index,options.ids);
let ids=options.ids.split(',');//字符串拆分
console.log(ids);
this.setData({
index:Number(options.index),
ids:ids
});
this.getData();
//背景音频
bg.onEnded(()=>{
console.log("播放完毕。");
this.next();
});
bg.onPause(()=>{
console.log("暂停播放");
this.setData({
isPlay:false
});
});
bg.onPlay(()=>{
console.log("开始播放");
this.setData({
isPlay:true
});
});
bg.onTimeUpdate(()=>{
// console.log("播放进度更新");
// this.update();
});
},
onReady: function () {
},
onShow: function () {
//设置进度条
timeId=setInterval(()=>{
this.update();
},500);
},
onHide: function () {
},
onUnload: function () {
//销毁定时器
clearInterval(timeId);
},
onPullDownRefresh: function () {
},
onReachBottom: function () {
},
onShareAppMessage: function () {
}
})
wxss:
.container{
position: absolute;
width: 100%;
height:100%;
background:rgba(0,0,0,0.2);
/* border: 1px solid red; */
}
.container>view{
/* border: 1px solid red; */
}
.download{
position: absolute;
right:20rpx;
top: 20rpx;
}
.img{
width:500rpx;
height:500rpx;
border-radius: 50%;
border: 20rpx solid rgba(255,255,255,0.4);
-webkit-animation: rotate 10s linear infinite;
}
.pause{
animation-play-state:paused;
-webkit-animation-play-state:paused; /* Safari 和 Chrome */
}
.menu{
margin:50rpx 0rpx;
}
.menu>view{
flex:1;
display: flex;
justify-content: center;
align-items: center;
}
.bgImage{
filter:blur(20px);
position:absolute;
height:100%;
width:100%;
z-index: -1;
}
.bgImage>image{
height:100%;
width:100%;
}
.move:active {
animation-delay: -6s;
animation: heartBeat 2s;
}
.line{
position: absolute;
top:50%;
}
.z{
}
@keyframes rotate {
0%{
transform:rotate(0deg);
-ms-transform:rotate(0deg); /* IE 9 */
-moz-transform:rotate(0deg); /* Firefox */
-o-transform:rotate(0deg);
}
100% {
transform:rotate(360deg);
-ms-transform:rotate(360deg); /* IE 9 */
-moz-transform:rotate(360deg); /* Firefox */
-o-transform:rotate(360deg);
}
}
@-webkit-keyframes rotate {
0%{
-moz-transform:rotate(0deg);
}
100%{
-moz-transform:rotate(360deg);
}
}
/*文字过渡*/
.hover {
position: absolute;
top: 0;
left: 0;
color: red;
overflow: hidden;
width: 100%;
white-space: nowrap;
}
@keyframes test {
from {
width: 0%;
}
to {
width: 100%;
}
}
更多推荐
所有评论(0)