Repository: mySkey/music-small Branch: master Commit: a21a9238173a Files: 42 Total size: 51.7 KB Directory structure: gitextract_ec2v8ip_/ ├── README.md ├── app.js ├── app.json ├── app.wxss ├── components/ │ ├── controls.js │ ├── controls.json │ ├── controls.wxml │ ├── controls.wxss │ ├── loadmore.js │ ├── loadmore.json │ ├── loadmore.wxml │ ├── loadmore.wxss │ ├── lrc.js │ ├── lrc.json │ ├── lrc.wxml │ ├── lrc.wxss │ ├── player.js │ ├── player.json │ ├── player.wxml │ ├── player.wxss │ ├── progress.js │ ├── progress.json │ ├── progress.wxml │ └── progress.wxss ├── pages/ │ └── music/ │ ├── detail.js │ ├── detail.json │ ├── detail.wxml │ ├── detail.wxss │ ├── list.js │ ├── list.json │ ├── list.wxml │ └── list.wxss ├── project.config.json └── utils/ ├── js/ │ ├── ajax.js │ ├── audio.js │ ├── common.js │ ├── dayjs.js │ └── props.js ├── wxs/ │ └── time.wxs └── wxss/ ├── icon.wxss ├── reset.wxss └── style.wxss ================================================ FILE CONTENTS ================================================ ================================================ FILE: README.md ================================================ ### 扫码预览
### 一、需求分析 * 基本功能 上一曲、下一曲、播放、暂停、列表播放 * 切换到别的页面播放继续 * 歌词同步 1、歌词随音乐进度更新 2、拖动歌词后出现可调节位置的控件,此时歌词滚动暂停 3、点击控件播放,关闭控件,改变进度 4、不点击,5s后关闭控件 * 进度条播放 可点击或者拖动进度条来改变歌曲进度 * 播放模式 单曲、列表、随机三种播放模式 * 倍速 可在不同倍速下播放,并且不同倍速下歌词同步 ### 其中的经验: * 1、wxs支持es4的语法 * 2、在app.json中设置"requiredBackgroundModes": ["audio"],将开启后台播放 * 3、wx.getBackgroundAudioManager()的onEnded监听在安卓机上面不生效 * 4、不支持倍速 ================================================ FILE: app.js ================================================ //app.js App({ onLaunch: function () { wx.setNavigationBarTitle({ title: 'mySkey音乐' }) this.initAudio() }, onShow(){ if(this.audioDom.paused){ this.player.status = 2; } }, onHide: function () { }, player: { list: [], current_music: 0, mode: 0, // 0 单曲 1 顺序 2 随机 status: 0, // 0 未播放 1 播放 2 暂停中 3 已结束 playbackRate: 1, // 倍速 a_resource: '', i_resource: '', global_show: 0, // 全局显示 0 不显示 1 显示 playing: { id: '', name: '', cover: '', url: '', singer: { avatar: '', name: '' }, currentTime: 0, duration: 0, timeArr: [], lrcArr: [] } }, audioDom: wx.getBackgroundAudioManager(), initAudio(){ this.audioDom.title = '起风了'; this.audioDom.epname = '555'; this.audioDom.singer = 'mySkey'; this.audioDom.coverImgUrl = 'http://img.22family.com/mySKey/favicon.ico'; this.audioDom.paused = true; this.audioDom.stop = true; } }) ================================================ FILE: app.json ================================================ { "pages": [ "pages/music/list", "pages/music/detail" ], "window": { "backgroundTextStyle": "light", "navigationBarBackgroundColor": "#fff", "navigationBarTitleText": "WeChat", "navigationBarTextStyle": "black" }, "requiredBackgroundModes": ["audio"] } ================================================ FILE: app.wxss ================================================ /**app.wxss**/ @import './utils/wxss/reset.wxss'; @import './utils/wxss/icon.wxss'; @import './utils/wxss/style.wxss'; .container { height: 100%; box-sizing: border-box; } ================================================ FILE: components/controls.js ================================================ // components/controls.js const app = getApp() import props from '../utils/js/props.js' import common from '../utils/js/common.js' Component({ /** * 组件的属性列表 */ properties: { currentTime: { type: Number, value: 0, observer: function (newVal, oldVal, changedPath) { props.setPlaying.call(this, { currentTime: newVal, duration: app.audioDom.duration }) //props.setPlayer.call(this, { status: app.player.status }) } } }, /** * 组件的初始数据 */ data: { player:{}, }, attached(){ props.setPlayer.call(this, app.player) }, /** * 组件的方法列表 */ methods: { pauseAudio() { app.audioDom.pause() props.setPlayer.call(this, { status: 2 }) }, palyAudio() { app.audioDom.play() props.setPlayer.call(this, { status: 1 }) }, playLast() { if (this.data.player.list.length > 0) { this.triggerEvent('playLast') } }, playNext() { if (this.data.player.list.length > 0) { this.triggerEvent('playNext') } }, changeMode() { let mode = this.data.player.mode + 1 if (mode > 2) { mode = 0 } props.setPlayer.call(this, { mode }) }, changeRate() { common.toast('小程序不支持倍速功能!') return let playbackRate = this.data.player.playbackRate + 0.25 if (playbackRate > 2) playbackRate = 0.5 props.setPlayer(this, { playbackRate }) global.audioDom.playbackRate = playbackRate; }, progressTouch() { this.setState({ userChange: true }) }, changeProgress(radio) { let currentTime = Math.floor(radio * this.props.audio.duration) global.audioDom.currentTime = currentTime this.props.setCurrentTime(currentTime) this.setState({ userChange: false }) } } }) ================================================ FILE: components/controls.json ================================================ { "component": true, "usingComponents": { "myProgress": "/components/progress" } } ================================================ FILE: components/controls.wxml ================================================ {{time.getAudioTime(player.playing.currentTime)}} {{time.getAudioTime(player.playing.duration)}} 正常 ×{{player.playbackRate}} ================================================ FILE: components/controls.wxss ================================================ /* components/controls.wxss */ @import '/utils/wxss/style.wxss'; @import '/utils/wxss/icon.wxss'; .controls{ width: 100%; position: absolute; bottom: 2rem; left: 0; } .controls .time{ color: #fff; font-size: 0.6rem; align-items: center; } .controls .time .progress{ width: 10rem; margin: 0 16px; } .controls .btns{ align-items: center; color: #dd5866; } .controls .btns .play_btn{ align-items: center; margin: 0 2rem; } .controls .btns .last_next{ font-size: 1.2rem; } .controls .btns .play_pause{ font-size: 2.4rem; margin: 0 1rem; } .controls .btns .mode_rete{ align-items: center; font-size: 0.7rem; color: #fff; min-width: 40px; } .controls .btns .mode_rete .mode{ font-size: 1rem; } ================================================ FILE: components/loadmore.js ================================================ // components/loadmore.js Component({ /** * 组件的属性列表 */ properties: { loading: { type: Number, value: 0, observer: function (newVal, oldVal, changedPath) { this.setData({ _loading: newVal }); } } }, /** * 组件的初始数据 */ data: { _loading: 0 }, /** * 组件的方法列表 */ methods: { } }) ================================================ FILE: components/loadmore.json ================================================ { "component": true, "usingComponents": {} } ================================================ FILE: components/loadmore.wxml ================================================ 已加载全部   ================================================ FILE: components/loadmore.wxss ================================================ /* components/loadmore.wxss */ .wrap{padding:20rpx 0 50rpx 0;} .loading-dot{ width:100%; height:40rpx; overflow:hidden; display:flex; align-items:center; justify-content:center; } .loading-dot .dot{ display:inline-block; width:16rpx; height:16rpx; border-radius:50%; overflow: hidden; background:#f2f2f2; margin:0 10rpx; -webkit-animation: bouncedelay 1s infinite ease-in-out; animation: bouncedelay 1s infinite ease-in-out; -webkit-animation-fill-mode: both; animation-fill-mode: both } .loading-dot .dot-1 {-webkit-animation-delay: -.48s; animation-delay: -.48s} .loading-dot .dot-2 {-webkit-animation-delay: -.24s; animation-delay: -.24s} @keyframes bouncedelay { 0%,100%,80% {background:lightgray;} 40% {background:gray;} } /* @keyframes bouncedelay { 0%,100%,80% {transform: scale(0); -webkit-transform: scale(0)} 40% {transform: scale(1);-webkit-transform: scale(1)} } */ .all{font-size:24rpx;color:#e8e8e8;text-align:center;} .placeholder{font-size:24rpx;} ================================================ FILE: components/lrc.js ================================================ // components/lrc.js const app = getApp() import props from '../utils/js/props.js' import common from '../utils/js/common.js' Component({ /** * 组件的属性列表 */ properties: { currentTime: { type: Number, value: 0, observer: function (newVal, oldVal, changedPath) { if (app.player.playing.lrcArr.length > 0){ props.setPlaying.call(this, { currentTime: newVal }) this.getCurrentLrc() } } } }, /** * 组件的初始数据 */ data: { player:{}, currentLrc: 0, currentWidth: 0, listTop: 0, useTime: 1, userChange: false, changeTo: 0 }, attached() { //console.log(app.player) this.setData({ player: app.player }) }, /** * 组件的方法列表 */ methods: { getCurrentLrc() { let player = app.player, audio = app.player.playing; let { currentTime } = audio let currentLrc = 0; audio.timeArr.forEach((v, k) => { if (currentTime > this.getSecond(v)) { currentLrc = k } }) // 更新当前歌词 if (currentLrc !== this.data.currentLrc) { this.setData({ currentLrc, currentWidth: 0 }, () => { this.getCurrentWidth((currentWidth)=>{ if (currentLrc < audio.timeArr.length - 1) { let useTime = this.getSecond(audio.timeArr[currentLrc + 1]) - this.getSecond(audio.timeArr[currentLrc]) // 根据当前的倍速来决定时间 useTime = useTime / player.playbackRate if (useTime > 10) { this.setData({ useTime: 10 }) return } this.setData({ useTime, currentWidth }) } }) }) if (!this.data.userChange) { this.scrollLrc(currentLrc) } } else { this.getCurrentWidth((currentWidth)=>{ this.setData({ currentWidth }) }) } }, getCurrentWidth(cb){ let query = wx.createSelectorQuery().in(this) query.select('.lrcItemShow').boundingClientRect(function (res) { //console.log(res.width) if(res){ cb && cb(res.width) } }).exec() }, getSecond(t) { let minute = Number(t.slice(0, 2)) let second = Number(t.slice(3, 5)) let minS = Number(t.slice(7)) return minS > 100 ? (minute * 60 + second + minS / 1000) : (minute * 60 + second + minS / 100) }, // 歌词滚动 scrollLrc(currentLrc) { if (currentLrc) { let listTop = currentLrc * 32 this.setData({ listTop }) } }, // 滑动歌词调整进度 handleMove() { if(this.data.userChange){ let query = wx.createSelectorQuery().in(this) query.select('.lrcList').boundingClientRect((res)=>{ //console.log(res.top / 32) let currentLrc = (-Math.round(res.top / 32)) + 3 let time = app.player.playing.timeArr[currentLrc] let currentTime = Math.ceil(common.getSecond(time)) this.setData({ changeTo: currentTime }) }).exec() } }, changeUserStatus(){ clearTimeout(this.timer) this.setData({ userChange: true }) this.timer = setTimeout(() => { this.setData({ userChange: false }) }, 5000) }, changeToLrc() { this.setData({ userChange: false }, () => { wx.seekBackgroundAudio({ position: this.data.changeTo, }) }) } } }) ================================================ FILE: components/lrc.json ================================================ { "component": true, "usingComponents": {} } ================================================ FILE: components/lrc.wxml ================================================ {{v}} {{v}} {{v}} {{time.getAudioTime(changeTo)}} ================================================ FILE: components/lrc.wxss ================================================ /* components/lrc.wxss */ @import '/utils/wxss/style.wxss'; @import '/utils/wxss/icon.wxss'; .lrc_container{ position: absolute; top: 80px; left:0; } .lrcList{ width: 100vw; text-align: center; align-items: center; padding-top: 160px; /* 从第6句开始特殊滑动 */ padding-bottom: 3rem; } .lrcList .lrcItem{ color: #fff; height: 32px; line-height: 32px; position: relative; white-space: nowrap; } .lrcList .lrcItemShow{ position: absolute; color: #fff; font-size: 1rem; top:0; left:0; z-index: 98; white-space: nowrap; } .lrcList .lrcItemShowIng{ position: absolute; color: #dd5866; font-size: 1rem; top:0; left:0; z-index: 99; white-space: nowrap; overflow: hidden; width: 0; } .swiper{ width: 100%; height: 20px; line-height: 20px; padding: 0 1rem; box-sizing: border-box; position: absolute; top: 166px; /* 从第六上面出现刻度线 */ left: 0; align-items: center; font-size: 28rpx; color: #db3e4b } .swiper .line{ width: 70%; height: 1px; background: radial-gradient(at 50% 0, transparent, transparent, rgba(252, 231, 231, 0.7),rgba(252, 231, 231, 0.7)); } .swiper .play .icon-play{ font-size: 40rpx; } ================================================ FILE: components/player.js ================================================ // components/player.js const app = getApp() import props from '../utils/js/props.js' Component({ /** * 组件的属性列表 */ properties: { currentTime: { type: Number, value: 0, observer: function (newVal, oldVal, changedPath) { if (newVal>0) { props.setPlayer.call(this, app.player) } } } }, /** * 组件的初始数据 */ data: { player:{} }, attached(){ }, /** * 组件的方法列表 */ methods: { pauseAudio() { app.audioDom.pause() props.setPlayer.call(this, { status: 2 }) this.triggerEvent('updatePlayer') }, playAudio() { app.audioDom.play() props.setPlayer.call(this, { status: 1 }) this.triggerEvent('updatePlayer') }, playLast() { let current_music = app.player.current_music - 1 if (current_music < 0) { current_music = app.player.list.length - 1 } this.playCurrent(current_music) }, playNext() { let current_music = app.player.current_music + 1 if (current_music >= app.player.list.length) { current_music = 0 } this.playCurrent(current_music) }, playCurrent(current_music) { props.setPlayer.call(this, { current_music, status: 1 }) props.setPlaying.call(this, app.player.list[current_music], ()=>{ let audioDom = app.audioDom audioDom.title = app.player.playing.name audioDom.src = app.player.a_resource + app.player.playing.url audioDom.coverImgUrl = app.player.i_resource + app.player.playing.cover + '-ph' audioDom.play() }) this.triggerEvent('updatePlayer') }, closePlayer() { props.setPlayer.call(this, { global_show: 0 }) }, toDtail() { let { id } = app.player.playing let url = `/pages/music/detail?id=${id}` wx.navigateTo({ url }) } } }) ================================================ FILE: components/player.json ================================================ { "component": true, "usingComponents": {} } ================================================ FILE: components/player.wxml ================================================ {{player.playing.name}} {{player.playing.singer.name}} ================================================ FILE: components/player.wxss ================================================ /* components/player.wxss */ @import '/utils/wxss/style.wxss'; @import '/utils/wxss/icon.wxss'; .player{ width: 90%; padding: 0.2rem 0.5rem; border-radius: 0.3rem; box-sizing: border-box; position: fixed; z-index: 9999; background: #999; bottom: 4rem; left:5%; align-items: center; } .avatar{ width: 2rem; height: 2rem; border-radius: 50%; overflow: hidden; margin-right: 0.5rem; } .avatar_img{ width: 2rem; height: 2rem; border-radius: 50%; } .content{ color: #fff; font-size: 0.7rem; } .content .singer{ margin-top: 0.2rem; font-size: 0.6rem; color: #d2d2d2; } .content .right{ align-items: center; } .content .right .controls{ align-items: center; } .content .right .controls .last_next{ font-size:1rem; } .content .right .controls .play_pause{ font-size: 1.7rem; margin: 0 0.3rem; } .content .right .delete{ align-items: center; margin-left: 1rem; } .content .right .delete .line{ height: 1.3rem; width: 2px; background: #fff; margin-right: 0.5rem; } @keyframes rotateImg{ from{transform: rotate(0);} to{transform: rotate(360deg);} } ================================================ FILE: components/progress.js ================================================ // components/progress.js const app = getApp() import props from '../utils/js/props.js' Component({ /** * 组件的属性列表 */ properties: { currentTime: { type: Number, value: 0, observer: function (newVal, oldVal, changedPath) { props.setPlaying.call(this, { currentTime: newVal, duration: app.audioDom.duration }) if(!this.data.userChange){ this.updateProgress() } } } }, /** * 组件的初始数据 */ data: { player:{}, left: 0, sliderW: 14, userChange: false, swiperWidth: 0, swiperLeft: 0, }, ready() { this.getSwiperInfo() }, /** * 组件的方法列表 */ methods: { getSwiperInfo(){ const that = this; let query = wx.createSelectorQuery().in(this) query.select('.swiper').boundingClientRect(function (res) { //console.log(res) that.setData({ swiperWidth: res.width, swiperLeft: res.left }) }).exec() }, updateProgress(){ let { currentTime, duration } = app.player.playing; let left = Math.floor(this.data.swiperWidth * (currentTime / duration)) this.setData({ left }) }, swiperTouch(e) { let swiperWidth = this.data.swiperWidth let startX = this.data.swiperLeft let endX = e.touches[0].clientX this.setData({ left: endX - startX }) let position = ((endX - startX) / swiperWidth) * app.audioDom.duration wx.seekBackgroundAudio({ position: Math.round(position), }) }, touchStart(e) { this.setData({ sliderW: 18, userChange: true }) }, touchMove(e) { let swiperWidth = this.data.swiperWidth let sliderWidth = this.data.sliderW let startX = this.data.swiperLeft let endX = e.touches[0].clientX if (endX - startX <= 0) { this.setData({ left: 0 }) return } if (endX - startX >= (swiperWidth - sliderWidth)) { this.setData({ left: swiperWidth - sliderWidth }) return } this.setData({ left: endX - startX }) }, touchEnd() { let swiperWidth = this.data.swiperWidth this.setData({ sliderW: 12, userChange: false }) //this.props.changeProgress(this.data.left / swiperWidth) let position = (this.data.left / swiperWidth) * app.audioDom.duration wx.seekBackgroundAudio({ position: Math.round(position), }) } } }) ================================================ FILE: components/progress.json ================================================ { "component": true, "usingComponents": {} } ================================================ FILE: components/progress.wxml ================================================ ================================================ FILE: components/progress.wxss ================================================ /* components/progress.wxss */ .swiper{ width: 100%; height: 6px; background: #d2d2d2; margin: 20px 0; position: relative; border-radius: 6px; } .line{ height: 6px; background: #dd5866; position: absolute; left: 0; top: 0; border-radius: 6px 0 0 6px; } .slider{ border-radius: 50%; background: #33c9d4; position: absolute; } ================================================ FILE: pages/music/detail.js ================================================ // pages/music/detail.js import props from '../../utils/js/props.js' import common from '../../utils/js/common.js' import ajax from '../../utils/js/ajax.js' const app = getApp() let audioDom = app.audioDom; Page({ /** * 页面的初始数据 */ data: { player:{}, id: '', audioDom:'' }, /** * 生命周期函数--监听页面加载 */ onLoad: function (options) { let id = options.id || 1 if (id == app.player.playing.id){ this.setData({ id }, ()=>{ this.updateLrc() }) }else{ wx.stopBackgroundAudio() this.setData({ id: options.id || 1 }, () => { this.getDetail() }) } }, /** * 生命周期函数--监听页面初次渲染完成 */ onReady: function () { }, /** * 生命周期函数--监听页面显示 */ onShow: function () { props.setPlayer.call(this, app.player) this.audioWatch() }, updeData(current_music){ if (current_music !== this.data.current_music) { props.setPlayer.call(this, { current_music }) props.setPlaying.call(this, app.player.list[current_music], ()=>{ this.playAudio() this.setData({ id: app.player.playing.id }, () => this.updateLrc()) }) } }, getDetail() { ajax.get('audio/detail', { id: this.data.id }).then(res => { if (res.code === 0) { let { a_resource, i_resource } = res.data; let { lrc, id, url, cover, singer, name } = res.data.audio let { timeArr, lrcArr } = common.analysis(lrc) props.setPlaying.call(this, { id, timeArr, lrcArr, url, singer, name, cover }) props.setPlayer.call(this, { i_resource, a_resource, global_show: 1, status: 1 }) this.playAudio() } }) }, updateLrc() { ajax.get('lrc', { id: this.data.id }).then(res => { if (res.code === 0) { let lrc = res.data.lrc let { timeArr, lrcArr } = common.analysis(lrc) props.setPlaying.call(this, { timeArr, lrcArr }) } }) }, audioWatch(){ let audioDom = app.audioDom audioDom.onPlay(() => { props.setPlayer.call(this, { status: 1 }) props.setPlaying.call(this, { duration: audioDom }) }) audioDom.onPause(()=>{ props.setPlayer.call(this, { status: 2 }) }) audioDom.onTimeUpdate(() => { props.setPlaying.call(this, { currentTime: audioDom.currentTime }) }) audioDom.onEnded(() => { // 安卓监听不到。。。,并没有问题,是将接口使用错误了 this.audioEnded() }) }, audioEnded(){ let player = app.player, playing = app.player.playing; let mode = app.player.mode; switch (mode) { // 0 单曲 1 顺序 2 随机 case 0: console.log('单曲模式') this.playAudio() break; case 1: if (player.list.length > 0) { console.log('列表模式下一曲') let current_music = player.current_music + 1 if (current_music >= player.list.length) { current_music = 0; } this.updeData(current_music) return } app.audioDom.pause() break; case 2: if (player.list.length > 0) { console.log('随机模式下一曲') let current_music = Math.floor(Math.random() * player.list.length) this.updeData(current_music) return } app.audioDom.pause() break; default: app.audioDom.pause() } }, playAudio(){ let audioDom = app.audioDom wx.setNavigationBarTitle({ title: app.player.playing.name }) audioDom.title = app.player.playing.name audioDom.src = app.player.a_resource + app.player.playing.url audioDom.coverImgUrl = app.player.i_resource + app.player.playing.cover + '-ph' audioDom.play() }, playLast() { let current_music = app.player.current_music - 1 if (current_music < 0) { current_music = app.player.list.length - 1 } props.setPlayer.call(this, { current_music }) this.setData({ current_music }, () => { let id = app.player.list[current_music].id this.setData({ id }, () => this.getDetail()) }) }, playNext() { let current_music = app.player.current_music + 1 if (current_music >= app.player.list.length) { current_music = 0 } props.setPlayer.call(this, { current_music }) this.setData({ current_music }, () => { let id = app.player.list[current_music].id this.setData({ id }, () => this.getDetail()) }) }, /** * 生命周期函数--监听页面隐藏 */ onHide: function () { }, /** * 生命周期函数--监听页面卸载 */ onUnload: function () { }, /** * 页面相关事件处理函数--监听用户下拉动作 */ onPullDownRefresh: function () { }, /** * 页面上拉触底事件的处理函数 */ onReachBottom: function () { }, /** * 用户点击右上角分享 */ onShareAppMessage: function () { } }) ================================================ FILE: pages/music/detail.json ================================================ { "usingComponents": { "lrc": "/components/lrc", "controls": "/components/controls" } } ================================================ FILE: pages/music/detail.wxml ================================================ ================================================ FILE: pages/music/detail.wxss ================================================ /* pages/music/detail.wxss */ .detail{ width: 100%; height: 100vh; overflow: hidden; } .detail_container{ width: 100%; height: 100vh; position: fixed; top:0; left: 0; background: rgba(0,0,0,0.25); } ================================================ FILE: pages/music/list.js ================================================ // pages/music/list.js const app = getApp() const dayjs = require('../../utils/js/dayjs.js') import ajax from '../../utils/js/ajax.js' import props from '../../utils/js/props.js' Page({ /** * 页面的初始数据 */ data: { player:{}, musics:[ { audios: [], page: {} }, // 推荐 { audios: [], page: {} }, // 最热 { audios: [], page: {} }, // 原创 { audios: [], page: {} }, // 飙升 { audios: [], page: {} }, // 最新 ], types: ['推荐', '最热', '原创', '飙升', '最新'], currentType: 0, swiperHeight: 0, startX: 0, endX: 0, lineWidth: 0, lineLeft: 0, loadingArr: [0, 0, 0, 0, 0], p: 1 }, /** * 生命周期函数--监听页面加载 */ onLoad: function (options) { this.getList() }, /** * 生命周期函数--监听页面初次渲染完成 */ onReady: function () { }, /** * 生命周期函数--监听页面显示 */ onShow: function () { this.updatePlayer() }, updatePlayer(){ props.setPlayer.call(this, app.player) }, getList(){ const type = this.data.currentType; let loadingArr = this.data.loadingArr loadingArr[type] = 1 this.setData({ loadingArr }) ajax.get('audio', { type, p: this.data.p }).then(res => { loadingArr[type] = 0 this.setData({ loadingArr }) if (res.code === 0) { let { audios, i_resource, a_resource, page } = res.data this.saveMusics({ type, page, audios }, ()=>{ this.getSwiperHeight((swiperHeight) => this.setData({ swiperHeight })) }) props.setPlayer.call(this, { i_resource, a_resource }) props.setPlayer.call(this, { list: this.data.musics[type].audios }) } }) }, saveMusics(data, cb){ let { type, page, audios } = data; let musics = this.data.musics musics[type].audios = musics[type].audios.concat(audios) musics[type].page = page this.setData({ musics }, ()=>{ cb && cb() }) }, dateFormat(t){ return dayjs(t * 1000).format('YYYY.MM.DD') }, changeAudio(e){ let currentType = e.currentTarget.dataset.k this.setData({ currentType }) }, handleChangeIndex(e) { let currentType = e.detail.current let p = this.data.musics[currentType].page.p || 1 props.setPlayer.call(this, { list: this.data.musics[currentType].audios }) //console.log(app.player.list) this.setData({ p, currentType }, () => { if (this.data.musics[currentType].audios.length === 0) { this.getList() } this.getSwiperHeight((swiperHeight) => this.setData({ swiperHeight })) }) }, getSwiperHeight(cb) { var query = wx.createSelectorQuery().in(this) query.select('.swiperShow').boundingClientRect(function (res) { cb && cb(res.height) }).exec() }, toDetail(event){ let { e, i } = event.currentTarget.dataset props.setPlayer.call(this, { current_music: i }) wx.navigateTo({ url: `/pages/music/detail?id=${e.id}` }) }, handleTouchStart(e) { let startX = e.touches[0].clientX this.setData({ startX }) }, handleTouchMove(e) { let endX = e.touches[0].clientX if (endX - this.data.startX > 0) { this.setData({ lineLeft: (endX - this.data.startX) / 10, lineWidth: (endX - this.data.startX) / 10 }) } else { this.setData({ lineWidth: (this.data.startX - endX) / 10 }) } }, handleTouchEnd(e) { this.setData({ lineWidth: 0, lineLeft: 0 }) }, /** * 生命周期函数--监听页面隐藏 */ onHide: function () { }, /** * 生命周期函数--监听页面卸载 */ onUnload: function () { }, /** * 页面相关事件处理函数--监听用户下拉动作 */ onPullDownRefresh: function () { }, /** * 页面上拉触底事件的处理函数 */ onReachBottom: function () { let currentType = this.data.currentType if (this.data.loadingArr[currentType] !== 0) return if (this.data.musics[currentType].page.p >= this.data.musics[currentType].page.total_page) { let loadingArr = this.data.loadingArr loadingArr[currentType] = 2 this.setData({ loadingArr }) return } let p = this.data.musics[currentType].page.p + 1 this.setData({ p }, ()=>{ this.getList() }) }, /** * 用户点击右上角分享 */ onShareAppMessage: function () { } }) ================================================ FILE: pages/music/list.json ================================================ { "usingComponents": { "loadmore": "/components/loadmore", "playerBar": "/components/player" } } ================================================ FILE: pages/music/list.wxml ================================================ {{v}} {{e.name}} {{e.singer.name}} {{time.dateFormat(e.date)}} ================================================ FILE: pages/music/list.wxss ================================================ /* pages/music/list.wxss */ .typeTitle{ width: 100%; position: fixed; left: 0; top: 0; z-index: 999; background: #fff; border-bottom: 1px solid #eee; } .typeList{ height: 1.8rem; padding-bottom: 0.4rem; font-size: 0.7rem; color: #333; align-items: flex-end; } .typeItem{ width: 20%; transition: all 0.3s; text-align: center; } .itemShow{ font-size: 0.9rem; color: #33c9d4; } .navSwiper{ position: relative; } .navSlider{ width: 20%; height: 2px; position: absolute; bottom: -1px; transition: all 0.5s; } .navLine{ height: 100%; background: #33c9d4; width: 60%; margin-left: 20%; } .swiper{ width: 100%; padding-top: 2rem; padding-bottom: 3rem; } .swiperShow{ width: 100%; padding-top: 2rem; padding-bottom: 3rem; max-height: auto; } .item{ color: #333; padding: 1rem 0.75rem 0.5rem 0.75rem; border-top: 1px solid #eee; align-items: center; } .swiper .item:first-child, .swiperShow .item:first-child{ border: none; } .item .item_cover{ width: 4rem; height: 4rem; border-radius: 50%; } .item_show{ color: #fff; background: url('http://audio.22family.com//gif/playing.gif') 0 0 /cover; } .item .item_info{ width: 100%; min-height: 4rem; padding: 0.5rem 0; padding-left: 1rem; } .item .item_info .item_name{ font-size: 0.8rem; align-self: flex-start; } .item .item_info .singer_name{ align-items: center; padding: 0.7rem 0 0.3rem 0; font-size: 0.7rem; color: #666; } .item .item_info .singer_name .singer_avatar{ width: 1.4rem; display: flex; justify-content: center; align-items: center; margin-right: 0.4rem; } .item .item_info .singer_name .avatar_img{ width: 1rem; height:1rem; border-radius: 50%; } .item .item_info .item_date{ font-size: 0.6rem; color: #999; } ================================================ FILE: project.config.json ================================================ { "description": "项目配置文件", "packOptions": { "ignore": [] }, "setting": { "urlCheck": true, "es6": true, "postcss": true, "minified": true, "newFeature": true, "autoAudits": false }, "compileType": "miniprogram", "libVersion": "2.6.2", "appid": "wx7d3aad6d394ff0ab", "projectname": "mySkey", "debugOptions": { "hidedInDevtools": [] }, "isGameTourist": false, "condition": { "search": { "current": -1, "list": [] }, "conversation": { "current": -1, "list": [] }, "plugin": { "current": -1, "list": [] }, "game": { "currentL": -1, "list": [] }, "miniprogram": { "current": 0, "list": [ { "id": -1, "name": "播放页", "pathName": "pages/music/detail", "query": "id=3", "scene": null } ] } } } ================================================ FILE: utils/js/ajax.js ================================================ let api_url = 'https://www.22family.com/music/test/' export default{ get(url, data){ return new Promise((resolve, reject)=>{ wx.request({ url: api_url + url, method: 'GET', data, header: { 'content-type': 'application/json' // 默认值 }, success(res) { resolve(res.data) }, error(err){ reject(err) } }) }) }, post(url, data) { return new Promise((resolve, reject) => { wx.request({ url: api_url + url, method: 'POST', data, header: { 'content-type': 'application/json' // 默认值 }, success(res) { resolve(res.data) }, error(err) { reject(err) } }) }) } } ================================================ FILE: utils/js/audio.js ================================================ ================================================ FILE: utils/js/common.js ================================================ export default { toast(title, duration = 1500){ wx.showToast({ title, icon: 'none', duration }) }, confirm(content, cb){ wx.showModal({ title: '温馨提示', content, success(res) { if (res.confirm) { cb && cb() } } }) }, showLoading(title){ wx.showLoading({ title }) }, hideLoading(){ wx.hideLoading() }, analysis(str) { str = str.slice(str.indexOf('00:00.00')) let s = str.replace(/[\s\r\n]/g, "").split('['); let timeArr = [], lrcArr = []; for (let v of s) { let lrc = v.split(']') timeArr.push(lrc[0]) lrcArr.push(lrc[1]) } return { timeArr, lrcArr } }, getAudioTime(num = 0) { let minute = Math.floor(num / 60).toString(); let second = Math.floor(num % 60).toString(); return `${minute.padStart(2, '0')} : ${second.padStart(2, '0')}` }, getSecond(t) { let minute = Number(t.slice(0, 2)) let second = Number(t.slice(3, 5)) let minS = Number(t.slice(7)) return minS > 100 ? (minute * 60 + second + minS / 1000) : (minute * 60 + second + minS / 100) }, } ================================================ FILE: utils/js/dayjs.js ================================================ !function (t, e) { "object" == typeof exports && "undefined" != typeof module ? module.exports = e() : "function" == typeof define && define.amd ? define(e) : t.dayjs = e() }(this, function () { "use strict"; var t = "millisecond", e = "second", n = "minute", r = "hour", s = "day", i = "week", a = "month", u = "year", c = /^(\d{4})-?(\d{1,2})-?(\d{0,2})(.*?(\d{1,2}):(\d{1,2}):(\d{1,2}))?.?(\d{1,3})?$/, o = /\[.*?\]|Y{2,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g, h = { name: "en", weekdays: "Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"), months: "January_February_March_April_May_June_July_August_September_October_November_December".split("_") }, d = function (t, e, n) { var r = String(t); return !r || r.length >= e ? t : "" + Array(e + 1 - r.length).join(n) + t }, f = { padStart: d, padZoneStr: function (t) { var e = Math.abs(t), n = Math.floor(e / 60), r = e % 60; return (t <= 0 ? "+" : "-") + d(n, 2, "0") + ":" + d(r, 2, "0") }, monthDiff: function (t, e) { var n = 12 * (e.year() - t.year()) + (e.month() - t.month()), r = t.clone().add(n, "months"), s = e - r < 0, i = t.clone().add(n + (s ? -1 : 1), "months"); return Number(-(n + (e - r) / (s ? r - i : i - r))) }, absFloor: function (t) { return t < 0 ? Math.ceil(t) || 0 : Math.floor(t) }, prettyUnit: function (c) { return { M: a, y: u, w: i, d: s, h: r, m: n, s: e, ms: t }[c] || String(c || "").toLowerCase().replace(/s$/, "") }, isUndefined: function (t) { return void 0 === t } }, $ = "en", l = {}; l[$] = h; var m = function (t) { return t instanceof D }, y = function (t, e, n) { var r; if (!t) return null; if ("string" == typeof t) l[t] && (r = t), e && (l[t] = e, r = t); else { var s = t.name; l[s] = t, r = s } return n || ($ = r), r }, M = function (t, e) { if (m(t)) return t.clone(); var n = e || {}; return n.date = t, new D(n) }, S = function (t, e) { return M(t, { locale: e.$L }) }, p = f; p.parseLocale = y, p.isDayjs = m, p.wrapper = S; var D = function () { function h(t) { this.parse(t) } var d = h.prototype; return d.parse = function (t) { var e, n; this.$d = null === (e = t.date) ? new Date(NaN) : p.isUndefined(e) ? new Date : e instanceof Date ? e : "string" == typeof e && /.*[^Z]$/i.test(e) && (n = e.match(c)) ? new Date(n[1], n[2] - 1, n[3] || 1, n[5] || 0, n[6] || 0, n[7] || 0, n[8] || 0) : new Date(e), this.init(t) }, d.init = function (t) { this.$y = this.$d.getFullYear(), this.$M = this.$d.getMonth(), this.$D = this.$d.getDate(), this.$W = this.$d.getDay(), this.$H = this.$d.getHours(), this.$m = this.$d.getMinutes(), this.$s = this.$d.getSeconds(), this.$ms = this.$d.getMilliseconds(), this.$L = this.$L || y(t.locale, null, !0) || $ }, d.$utils = function () { return p }, d.isValid = function () { return !("Invalid Date" === this.$d.toString()) }, d.$compare = function (t) { return this.valueOf() - M(t).valueOf() }, d.isSame = function (t) { return 0 === this.$compare(t) }, d.isBefore = function (t) { return this.$compare(t) < 0 }, d.isAfter = function (t) { return this.$compare(t) > 0 }, d.year = function () { return this.$y }, d.month = function () { return this.$M }, d.day = function () { return this.$W }, d.date = function () { return this.$D }, d.hour = function () { return this.$H }, d.minute = function () { return this.$m }, d.second = function () { return this.$s }, d.millisecond = function () { return this.$ms }, d.unix = function () { return Math.floor(this.valueOf() / 1e3) }, d.valueOf = function () { return this.$d.getTime() }, d.startOf = function (t, c) { var o = this, h = !!p.isUndefined(c) || c, d = function (t, e) { var n = S(new Date(o.$y, e, t), o); return h ? n : n.endOf(s) }, f = function (t, e) { return S(o.toDate()[t].apply(o.toDate(), h ? [0, 0, 0, 0].slice(e) : [23, 59, 59, 999].slice(e)), o) }; switch (p.prettyUnit(t)) { case u: return h ? d(1, 0) : d(31, 11); case a: return h ? d(1, this.$M) : d(0, this.$M + 1); case i: return d(h ? this.$D - this.$W : this.$D + (6 - this.$W), this.$M); case s: case "date": return f("setHours", 0); case r: return f("setMinutes", 1); case n: return f("setSeconds", 2); case e: return f("setMilliseconds", 3); default: return this.clone() } }, d.endOf = function (t) { return this.startOf(t, !1) }, d.$set = function (i, c) { switch (p.prettyUnit(i)) { case s: this.$d.setDate(this.$D + (c - this.$W)); break; case "date": this.$d.setDate(c); break; case a: this.$d.setMonth(c); break; case u: this.$d.setFullYear(c); break; case r: this.$d.setHours(c); break; case n: this.$d.setMinutes(c); break; case e: this.$d.setSeconds(c); break; case t: this.$d.setMilliseconds(c) }return this.init(), this }, d.set = function (t, e) { return this.clone().$set(t, e) }, d.add = function (t, c) { var o = this; t = Number(t); var h, d = p.prettyUnit(c), f = function (e, n) { var r = o.set("date", 1).set(e, n + t); return r.set("date", Math.min(o.$D, r.daysInMonth())) }, $ = function (e) { var n = new Date(o.$d); return n.setDate(n.getDate() + e * t), S(n, o) }; if (d === a) return f(a, this.$M); if (d === u) return f(u, this.$y); if (d === s) return $(1); if (d === i) return $(7); switch (d) { case n: h = 6e4; break; case r: h = 36e5; break; case e: h = 1e3; break; default: h = 1 }var l = this.valueOf() + t * h; return S(l, this) }, d.subtract = function (t, e) { return this.add(-1 * t, e) }, d.format = function (t) { var e = this, n = t || "YYYY-MM-DDTHH:mm:ssZ", r = p.padZoneStr(this.$d.getTimezoneOffset()), s = this.$locale(), i = s.weekdays, a = s.months, u = function (t, e, n, r) { return t && t[e] || n[e].substr(0, r) }; return n.replace(o, function (t) { if (t.indexOf("[") > -1) return t.replace(/\[|\]/g, ""); switch (t) { case "YY": return String(e.$y).slice(-2); case "YYYY": return String(e.$y); case "M": return String(e.$M + 1); case "MM": return p.padStart(e.$M + 1, 2, "0"); case "MMM": return u(s.monthsShort, e.$M, a, 3); case "MMMM": return a[e.$M]; case "D": return String(e.$D); case "DD": return p.padStart(e.$D, 2, "0"); case "d": return String(e.$W); case "dd": return u(s.weekdaysMin, e.$W, i, 2); case "ddd": return u(s.weekdaysShort, e.$W, i, 3); case "dddd": return i[e.$W]; case "H": return String(e.$H); case "HH": return p.padStart(e.$H, 2, "0"); case "h": case "hh": return 0 === e.$H ? 12 : p.padStart(e.$H < 13 ? e.$H : e.$H - 12, "hh" === t ? 2 : 1, "0"); case "a": return e.$H < 12 ? "am" : "pm"; case "A": return e.$H < 12 ? "AM" : "PM"; case "m": return String(e.$m); case "mm": return p.padStart(e.$m, 2, "0"); case "s": return String(e.$s); case "ss": return p.padStart(e.$s, 2, "0"); case "SSS": return p.padStart(e.$ms, 3, "0"); case "Z": return r; default: return r.replace(":", "") } }) }, d.diff = function (t, c, o) { var h = p.prettyUnit(c), d = M(t), f = this - d, $ = p.monthDiff(this, d); switch (h) { case u: $ /= 12; break; case a: break; case "quarter": $ /= 3; break; case i: $ = f / 6048e5; break; case s: $ = f / 864e5; break; case r: $ = f / 36e5; break; case n: $ = f / 6e4; break; case e: $ = f / 1e3; break; default: $ = f }return o ? $ : p.absFloor($) }, d.daysInMonth = function () { return this.endOf(a).$D }, d.$locale = function () { return l[this.$L] }, d.locale = function (t, e) { var n = this.clone(); return n.$L = y(t, e, !0), n }, d.clone = function () { return S(this.toDate(), this) }, d.toDate = function () { return new Date(this.$d) }, d.toArray = function () { return [this.$y, this.$M, this.$D, this.$H, this.$m, this.$s, this.$ms] }, d.toJSON = function () { return this.toISOString() }, d.toISOString = function () { return this.toDate().toISOString() }, d.toObject = function () { return { years: this.$y, months: this.$M, date: this.$D, hours: this.$H, minutes: this.$m, seconds: this.$s, milliseconds: this.$ms } }, d.toString = function () { return this.$d.toUTCString() }, h }(); return M.extend = function (t, e) { return t(e, D, M), M }, M.locale = y, M.isDayjs = m, M.unix = function (t) { return M(1e3 * t) }, M.en = l[$], M }); ================================================ FILE: utils/js/props.js ================================================ const app = getApp() export default class Props{ static setPlayer(player, cb=null){ app.player = Object.assign({}, app.player, player) this.setData({ player: app.player }, ()=>{ cb && cb() }) } static setPlaying(playing, cb=null){ app.player.playing = Object.assign({}, app.player.playing, playing) this.setData({ player: app.player }, ()=>{ cb && cb() }) } } ================================================ FILE: utils/wxs/time.wxs ================================================ // wxs中只支持到es4的语法,所以别使用es6 module.exports = { getAudioTime: function(num) { num = Number(num) var minute = Math.floor(num / 60) var second = Math.floor(num % 60) return ( minute>9 ? minute : '0'+minute) + ' : ' + (second>9 ? second : '0'+second) }, dateFormat: function(t) { var date = getDate(t * 1000) var year = date.getFullYear() var month = date.getMonth() + 1 var day = date.getDate() return year + '.' + (month>9 ? month : '0' + month) + '.' + (day>9 ? day : '0' + day) } } ================================================ FILE: utils/wxss/icon.wxss ================================================ @font-face {font-family: "iconfont"; src: url('//at.alicdn.com/t/font_1101476_zl0zw7wq9p8.eot?t=1553943371044'); /* IE9 */ src: url('//at.alicdn.com/t/font_1101476_zl0zw7wq9p8.eot?t=1553943371044#iefix') format('embedded-opentype'), /* IE6-IE8 */ url('data:application/x-font-woff2;charset=utf-8;base64,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') format('woff2'), url('//at.alicdn.com/t/font_1101476_zl0zw7wq9p8.woff?t=1553943371044') format('woff'), url('//at.alicdn.com/t/font_1101476_zl0zw7wq9p8.ttf?t=1553943371044') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */ url('//at.alicdn.com/t/font_1101476_zl0zw7wq9p8.svg?t=1553943371044#iconfont') format('svg'); /* iOS 4.1- */ } .iconfont { font-family: "iconfont" !important; font-size: 16px; font-style: normal; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } .icon-news:before { content: "\e63d"; } .icon-last-play:before { content: "\e60b"; } .icon-next-play:before { content: "\e611"; } .icon-music:before { content: "\e794"; } .icon-menu:before { content: "\e62e"; } .icon-only:before { content: "\e607"; } .icon-error:before { content: "\e766"; } .icon-loading:before { content: "\e65d"; } .icon-suiji:before { content: "\e802"; } .icon-next:before { content: "\e60f"; } .icon-fm:before { content: "\e65b"; } .icon-pause:before { content: "\e620"; } .icon-play:before { content: "\e621"; } ================================================ FILE: utils/wxss/reset.wxss ================================================ html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video { margin: 0; padding: 0; border: 0; font-size: 100%; font: inherit; vertical-align: baseline; } /* HTML5 display-role reset for older browsers */ article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { display: block; } body { line-height: 1; } ol, ul { list-style: none; } blockquote, q { quotes: none; } blockquote:before, blockquote:after, q:before, q:after { content: ''; content: none; } table { border-collapse: collapse; border-spacing: 0; } ================================================ FILE: utils/wxss/style.wxss ================================================ html{ font-size: 7.3333333333vw; background: #fff; -moz-user-select: none; /*火狐*/ -webkit-user-select: none; /*webkit浏览器*/ -ms-user-select: none; /*IE10*/ -khtml-user-select: none; /*早期浏览器*/ user-select: none; margin: 0; padding: 0; } body { margin: 0; padding: 0; font-family: sans-serif; } view{ margin: 0; padding: 0; } ul>li{ list-style: none; } .df{ display: flex; } .df-1{ flex: 1; } .df-col{ display: flex; flex-direction: column; } .df-j-b{ display: flex; justify-content: space-between; } .df-j-c{ display: flex; justify-content: center; }