微信小程序(7)推荐音乐&音乐详情页

发布时间 2023-08-01 22:42:26作者: QiaoZhi

1. 效果

1. 逻辑

  1. 点击首页的每日推荐,跳转到推荐页面
  2. 推荐页面进行判断,如果没有登录重定向到登录页面; 如果已经登录就获取推荐列表并且进行展示
  3. 点击音乐能跳转到音乐详情页
  4. 音乐详情页可以进行播放暂停;同时可以上一首、下一首进行切换

2. 界面展示

  1. 推荐页面

  1. 详情页面

2. 核心知识点

1. 推荐页面-顶部日期定位

​ 父元素设置为relative 相对定位; 当前日期元素设置为绝对定位

.recommendSongContainer .header {
  position: relative;
  width: 100%;
  height: 300rpx;
}

.recommendSongContainer .header image {
  width: 100%;
  height: 100%;
}

.recommendSongContainer .header .date {
  position: absolute;
  left: 50%;
  top: 50%;
  margin-left: -150rpx;
  margin-top: -50rpx;
  width: 300rpx;
  height: 100rpx;
  text-align: center;
  line-height: 100rpx;
  color: #fff;
}

.header .date .day {
  font-size: 38rpx;
}

2. 详情页-摇杆与头像的动画(旋转一定角度)

​ 默认摇杆提起来,旋转-20度。播放的时候摇杆落下。 根据当前是否在播放然后加对应的class,根据class 展示效果。

  1. Css 两个class 效果
/* 摇杆 */
.needle {
  position: relative;
  z-index: 99;
  top: -40rpx;
  left: 60rpx;
  width: 192rpx;
  height: 274rpx;
  transform-origin: 40rpx 0;
  transform: rotate(-20deg);
  transition: transform 1s;
}

.needleRotate {
  transform: rotate(0deg);
}
  1. 摇杆类上class 控制
<image class="needle {{isPlay && 'needleRotate'}}" src="/static/images/song/needle.png"></image>

​ 这里有个根据isPlay 动态判断class是否加 needleRotate 的判断。

3. 详情页-头像旋转(整体元素转圈)

​ 默认头像不转,当播放音乐的时候头像进行旋转。和上面摇杆一样,采用动态class 元素+c3来实现。

  1. css
/* 磁盘 */
.discContainer {
  position: relative;
  top: -170rpx;
  width: 598rpx;
  height: 598rpx;
}

.discAnimation {
  animation: disc 4s linear infinite;
  animation-delay: 1s;
}

/*
  @keyframes: 设置动画帧
    1) from to
      - 使用于简单的动画,只有起始帧和结束帧
      - 北京 - 上海  直达
    2) 百分比
      - 多用于复杂的动画,动画不止两帧
      - 北京 - 上海 ---> 北京 -- 天津 --- 深圳 --- 上海
      - 0% - 100%, 可以任意拆分

*/
  1. 头像上class控制
  <view class="discContainer {{isPlay && 'discAnimation'}}">
    <image class="disc" src="/static/images/song/disc.png"></image>
    <image class="musicImg" src="{{song.al.picUrl}}"></image>
  </view>

4. 详情页-音乐播放

​ wx.getBackgroundAudioManager() 获取全局唯一的背景音频管理器。

​ 小程序切入后台,如果音频处于播放状态,可以继续播放。但是后台状态不能通过调用API操纵音频的播放状态。

​ 若需要在小程序切后台后继续播放音频,需要在 app.json中配置 requiredBackgroundModes 属性。

​ 实例代码:

const backgroundAudioManager = wx.getBackgroundAudioManager()

backgroundAudioManager.title = '寂寞沙洲冷'
backgroundAudioManager.epname = '寂寞沙洲冷'
backgroundAudioManager.singer = '于潼'
backgroundAudioManager.coverImgUrl = 'https://p1.music.126.net/-GVV1FnjB3iawDY0yyHOBg==/109951165048523233.jpg'
// 设置了 src 之后会自动播放
backgroundAudioManager.src = 'http://m801.music.126.net/20230730192632/da76bfd77d56c673c7fc00d1f6d14e9d/jdymusic/obj/wo3DlMOGwrbDjj7DisKw/14096641426/b997/eb2b/3ea7/e4bb8998c7b2c29cf8a9b2e29668f54b.mp3'

和视频管理器一样,有核心的方法回调: (下面的API是点击微信自带的音频控制器的回调)

    // 创建控制音乐播放的实例
    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('switchType', 'next')
      // 将实时进度条的长度还原成 0;时间还原成 0;
      this.setData({
        currentWidth: 0,
        currentTime: '00:00'
      })
    });
    
    // 监听音乐实时播放的进度
    this.backgroundAudioManager.onTimeUpdate(() => {
      // console.log('总时长: ', this.backgroundAudioManager.duration);
      // console.log('实时的时长: ', this.backgroundAudioManager.currentTime);
      // 格式化实时的播放时间
      let currentTime = moment(this.backgroundAudioManager.currentTime * 1000).format('mm:ss')
      let currentWidth = this.backgroundAudioManager.currentTime/this.backgroundAudioManager.duration * 450;
      this.setData({
        currentTime,
        currentWidth
      })
      
    })

5. 详情页-保存页面级数据

​ 将音乐的数据进行保存,当点击回退重新进入页面可以正确的显示数据。 第一种是存到本地,第二种是存到全局数据App.js 中。相当于全局数据,页面销毁数据还在,重新加载应用数据才会初始化。

  1. app.js 加数据
App({

  // 全局数据
  globalData: {
    isMusicPlay: false, // 是否有音乐在播放
    musicId: '' // 音乐id
  },
  
  ...
  1. 页面js 使用, songDetail.js
import PubSub from 'pubsub-js';
import moment from 'moment'
import request from '../../utils/request'
// 获取全局实例
const appInstance = getApp();

...
    if(appInstance.globalData.isMusicPlay && appInstance.globalData.musicId === musicId){
      ...

6. 详情页-页面通信(npm 模块使用)

​ 详情页点击下一页,需要从推荐页面获取数据就涉及到页面通信。需要用到npm。

​ PubSubJS 是一个用 JavaScript 编写的基于主题的 发布/订阅库。PubSubJS 有同步解耦,所以主题是异步发布的。这有助于使您的程序保持可预测性,因为在消费者处理主题时,主题的发起者不会被阻止。

  1. 项目根路径执行:
npm init -y
  1. 安装相关模块
npm install pubsub-js

​ 安装之后的目录在node_modules。

  1. 安装之后, 需要在微信开发者工具-》工具-》构建npm,然后会生成miniprogram_npm
  2. 简单使用
import PubSub from 'pubsub-js';

  PubSub.subscribe('musicId', (msg, data) => {
      console.log(msg, data); // musicId data111222
      // 取消订阅
      PubSub.unsubscribe('musicId');
    })

  PubSub.publish('musicId', 'data111222')

7. 详情页-音乐播放时长格式化

  1. 安装moment 包
npm install moment
  1. 使用
import moment from 'moment'

let currentTime = moment(35 * 1000).format('mm:ss')
console.log("currentTime", currentTime)

8. 详情页-进度条展示

思路是一个固定宽度为450rpx 的进度条,然后根据当前播放的时长与总时长的比例计算已经播放的宽度。

  1. wxml
  <!-- 进度条控制区域 -->
  <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>
  1. wxss
/* 进度条控制区域 */
.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;

}
  1. js 实时计算宽度
 		// 监听音乐实时播放的进度
    this.backgroundAudioManager.onTimeUpdate(() => {
      // console.log('总时长: ', this.backgroundAudioManager.duration);
      // console.log('实时的时长: ', this.backgroundAudioManager.currentTime);
      // 格式化实时的播放时间
      let currentTime = moment(this.backgroundAudioManager.currentTime * 1000).format('mm:ss')
      let currentWidth = this.backgroundAudioManager.currentTime/this.backgroundAudioManager.duration * 450;
      this.setData({
        currentTime,
        currentWidth
      })