仿网易云音乐-音乐播放歌词展示,歌词滚动,旋转唱片,和时长进度条
<view class="m-song-bg" style="background-image: url({{songDetail.al.picUrl}});filter: blur(30px);"> </view> <view> <view class='m-song-wrap {{playStatus?"z-playing":"z-pausing"}}'> <view class="m-song-disc"> <view class="m-song-turn"> <view class="m-song-rollwrap"> <view class='m-song-img a-circling' style="animation-play-state:{{state}}"> <image class="u-img" alt="song-img" src="{{ songDetail.al.picUrl }}"></image> </view> </view> <view class="m-song-lgour"> <view class="m-song-light a-circling" style="animation-play-state:{{state}}"> </view> </view> </view> <text wx:if="{{!playStatus}}" class="m-song-plybtn"></text> </view> <view class="m-song-clickarea" bindtap="playOrpause"></view> </view> </view>
var requestUrl = require('../../utils/request.js') Page({ /** * 页面的初始数据 */ data: { palyMusicUrl: ' ', playTitle:' ', music:null, songDetail: null, playStatus: true, state: ' ', }, playMusic: function () { // const music = wx.createInnerAudioContext() const music = wx.getBackgroundAudioManager() let that = this; that.setData({ music: music }); music.autoplay = true; music.src = this.data.palyMusicUrl; music.title=this.data.playTitle; // console.log(this.data.palyMusicUrl) music.onPlay(() => { that.setData({ playStatus: true, state: ' ' }) this.setCurrentIndex(); }); music.onPause(() => { console.log('pause'); that.setData({ playStatus: false, state: 'paused' }) }); music.onError((res) => { console.log(res.errMsg) console.log(res.errCode) }) }, playOrpause: function() { // var that =this; if (this.data.playStatus) { this.data.music.pause(); // console.log(this.data.playStatus); } else { // console.log(this.data.playStatus); this.data.music.play(); } }, onLoad: async function (options) { // console.log(options.title); // const url = `/song/detail?ids=${options.id}`; const url = `/song/url?id=${options.id}`; const res = await requestUrl.get(url); const res_songDetail= await requestUrl.get(songDetail); // const palyMusicUrl = res.data.data[0].url; // console.log(res_lyric.data.lrc.lyric); // console.log(res_songDetail.data.songs); this.setData({ palyMusicUrl: res.data.data[0].url, playTitle: options.title, songDetail: res_songDetail.data.songs[0] }); this.playMusic(); this.dealLrc(); // console.log(res.data); // this.time(); },
text { padding: 0; margin: 0; } .m-song-wrap { padding-top: 63px; } .m-song-disc { position: relative; width: 248px; height: 248px; margin: 0 auto; } .m-song-turn { width: 100%; height: 100%; } .m-song-turn:before { content: " "; position: absolute; left: 0; right: 0; top: 0; bottom: 0; z-index: 2; background: url(//s3.music.126.net/mobile-new/img/disc.png?d3bdd10…=) no-repeat; background-size: contain; } .m-song-rollwrap { position: absolute; width: 150px; height: 150px; left: 50%; top: 50%; z-index: 1; margin: -75px 0 0 -75px; } .m-song-img { width: 100%; height: 100%; border-radius: 50%; overflow: hidden; background: url(//s3.music.126.net/mobile-new/img/disc_default.png?ba7c53e…=) no-repeat; background-size: contain; } .m-song-lgour, .m-song-light { position: absolute; left: 0; right: 0; top: 0; bottom: 0; z-index: 3; } .m-song-light { background: url(//s3.music.126.net/mobile-new/img/disc_light.png?2bb24f3…=) no-repeat; background-size: contain; } .m-song-plybtn { position: absolute; width: 50px; height: 50px; left: 50%; top: 50%; -webkit-transform: translate(-50%,-50%); -ms-transform: translate(-50%,-50%); transform: translate(-50%,-50%); z-index: 10; background: url(//s3.music.126.net/mobile-new/img/play_btn_3x.png?4da7e135b7c089f3777ec5cdbbb3a8d8=) 0 0 no-repeat; background-size: contain; } .m-song-plybtn:after { content: ""; display: block; position: absolute; } .m-song-disc:after { width: 96px; height: 137px; top: -70px; left: 133px; background-image: url(//s3.music.126.net/mobile-new/img/needle-ip6.png?be4ebbe…=); } .m-song-disc:after { content: " "; position: absolute; top: -63px; left: 107px; z-index: 5; width: 84px; height: 122px; background: url(//s3.music.126.net/mobile-new/img/needle.png?702cf6d…=) no-repeat; background-size: contain; } .m-song-light { background: url(//s3.music.126.net/mobile-new/img/disc_light.png?2bb24f3…=) no-repeat; background-size: contain; } .m-song-bg { background-color: #161824; background-position: 50%; background-repeat: no-repeat; background-size: 150% 150%; -webkit-transform: scale(1.5); -ms-transform: scale(1.5); transform: scale(1.5); -webkit-transform-origin: center; -ms-transform-origin: center; transform-origin: center; position: fixed; left: 0; right: 0; top: 0; height: 100%; overflow: hidden; z-index: 1; } .m-song-bg:before { content: " "; position: absolute; left: 0; right: 0; bottom: 0; top: 0; background-color: rgba(0,0,0,.7); } .m-song-clickarea { position: absolute; width: 100%; top: 0; left: 0; bottom: 200px; z-index: 10; margin-bottom: env(safe-area-inset-bottom); } .u-img { width: 100%; vertical-align: middle; } .aboutsougou { display: none; } .m-song-info { padding: 0 35px; margin-top: 25px; position: relative; z-index: 20; text-align: center; } .m-song-h2 { font-size: 18px; } .m-song-h2 { text-align: center; line-height: 1.1; color: #fefefe; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; } .m-song-autr, .m-song-pure { font-size: 16px; } .m-song-autr { color: hsla(0,0%,100%,.6); } .m-song-lrc { position: relative; margin-top: 14px; } .m-song-scroll { line-height: 1.5; font-size: 13px; /* height: 50px; */ overflow: hidden; -webkit-mask: -webkit-linear-gradient(top,#000,#000 70%,rgba(0,0,0,0)); } .m-song-lremp, .m-song-scroll { text-align: center; color: hsla(0,0%,100%,.6); } .m-song-lritem { /* display: inline-flex; */ height: 32px; line-height: 32px; } /* animation css */ .a-circling { -webkit-animation: circling 20s linear infinite; animation: circling 20s linear infinite; } .pause{ animation-play-state:paused; } @keyframes circling { from {transform: rotateZ(0deg);} to {transform: rotateZ(360deg);} } @-webkit-keyframes circling { from {-webkit-transform: rotate(0deg); } to {-webkit-transform: rotate(360deg);} } .m-song-iner { -webkit-transition: -webkit-transform .5s ease-out; transition: -webkit-transform .5s ease-out; transition: transform .5s ease-out; transition: transform .5s ease-out,-webkit-transform .3s ease-out; } /*进度条长度 */ .slid{ display: flex; position: relative; z-index: 10; justify-content: space-between; padding: 50rpx 20rpx; } .slid view{ font-size: 32rpx; color: #999; } slider{ width: 500rpx; margin: 0 20rpx; border-radius:5rpx; }
唱片旋转的效果使用CSS3动画。唱片随歌曲播放和暂停。
<view class="m-song-light a-circling" style="animation-play-state:{{state}}">
设定state的状态,‘ ’或者‘pause’
music.onPlay(() => { that.setData({ playStatus: true, state: ' ' }) this.setCurrentIndex(); }); music.onPause(() => { console.log('pause'); that.setData({ playStatus: false, state: 'paused' }) });
2. 播放slide采用了微信小程序的表单组件silder
<view class="slid"> <view> <text class='times'>{{currentTime}}</text> </view> <slider bindchange="sliderChange" activeColor="#d33a31" block-size="12" backgroundColor="#999" value="{{percent}}" > </slider> <view> <text class='times'>{{duration}}</text> </view> </view>
获取数据:currentTime duration
处理数据:percent
data: {
currentTime:'00:00',
duration:' ',
percent:' ',
}
formatTime: function(time) {
var minute =Math.ceil(time/60)%60;
var sec = Math.ceil(time)%60;
return (minute<10? '0'+minute : minute) + ':' + (sec<10 ? '0' +sec : sec)
},
music.onTimeUpdate(() =>{ let duration = that.formatTime(music.duration); let currentTime = that.formatTime(music.currentTime); let percent =(music.currentTime/music.duration*100).toFixed(2); // console.log(music.duration +'--'+ percent +'--'+ music.currentTime); that.setData({ duration:duration, currentTime: currentTime, percent:percent, musicTime:parseInt(music.currentTime) }); });
//滑块滑动jS
sliderChange: function(e) {
var that=this;
// that.data.music.pause();
let slideValue = e.detail.value;
let seeKPosition = that.data.music.duration/100*slideValue
that.data.music.seek(seeKPosition);
// console.log(currentTime)
setTimeout(function () {
that.data.music.play();
}, 500);
},
获取歌词并设定歌词滚动
//歌词处理,得到的歌词需要经过处理才能使用 dealLrc: function(lrc) { // let lrc = this.data.musicLyric; var that=this; var arr = lrc.split("\n"); var timeArr = [], lrcArr = []; arr.forEach(function (item, index) { var time = item.slice(item.indexOf("["), item.indexOf("]") + 1); time = that.dealLrcTime(time); timeArr.push(time); var lrc = item.substr(item.indexOf("]") + 1)+"\n"; lrcArr.push(lrc); // console.log(time) }); // console.log(lrcArr) // console.log(timeArr) that.setData({ lyrics: lrcArr, lyricsTime:timeArr }); }, dealLrcTime: function(time) { time = time.replace("[", ""); time = time.replace("]", ""); var arr = time.split(":"); var relTime = parseInt(arr[0]) * 60 + parseInt(arr[1]); return relTime; }, //找到当前播放时间 findCurrentIndex(curTime,timeArr) { // var curTime = this.data.musicTime; // var timeArr = this.data.lyricsTime; var index = -1; for (let i = 0; i < timeArr.length; ++i) { if (timeArr[i] == NaN) { continue; } if (curTime <= timeArr[i]) { index = i; break; } } return index; }, //设置当前歌曲播放的位置 setCurrentIndex:function(){ var timeId=wx.getStorageSync("timeId"); if(timeId){ clearInterval(timeId); } if(this.data.musicLyric){ // var audio = this.data.audio; // var duration = audio.duration; var timeArr = this.data.lyricsTime; var currentTime = -1; var that = this; timeId = setInterval(() => { currentTime = that.data.musicTime; var currentIndex =that.findCurrentIndex(currentTime, timeArr); this.setData({ currentIndex: currentIndex }); }, 2000); wx.setStorageSync("timeId", timeId); } }, onLoad: async function (options) { // console.log(options.title); // const url = `/song/detail?ids=${options.id}`; var that=this; const url = `/song/url?id=${options.id}`; const lyric=`/lyric/?id=${options.id}`; const songDetail=`/song/detail?ids=${options.id}`; const res = await requestUrl.get(url); const res_lyric= await requestUrl.get(lyric); const res_songDetail= await requestUrl.get(songDetail); const musicLyric = that.dealLrc(res_lyric.data.lrc.lyric); // const palyMusicUrl = res.data.data[0].url; // console.log(res_lyric.data.lrc.lyric); // console.log(res_songDetail.data.songs); this.setData({ palyMusicUrl: res.data.data[0].url, playTitle: options.title, // musicLyric: res_lyric.data.lrc.lyric, songDetail: res_songDetail.data.songs[0] }); this.playMusic(); // this.dealLrc(); // console.log(res.data); // this.time(); },
<view> <view style="position:relative"> <view class="m-song-info"> <h2 class="m-song-h2"> <text class="m-song-sname">{{songDetail.name}}</text> <text class="m-song-gap">-</text> <b class="m-song-autr">{{songDetail.ar[0].name}}</b> </h2> <view class="m-song-lrc f-pr"> <view class="m-song-scroll" > <view class="m-song-iner" style="transform: translateY(-{{32*currentIndex}}px);"> <block wx:for="{{lyrics}}" wx:key="index" wx:for-index="i" > <view class="m-song-lritem j-lritem" style='{{i==currentIndex?"color: rgb(255, 255, 255);":" "}}'> {{item}} </view> </block> </view> </view> </view> </view> </view> <view class="m-link m-music-street" style="margin-top: 15px;"> <view class="m-musicStreetWakeUp"> <image src=""></image> </view> </view> </view>
歌词滚动,获取当前所在的歌词位置,CSS控制歌词向上滚动