赞
踩
真实接口请求的数据,数据渲染
音乐播放,暂停,下一曲
注册成为微信小程序开发者
下载微信开发者工具
新建项目:
项目目录了解:
components----组件文件夹
pages------------页面文件夹
utils--------------封装业务逻辑文件
app.js-----------全局功能实现文件(App对象,注册)
app.json--------全局配置文件
app.wxss-------全局样式设置
安装Node.js环境
下载安装:
测试:
启动代理服务器:
进入当前文件夹:
进入命令行:
注意:如果没有npm,需要先安装NODEMON
服务器启动成功
网易云音乐接口API
https://binaryify.github.io/NeteaseCloudMusicApi/#/
pages新建首页:
结构实现代码
头部:
轮播图区域代码实现:
导航区域结构实现:
推荐歌曲区域结构
样式设置:
- /* pages/index/index.wxss */
- /* 搜索 */
- .header {
- display: flex;
- padding: 10rpx;
- }
- .header image{
- width: 60rpx;
- height: 60rpx;
- }
- .header .search {
- border: 1rpx solid #eee;
- margin: 0 10rpx;
- border-radius: 10rpx;
- font-size: 26rpx;
- text-align: center;
- line-height: 60rpx;
- color: #d43c33;
- /*flex-grow:可拉伸 flex-shrink:可压缩 flex-basic:当前元素的宽度*/
- /*flex: flex-grow:0,flex-shrink:1,flex-basic:auto*/
- /*flex:1 flex-grow:1,flex-shrink:1,flex-basic:0%*/
- /*flex:auto flex-grow:1,flex-shrink:1,flex-basic:auto*/
- flex: 1;
- }
- /* 轮播图 */
- .banners{
- width: 100%;
- height: 300rpx;
- }
-
- .banners image{
- width: 100%;
- height: 100%;
- }
-
- .navContainer{
- display: flex;
- }
-
- .navItem{
- display: flex;
- flex-direction: column;
- align-items: center;
- width: 20%;
- }
- /* 矢量图 */
- .navItem .iconfont{
- width: 100rpx;
- height: 100rpx;
- border-radius: 50%;
- text-align: center;
- line-height: 100rpx;
- background: rgb(240,19,19);
- font-size: 50rpx;
- color: #fff;
- margin: 20rpx 0;
- }
-
- .navItem text{
- font-size: 26rpx;
- }
- /* 推荐歌曲 */
- .recommendContainer{
- padding: 20rpx;
- }
-
- /* 内容推荐区 */
- .recommendScroll{
- display: flex;
- height: 270rpx;
- }
- .scrollItem{
- width: 200rpx;
- margin-right: 20rpx;
- }
- .scrollItem image{
- width: 200rpx;
- height: 200rpx;
- border-radius: 10rpx;
- }
- .scrollItem text{
- font-size: 24rpx;
- /* 多行文本溢出隐藏 省略号代替 */
- overflow: hidden;
- text-overflow: ellipsis;
- display: -webkit-box;
- -webkit-box-orient: vertical;/*设置纵向对齐*/
- -webkit-line-clamp: 2;/*设置盒子行数*/
- }
- /* 排行榜 */
- .topList{
- padding: 20rpx;
- }
- .topListSwiper{
- height: 440rpx;
- }
- .swiperItem{
- width: 95%;
- background-color: rgb(248, 248, 248);
- border-radius: 10rpx;
- }
- .swiperItem .title{
- font-size: 30rpx;
- line-height: 80rpx;
- padding-left: 10rpx;
- }
- .musicItem{
- /* 一个元素为flex,子元素自动变成block */
- display: flex;
- }
- .musicItem image{
- width: 100rpx;
- height: 100rpx;
- margin-bottom: 20rpx;
- border-radius: 6rpx;
- padding-left: 10rpx;
- }
- .musicItem .count{
- width: 100rpx;
- height: 100rpx;
- text-align: center;
- line-height: 100rpx;
- }
- .musicItem .musicName{
- height: 100rpx;
- line-height: 100rpx;
- max-width: 400rpx;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- }
功能实现:
- // pages/index/index.js
- import request from '../../utils/request'
- Page({
-
- /**
- * 页面的初始数据
- */
- data: {
- bannerList:[],//轮播图数据
- recommendList:[],//推荐歌单数据
- topList:[],//排行榜数据
- },
-
- /**
- * 生命周期函数--监听页面加载
- */
- onLoad: async function (options) {
- //滑块数据
- let bannerListData = await request('/banner', {type:2});
- this.setData({
- bannerList: bannerListData.banners
- })
- //推荐歌单数据
- let recommendListData = await request('/personalized', {limit:10})
- this.setData({
- recommendList: recommendListData.result
- })
- //排行榜数据
- let index = 0;
- let resultArr = [];
- while(index<5){
- let topListData = await request('/top/list', {idx:index++});
- let topListItem = {name: topListData.playlist.name, tracks: topListData.playlist.tracks.slice(0, 3)};
- resultArr.push(topListItem);
- //拿到数据就渲染到页面,但是渲染次数多
- this.setData({
- topList: resultArr
- })
- }
- },
- //跳转到每日推荐歌曲页面
- toRecommendSong(){
- wx.navigateTo({
- url: '/pages/recommendSong/recommendSong',
- })
- },
- //跳转到搜索页面
- toSearch(){
- wx.navigateTo({
- url: '/pages/search/search',
- })
- },
-
- toSongDetail(event){
- wx.navigateTo({
- url: '/pages/songDetail/songDetail?song=' + event.currentTarget.id
- })
- },
- //跳转到歌单歌曲列表页面
- toPlayList(event){
- wx.navigateTo({
- url: '/pages/playlist/playlist?id=' + event.currentTarget.id
- })
- },
-
- /**
- * 生命周期函数--监听页面初次渲染完成
- */
- onReady: function () {
-
- },
-
- /**
- * 生命周期函数--监听页面显示
- */
- onShow: function () {
-
- },
-
- /**
- * 生命周期函数--监听页面隐藏
- */
- onHide: function () {
-
- },
-
- /**
- * 生命周期函数--监听页面卸载
- */
- onUnload: function () {
-
- },
-
- /**
- * 页面相关事件处理函数--监听用户下拉动作
- */
- onPullDownRefresh: function () {
-
- },
-
- /**
- * 页面上拉触底事件的处理函数
- */
- onReachBottom: function () {
-
- },
-
- /**
- * 用户点击右上角分享
- */
- onShareAppMessage: function () {
-
- }
- })
- <!--pages/personal/personal.wxml-->
- <view class="personalContainer">
- <view class="user-section">
- <image class="bg" src="/static/images/personal/bgImg2.jpg"></image>
- <view class="user-info-box" bindtap="toLogin">
- <view class="portrait-box">
- <image class="portrait" src='{{userInfo.avatarUrl ? userInfo.avatarUrl : "/static/images/personal/missing-face.png"}}'></image>
- </view>
- <view class="info-box">
- <text class="username">{{userInfo.nickname ? userInfo.nickname : "游客"}}</text>
- </view>
- </view>
-
- <view class="vip-card-box">
- <image class="card-bg" src="/static/images/personal/vip-card-bg.png" mode=""></image>
- <view class="b-btn">
- 立即开通
- </view>
- <view class="tit">
- <!-- 会员图标-->
- <text class="iconfont icon-huiyuan"></text>
- 会员
- </view>
- <text class="e-m">manster union</text>
- <text class="e-b">开通会员听歌, 撸代码</text>
- </view>
- </view>
-
-
- <view
- class="cover-container"
- bindtouchstart="handleTouchStart"
- bindtouchmove="handleTouchMove"
- bindtouchend="handleTouchEnd"
- style="transform: {{coverTransform}};transition: {{coverTransition}}"
- >
- <image class="arc" src="/static/images/personal/arc.png"></image>
- <!-- 个人中心导航 -->
- <view class="nav-section">
- <view class="nav-item" hover-class="common-hover" hover-stay-time="50">
- <text class="iconfont icon-xiaoxi"></text>
- <text>我的消息</text>
- </view>
- <view class="nav-item" hover-class="common-hover" hover-stay-time="50">
- <text class="iconfont icon-haoyou"></text>
- <text>我的好友</text>
- </view>
- <view class="nav-item" hover-class="common-hover" hover-stay-time="50">
- <text class="iconfont icon-geren"></text>
- <text>个人主页</text>
- </view>
- <view class="nav-item" hover-class="common-hover" hover-stay-time="50">
- <text class="iconfont icon-gexingzhuangban"></text>
- <text>个性装扮</text>
- </view>
- </view>
-
- <!-- 个人中心列表 -->
- <view class="personalContent">
- <view class="recentPlayContainer">
- <text class="title">最近播放</text>
- <!-- 最近播放记录 -->
- <scroll-view wx:if="{{recentPlayList.length}}" scroll-x="true" class="recentScroll" enable-flex="true">
- <view class="recentItem" wx:for="{{recentPlayList}}" wx:key="id">
- <image src="{{item.song.al.picUrl}}"></image>
- </view>
- </scroll-view>
- <view wx:else>暂无播放记录</view>
- </view>
-
- <view class="cardList">
- <view class="card-item">
- <text class="title">我的音乐</text>
- <text class="more"> > </text>
- </view>
- <view class="card-item">
- <text class="title">我的收藏</text>
- <text class="more"> > </text>
- </view>
- <view class="card-item">
- <text class="title">我的电台</text>
- <text class="more"> > </text>
- </view>
- </view>
- </view>
- </view>
-
- <!-- 退出登录按钮 -->
- <view hidden="{{!isLogin}}">
- <button class="logout-btn" bindtap="logout">退出登录</button>
- </view>
- </view>
- /* pages/personal/personal.wxss */
- /* pages/personal/personal.wxss */
- .personalContainer {
- width: 100%;
- height: 100%;
-
- }
-
- .personalContainer .user-section {
- height: 520rpx;
- position: relative;
- padding: 100rpx 30rpx 0;
- }
- .user-section .bg {
- position: absolute;
- left: 0;
- top: 0;
- width: 100%;
- height: 100%;
- opacity: 0.7;
- filter: blur(1px);
- }
-
-
- .user-info-box{
- height: 180rpx;
- display:flex;
- align-items:center;
- position:relative;
- z-index: 1;
- }
-
- .user-info-box .portrait{
- width: 130rpx;
- height: 130rpx;
- border:5rpx solid #fff;
- border-radius: 50%;
- }
- .user-info-box .username{
- font-size: 24;
- color: #303133;
- margin-left: 20rpx;
- }
-
- /* vip-box */
- .vip-card-box {
- position: relative;
- display: flex;
- flex-direction: column;
- background: rgba(0, 0, 0, .7);
- height: 240rpx;
- color: #f7d680;
- border-radius: 16rpx 16rpx 0 0;
- padding: 20rpx 24rpx;
- }
-
-
- .vip-card-box .card-bg{
- position:absolute;
- top: 20rpx;
- right: 0;
- width: 380rpx;
- height: 260rpx;
- }
-
- .vip-card-box .b-btn{
- position: absolute;
- right: 20rpx;
- top: 16rpx;
- width: 132rpx;
- height: 40rpx;
- text-align: center;
- line-height: 40rpx;
- font-size: 22rpx;
- color: #36343c;
- border-radius: 20px;
- background: #f9e6af;
- z-index: 1;
- }
-
- .vip-card-box .b-btn{
- position: absolute;
- right: 20rpx;
- top: 16rpx;
- width: 132rpx;
- height: 40rpx;
- text-align: center;
- line-height: 40rpx;
- font-size: 22rpx;
- color: #36343c;
- border-radius: 20px;
- /*background: linear-gradient(left, #f9e6af, #ffd465);*/ /*渐变不生效*/
- background: #f9e6af;
- z-index: 1;
- }
-
- .vip-card-box .tit {
- font-size: 22rpx;
- color: #f7d680;
- margin-bottom: 28rpx;
- }
- .vip-card-box .tit .iconfont{
- color: #f6e5a3;
- margin-right: 16rpx;
- }
-
-
-
-
- .vip-card-box .e-m{
- font-size: 34rpx;
- margin-top: 10rpx;
- }
- .vip-card-box .e-b{
- font-size: 24rpx;
- color: #d8cba9;
- margin-top: 10rpx;
- }
-
-
- .cover-container{
- margin-top: -150rpx;
- padding: 0 30rpx;
- position:relative;
- background: #f5f5f5;
- padding-bottom: 20rpx;
- }
-
- .cover-container .arc{
- position:absolute;
- left: 0;
- top: -34rpx;
- width: 100%;
- height: 36rpx;
- }
-
-
- /* 导航部分 */
- .cover-container .nav-section {
- display: flex;
- background: #fff;
- padding: 20rpx 0;
- border-radius: 15rpx;
- }
-
-
- .nav-section .nav-item {
- width: 25%;
- box-sizing: border-box;
- display: flex;
- flex-direction: column;
- align-items: center;
- }
-
- .nav-section .nav-item .iconfont {
- font-size: 50rpx;
- color: #d43c33;
- line-height: 70rpx;
- }
-
- .nav-section .nav-item text:last-child {
- font-size: 22rpx;
-
- }
-
-
- /* 个人中心列表 */
- .personalContent {
- background: #fff;
- margin-top: 20rpx;
- }
-
- /* 最近播放 */
- .personalContent .scrollView {
- display: flex;
- height: 160rpx;
- }
- .personalContent .recentPlay {
- display: flex;
- }
-
- .recentPlayContainer .title {
- padding-left: 20rpx;
- font-size: 26rpx;
- color: #333;
- line-height: 80rpx;
- }
-
- .personalContent .recentPlay image {
- width: 160rpx;
- height: 160rpx;
- margin-left: 20rpx;
- border-radius: 20rpx;
- }
-
-
- .cardList {
- margin-top: 20rpx;
-
- }
- .cardList .card-item{
- border-top: 1rpx solid #eee;
- height: 80rpx;
- line-height: 80rpx;
- padding: 10rpx;
- font-size: 26rpx;
- color: #333;
- }
- .cardList .card-item .more {
- float: right;
- }
-
- /*最近播放记录*/
- .recentScroll {
- display: flex;
- height: 200rpx;
- }
- .recentItem {
- margin: 0 20rpx;
- }
- .recentItem image{
- width: 200rpx;
- height: 200rpx;
- border-radius: 10rpx;
- }
-
- .logout-btn {
- margin-top: 5rpx;
- }
- // pages/personal/personal.js
- import request from '../../utils/request'
- let startY = 0;//手指起始坐标
- let moveY = 0;//手指移动坐标
- let moveDistance = 0;//手指移动距离
- Page({
-
- /**
- * 页面的初始数据
- */
- data: {
- coverTransform: 'translateY(0)',
- coverTransition: '',
- userInfo: {},
- recentPlayList: [],//播放记录
- isLogin: false //控制退出登录显示
- },
-
- /**
- * 生命周期函数--监听页面加载
- */
- onLoad: function (options) {
- //读取用户基本信息
- let userInfo = wx.getStorageSync('userInfo');
- if(userInfo){
- //更新用户信息
- this.setData({
- userInfo: JSON.parse(userInfo),
- isLogin: true
- })
- //播放记录
- this.getUserRecentPlayList(this.data.userInfo.userId)
- }
- },
-
- //获取用户播放记录
- async getUserRecentPlayList(userId){
- let recentPlayListData = await request('/user/record',{uid: userId,type: 0});
- let index = 0;
- let recentPlayList = recentPlayListData.allData.splice(0,10).map(item=>{
- item.id = index++;
- return item;
- })
- this.setData({
- recentPlayList: recentPlayList
- })
- },
-
- handleTouchStart(event){
- this.setData({
- coverTransition: ''
- })
- //起始手指坐标
- startY = event.touches[0].clientY;
- },
- handleTouchMove(event){
- moveY = event.touches[0].clientY;
- moveDistance = moveY - startY;
-
- if(moveDistance <= 0){
- return;
- }
- if(moveDistance >= 80){
- moveDistance = 80;
- }
-
- //动态更新coverTransform
- this.setData({
- coverTransform: 'translateY('+moveDistance+'rpx)'
- })
- },
- handleTouchEnd(){
- //重置coverTransform
- this.setData({
- coverTransform: 'translateY(0)',
- coverTransition: 'transform 1s linear'
- })
- },
- //登录跳转
- toLogin(){
- wx.navigateTo({
- url: '/pages/login/login',
- })
- },
- //退出登录
- logout(){
- //服务端退出登录状态
- let status = request('/logout');
- console.log(status)
- //删除客户端信息
- wx.removeStorageSync('userInfo')
- this.setData({
- userInfo: {},
- isLogin: false
- })
- wx.navigateTo({
- url: '/pages/login/login',
- })
- wx.showToast({
- title: '退出登录',
- icon: 'success'
- })
- },
- /**
- * 生命周期函数--监听页面初次渲染完成
- */
- onReady: function () {
-
- },
-
- /**
- * 生命周期函数--监听页面显示
- */
- onShow: function () {
-
- },
-
- /**
- * 生命周期函数--监听页面隐藏
- */
- onHide: function () {
-
- },
-
- /**
- * 生命周期函数--监听页面卸载
- */
- onUnload: function () {
-
- },
-
- /**
- * 页面相关事件处理函数--监听用户下拉动作
- */
- onPullDownRefresh: function () {
-
- },
-
- /**
- * 页面上拉触底事件的处理函数
- */
- onReachBottom: function () {
-
- },
-
- /**
- * 用户点击右上角分享
- */
- onShareAppMessage: function () {
-
- }
- })
- <!--pages/recommendSong/recommendSong.wxml-->
- <view class="recommendSongContainer">
- <!-- 头部 -->
- <view class="header">
- <image src="/static/images/recommendSong/recommend.jpg"></image>
- <view class="date">
- <text class="year">{{year}} / </text>
- <text class="month">{{month}} / </text>
- <text class="day">{{day}}</text>
- </view>
- </view>
- <!-- 列表区域 -->
- <view class="ListContainer">
- <view class="listHeader">
- <text>播放全部</text>
- <text class="changeMore">多选</text>
- </view>
- <!-- 歌曲 -->
- <scroll-view scroll-y="true" class="listScroll">
- <view class="scrollItem" wx:for="{{recommendList}}" wx:key="id" data-index="{{index}}" data-song="{{item}}" bindtap="toSongDetail">
- <image src="{{item.album.picUrl}}"></image>
- <view class="musicInfo">
- <text class="musicName">{{item.name}}</text>
- <text class="musicAuthor">{{item.artists[0].name}}</text>
- </view>
- <text class="iconfont icon-icon"></text>
- </view>
- </scroll-view>
- </view>
- </view>
- /* pages/recommendSong/recommendSong.wxss */
- /* 头部 */
- .recommendSongContainer .header {
- position: relative;
- width: 100%;
- height: 300rpx;
- }
- .recommendSongContainer .header image {
- width: 100%;
- height: 100%;
- }
- .recommendSongContainer .header .date {
- position: absolute;
- left: 30%;
- top:20%;
-
- width: 300rpx;
- height: 100rpx;
- text-align: center;
- line-height: 100rpx;
- color: #B9B3E1;
- font-size: 40rpx;
- }
- .date .month {
- font-size: 60rpx;
- }
- /* 列表 */
- .ListContainer {
- position: relative;
- top: -20rpx;
- padding: 0 20rpx;
- border-radius: 30rpx;
- background: #fff;
- }
- .listHeader {
- height: 80rpx;
- line-height: 80rpx;
-
- }
- .listHeader .changeMore {
- float: right;
- }
- /* 歌曲 */
- .listScroll {
- height: calc(100vh - 380rpx);/*整体高度减去除了scroll-view的元素的高度,即可得到只使得scroll-view元素滑动效果*/
- }
- .scrollItem {
- display: flex;
- position: relative;
- margin-bottom: 20rpx;
- }
- .scrollItem image {
- height: 80rpx;
- width: 80rpx;
- border-radius: 8rpx;
- }
- .musicInfo {
- display: flex;
- flex-direction: column;
- margin-left: 20rpx;
- }
- .musicInfo text {
- height: 40rpx;
- line-height: 40rpx;
- font-size: 26rpx;
- max-width: 400rpx;/*最长文本长度*/
- white-space: nowrap;/*不允许换行*/
- overflow: hidden;/*超过部分隐藏*/
- text-overflow: ellipsis;/*超过部分换为省略号*/
- }
- .scrollItem .iconfont{
- position: absolute;
- right: 10rpx;
- width: 80rpx;
- height: 80rpx;
- text-align: right;
- }
- // pages/recommendSong/recommendSong.js
- import PubSub from 'pubsub-js';
- import request from '../../utils/request'
- Page({
-
- /**
- * 页面的初始数据
- */
- data: {
- year: '',//年
- month: '',//月
- day: '',//天
- recommendList: [],//推荐列表数据
- index: 0,//音乐下标
- },
-
- /**
- * 生命周期函数--监听页面加载
- */
- onLoad: function (options) {
- //判断用户是否登录
- let userIinfo = wx.getStorageSync('userInfo');
- if(!userIinfo){
- wx.showToast({
- title: '请先进行登录',
- icon: 'none',
- success: ()=>{
- //跳转至登录界面
- wx.reLaunch({
- url: '/pages/login/login',
- })
- }
- })
- }
- let nowTime = new Date();
- //更新日期
- this.setData({
- day: nowTime.getDate(),
- month: nowTime.getMonth() + 1,
- year: nowTime.getFullYear()
- })
-
- //获取每日推荐的数据
- this.getRecommendList();
-
- //订阅来自songDetail页面发布的消息
- PubSub.subscribe('switchMusic',(msg,type) => {
- let {recommendList,index} = this.data;
- if(type === 'pre'){//上一首
- (index === 0) && (index = recommendList.length);
- index -= 1;
- }else{//下一首
- (index === recommendList.length - 1) && (index = -1);
- index += 1;
- }
-
- //更新下标
- this.setData({
- index: index
- })
-
- let musicId = recommendList[index].id;
- //将音乐id回传给songDetail页面
- PubSub.publish('musicId',musicId);
- })
- },
-
- //获取每日推荐数据
- async getRecommendList(){
- let recommendListData = await request('/recommend/songs');
-
- this.setData({
- recommendList: recommendListData.recommend
- })
-
- },
- //跳转至songDetail页面
- toSongDetail(event){
- let {song,index} = event.currentTarget.dataset;
-
- this.setData({
- index: index
- })
- //路由跳转传参:query参数
- wx.navigateTo({
- url: '/pages/songDetail/songDetail?song=' + song.id
- })
- },
-
- /**
- * 生命周期函数--监听页面初次渲染完成
- */
- onReady: function () {
-
- },
-
- /**
- * 生命周期函数--监听页面显示
- */
- onShow: function () {
-
- },
-
- /**
- * 生命周期函数--监听页面隐藏
- */
- onHide: function () {
-
- },
-
- /**
- * 生命周期函数--监听页面卸载
- */
- onUnload: function () {
-
- },
-
- /**
- * 页面相关事件处理函数--监听用户下拉动作
- */
- onPullDownRefresh: function () {
-
- },
-
- /**
- * 页面上拉触底事件的处理函数
- */
- onReachBottom: function () {
-
- },
-
- /**
- * 用户点击右上角分享
- */
- onShareAppMessage: function () {
-
- }
- })
- <!--pages/songDetail/songDetail.wxml-->
- <view class="songDetailContainer">
-
- <view class="musicAuthor">{{song.ar[0].name}}</view>
- <view class="circle"></view>
-
- <!-- 摇杆 -->
- <image class="needle {{isPlay && 'needleRotate'}}" src="/static/images/song/needle.png"></image>
- <!-- 磁盘 -->
- <view class="discContainer {{isPlay && 'discAnimation'}}">
- <image class="disc" src="/static/images/song/disc.png"></image>
- <!-- 歌曲封面图 -->
- <image class="musicImg" src="{{song.al.picUrl}}"></image>
- </view>
- <!-- 歌词 -->
- <view class="scrollLrc">
- <text>{{currentLyric}}</text>
- </view>
- <!-- 进度条控制 -->
- <view class="progressControl">
- <text>{{currentTime}}</text>
- <!-- 总进度条 -->
- <view class="barControl">
- <!-- 实时进度条 -->
- <view class="audio-currentTime-Bar" style="width: {{currentWidth + 'rpx'}}">
- <!-- 小圆球 -->
- <view class="audio-circle"></view>
- </view>
- </view>
- <text>{{durationTime}}</text>
- </view>
-
- <!-- 歌曲播放控制 -->
- <view class="musicControl">
- <text class="iconfont icon-random"></text>
- <text class="iconfont icon-diyigeshipin" id="pre" bindtap="handleSwitch"></text>
- <text class="iconfont {{isPlay ? 'icon-zanting' : 'icon-kaishi'}} big" bindtap="handleMusicPlay"></text>
- <text class="iconfont icon-zuihouyigeshipin" id="next" bindtap="handleSwitch"></text>
- <text class="iconfont icon-liebiao"></text>
- </view>
-
- </view>
- /* pages/songDetail/songDetail.wxss */
- .songDetailContainer {
- height: 100%;
- background: rgba(0,0,0,0.5);
- display: flex;
- flex-flow: column;
- align-items: center;
- }
- /* 底座 */
- .circle {
- position: relative;
- z-index: 100;
- width: 60rpx;
- height: 60rpx;
- border-radius: 50%;
- background: #fff;
- margin: 10rpx 0;
- }
- /* 摇杆 */
- .needle {
- position: relative;
- z-index: 99;
- top: -40rpx;
- left: 56rpx;
- width: 192rpx;
- height: 274rpx;
- transform-origin: 40rpx 0;
- transform: rotate(-20deg);
- transition: transform 1s;
- }
- /* 摇杆落下 */
- .needleRotate {
- transform: rotate(0deg);
- }
- .discContainer {
- position: relative;
- top: -170rpx;
- width: 598rpx;
- height: 598rpx;
- }
- .discAnimation {
- animation: disc 20s linear infinite;
- animation-delay: 1s;
- }
- /*设置动画帧 1.from to(只有起始帧和结束帧) 2.百分比(不止两帧)*/
- @keyframes disc{
- from {
- transform: rotate(0deg);
- }
- to {
- transform: rotate(360deg);
- }
- }
- /* 磁盘 */
- .disc {
- width: 100%;
- height: 100%;
- }
- /* 歌曲封面 */
- .musicImg {
- position: absolute;
- top: 0;
- left: 0;
- bottom: 0;
- right: 0;
- margin: auto;
- width: 370rpx;
- height: 370rpx;
- border-radius: 50%;
- }
- /* 歌词显示 */
- .scrollLrc {
- position: absolute;
- bottom: 280rpx;
- width: 640rpx;
- height: 120rpx;
- line-height: 120rpx;
- text-align: center;
- }
- /* 底部控制器 */
- .musicControl {
- position: absolute;
- bottom: 40rpx;
- left: 0;
- border-top: 1rpx solid #fff;
- width: 100%;
- display: flex;
- }
- .musicControl text {
- width: 20%;
- height: 120rpx;
- line-height: 120rpx;
- text-align: center;
- color: #fff;
- font-size: 50rpx;
- }
- .musicControl text.big {
- font-size: 80rpx;
- }
- /* 进度条控制 */
- .progressControl {
- position: absolute;
- bottom: 200rpx;
- width: 640rpx;
- height: 80rpx;
- line-height: 80rpx;
- display: flex;
- }
- .barControl {
- position: relative;
- width: 450rpx;
- height: 4rpx;
- background: rgba(0,0,0,0.4);
- margin: auto;
- }
- .audio-currentTime-Bar {
- position: absolute;
- top: 0;
- left: 0;
- z-index: 1;
- height: 4rpx;
- background: red;
- }
- /* 小圆球 */
- .audio-circle {
- position: absolute;
- right: -12rpx;
- top: -4rpx;
- width: 12rpx;
- height: 12rpx;
- border-radius: 50%;
- background: #fff;
- }
- // pages/songDetail/songDetail.js
- import PubSub from 'pubsub-js';
- import moment from 'moment';
- import request from '../../utils/request';
- //获取全局实例
- const appInstance = getApp();
- Page({
-
- /**
- * 页面的初始数据
- */
- data: {
- isPlay: false,//标识播放状态
- song: {},//歌曲详情对象
- musicId: '',//歌曲Id
- currentTime: '00:00',//当前时长
- durationTime:'00:00',//总时长
- currentWidth: 0,//实时进度条宽度
- lyric: [],//歌词
- lyricTime: 0,//歌词对应的时间
- currentLyric: "",//当前歌词对象
- },
-
- /**
- * 生命周期函数--监听页面加载
- */
- onLoad: function (options) {
- //options路由跳转参数
- let musicId = options.song;
- this.setData({
- musicId: musicId
- })
- this.getMusicInfo(musicId);
- this.getLyric(musicId);
- //判断当前页面音乐是否在播放
- if(appInstance.globalData.isMusicPlay && appInstance.globalData.musicId === musicId){
- //修改当前页面音乐播放状态
- this.setData({
- isPlay: true
- })
- }
-
- //创建控制音乐播放实例对象
- this.backgroundAudioManager = wx.getBackgroundAudioManager();
- //监视音乐播放与暂停
- this.backgroundAudioManager.onPlay(()=>{
- //修改音乐播放状态
- this.changePlayState(true);
-
- appInstance.globalData.musicId = musicId;
- });
- this.backgroundAudioManager.onPause(()=>{
- this.changePlayState(false);
- });
- this.backgroundAudioManager.onStop(()=>{
- this.changePlayState(false);
- });
- //音乐播放自然结束
- this.backgroundAudioManager.onEnded(()=>{
- //切歌
- PubSub.publish('switchMusic','next');
- this.setData({
- currentWidth: 0,
- currentTime: '00:00',
- lyric: [],
- lyricTime: 0,
- })
- })
- //监听音乐实时播放的进度
- this.backgroundAudioManager.onTimeUpdate(() => {
- //获取歌词对应时间
- let lyricTime = Math.ceil(this.backgroundAudioManager.currentTime);
- let currentTime = moment(this.backgroundAudioManager.currentTime * 1000).format('mm:ss');
- let currentWidth = (this.backgroundAudioManager.currentTime/this.backgroundAudioManager.duration) * 450;
-
- this.setData({
- lyricTime,
- currentTime,
- currentWidth
- })
-
- this.getCurrentLyric();
- })
-
- },
-
- //修改播放状态
- changePlayState(isPlay){
- this.setData({
- isPlay: isPlay
- })
- //修改全局播放状态
- appInstance.globalData.isMusicPlay = isPlay;
- },
- //点击暂停/播放的回调
- handleMusicPlay(){
- //修改是否播放的状态
- let isPlay = !this.data.isPlay;
- // this.setData({
- // isPlay: isPlay
- // })
- let {musicId} = this.data;
- this.musicControl(isPlay,musicId);
- },
- //请求歌曲信息
- async getMusicInfo(musicId){
- let songData = await request('/song/detail',{ids: musicId});
- let durationTime = moment(songData.songs[0].dt).format('mm:ss');
- this.setData({
- song: songData.songs[0],
- durationTime: durationTime
- })
- //动态修改窗口标题
- wx.setNavigationBarTitle({
- title: this.data.song.name
- })
- },
-
- //歌曲播放控制功能
- async musicControl(isPlay,musicId){
-
- if(isPlay){//音乐播放
- //获取音频资源
- let musicLinkData = await request('/song/url',{id: musicId})
- let musicLink = musicLinkData.data[0].url;
- console.log(musicLink)
- if(musicLink === null){
- wx.showToast({
- title: '请开通会员后听取',
- icon: 'none'
- })
- return;
- }
- //歌曲播放
- this.backgroundAudioManager.src = musicLink;
- this.backgroundAudioManager.title = this.data.song.name;
- }else{//音乐暂停
- this.backgroundAudioManager.pause();
- }
- },
-
- //歌曲切换
- handleSwitch(event){
- //切换类型
- let type = event.currentTarget.id;
-
- //关闭当前播放音乐
- this.backgroundAudioManager.stop();
-
- //订阅来自recommendSong页面
- PubSub.subscribe('musicId',(msg,musicId) => {
- //获取歌曲
- this.getMusicInfo(musicId);
- //自动播放当前音乐
- this.musicControl(true,musicId);
- //取消订阅
- PubSub.unsubscribe('musicId');
- })
- //发布消息数据给recommendSong页面
- PubSub.publish('switchMusic',type);
- },
-
- //获取歌词
- async getLyric(musicId){
- let lyricData = await request("/lyric", {id: musicId});
- let lyric = this.formatLyric(lyricData.lrc.lyric);
- },
-
- //传入初始歌词文本text
- formatLyric(text) {
- let result = [];
- let arr = text.split("\n"); //原歌词文本已经换好行了方便很多,我们直接通过换行符“\n”进行切割
- let row = arr.length; //获取歌词行数
- for (let i = 0; i < row; i++) {
- let temp_row = arr[i]; //现在每一行格式大概就是这样"[00:04.302][02:10.00]hello world";
- let temp_arr = temp_row.split("]");//我们可以通过“]”对时间和文本进行分离
- let text = temp_arr.pop(); //把歌词文本从数组中剔除出来,获取到歌词文本了!
- //再对剩下的歌词时间进行处理
- temp_arr.forEach(element => {
- let obj = {};
- let time_arr = element.substr(1, element.length - 1).split(":");//先把多余的“[”去掉,再分离出分、秒
- let s = parseInt(time_arr[0]) * 60 + Math.ceil(time_arr[1]); //把时间转换成与currentTime相同的类型,方便待会实现滚动效果
- obj.time = s;
- obj.text = text;
- result.push(obj); //每一行歌词对象存到组件的lyric歌词属性里
- });
- }
- result.sort(this.sortRule) //由于不同时间的相同歌词我们给排到一起了,所以这里要以时间顺序重新排列一下
- this.setData({
- lyric: result
- })
- },
- sortRule(a, b) { //设置一下排序规则
- return a.time - b.time;
- },
-
- //控制歌词播放
- getCurrentLyric(){
- let j;
- for(j=0; j<this.data.lyric.length-1; j++){
- if(this.data.lyricTime == this.data.lyric[j].time){
- this.setData({
- currentLyric : this.data.lyric[j].text
- })
- }
- }
- },
-
- /**
- * 生命周期函数--监听页面初次渲染完成
- */
- onReady: function () {
-
- },
-
- /**
- * 生命周期函数--监听页面显示
- */
- onShow: function () {
-
- },
-
- /**
- * 生命周期函数--监听页面隐藏
- */
- onHide: function () {
-
- },
-
- /**
- * 生命周期函数--监听页面卸载
- */
- onUnload: function () {
-
- },
-
- /**
- * 页面相关事件处理函数--监听用户下拉动作
- */
- onPullDownRefresh: function () {
-
- },
-
- /**
- * 页面上拉触底事件的处理函数
- */
- onReachBottom: function () {
-
- },
-
- /**
- * 用户点击右上角分享
- */
- onShareAppMessage: function () {
-
- }
- })
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。