• 微信小程序的分包加载


    前情提要

    某些情况下,需要对小程序进行分包。分包后的小程序必定有一个主包,一个或多个分包
    小程序启动时,默认下载 主包并启动主包内页面,只有当进入分包内某个页面时,客户端才会下载对应分包,然后展示分包页面,即分包按需进行加载的。

    目前小程序分包大小有如下限制:

    1. 整个小程序所有分包大小不超过 20M
    2. 主包 或 单个分包大小不超过 2M

    小程序的包结构

    小程序的包结构如下。其中,文件夹pages下的是主包,文件夹music-package下的是分包。
    在这里插入图片描述

    app.json声明包结构

    app.json中声明包结构。其中, pages 字段声明了主包,subpackages 字段声明了分包结构。subpackages的值是一个数组,数组的每个元素是一个对象,该对象包含如下属性:

    • root,分包的根目录,string类型。
    • name,分包别名,分包预下载时可以使用,string类型。
    • pages,分包页面路径,相对于分包根目录,即相对于root。pages是一个字符串数组。
    • independent,是否使用独立分包,布尔类型。

    独立分包

    独立分包是小程序中一种特殊类型的分包,它可以独立于主包和其他分包运行
    一个小程序可以有多个独立分包。
    当小程序从主包页面或者其他分包页面启动时,需要先下载主包。而当进入独立分包页面时,不需要下载主包,因为独立分包是独立于主包和其他分包运行的。

    声明独立分包

    app.json中的subpackages字段下的各配置项的 independent 字段,设置为 true,即为独立分包。

    分包预下载

    分包预下载,即在进入小程序页面时,预先下载可能需要的分包。

    声明分包预下载

    app.jsonpreloadRule 下声明分包预下载。preloadRule的值是一个对象,该对象中的内容以key-value的形式出现。其中,key是某个分包页面路径,value是一个对象,该对象包含如下属性:

    • packages,是一个数组,每个元素是进入页面后预下载分包的rootname。另外,__APP__代表主包。如packages:["package_a_root","package_b_name","__APP__"]
    • network,表示在指定网络下预加载。string类型,支持以下两个值:
      • "all",表示不限网络。
      • "wifi",表示,仅wifi下预下载。

    搭建静态资源服务器

    1. 全局安装serve:npm install -g serve
    2. 在任何你想的地方新建文件夹:resources。resources下新建文件夹:images,用于存放静态图片资源;resources下新建文件夹:videos,用于存放视频文件;resources下新建文件夹:audios,用于存放音频文件。
    3. 启动服务器:serve resources

    小程序项目

    代码涉及的主要文件有:

    1. app.json
    2. app.wxss
    3. app.js
    4. pages/search/search.json
    5. pages/search/search.wxml
    6. pages/search/search.wxss
    7. pages/search/search.js
    8. pages/login/login.json
    9. pages/login/login.wxml
    10. pages/login/login.wxss
    11. pages/login/login.js
    12. music-package/pages/index/index.json
    13. music-package/pages/index/index.wxml
    14. music-package/pages/index/index.wxss
    15. music-package/pages/index/index.js
    16. music-package/pages/music/music.json
    17. music-package/pages/music/music.wxml
    18. music-package/pages/music/music.wxss
    19. music-package/pages/music/music.js

    在这里插入图片描述

    app.json

    {
      "pages": [
        "pages/search/search",
        "pages/login/login"
      ],
      "subPackages": [ 
        {
          "root": "music-package",
          "pages": [
            "pages/index/index",
            "pages/music/music"
          ]
        }
      ],
      "window": {
        "navigationBarBackgroundColor": "#fff",
        "navigationBarTitleText": "首页",
        "navigationBarTextStyle": "black"
      },
      "tabBar": {
        "position": "top",
        "color": "#000000",
        "selectedColor": "#971a22",
        "backgroundColor": "#ffffff",
        "list": [
          {
            "pagePath": "pages/search/search",
            "text": "搜索音乐"
          },
          {
            "pagePath": "pages/login/login",
            "text": "个人登录"
          }
        ]
      },
      "requiredBackgroundModes": [
        "audio"
      ],
      "style": "v2",
      "sitemapLocation": "sitemap.json"
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    主包

    app.wxss

    page{
      height: 100%;
    }
    
    • 1
    • 2
    • 3

    app.js

    App({
      globalData:{
        isPlayGlobal:false,   //当前是否有歌曲在播放
        musicIdGlobal:''     //当前正在播放的歌曲是哪首
      }
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    pages/search/search.json

    {
      "usingComponents": {},
      "navigationBarTitleText": "搜索"
    }
    
    • 1
    • 2
    • 3
    • 4

    pages/search/search.wxml

    <view class="search-container">
      <view class="header">
        <view class="input-box">
          <image src="/static/images/search.png">image>
          <input type="text" placeholder="你想听的 这里都有~" placeholder-style="font-swxize:27rpx"
            bindinput="handleInput" value="{{keyword}}"/>
            <image src="/static/images/cross.png" bindtap="removeKeyword" hidden="{{!keyword}}">image>
        view>
      view>
      <block wx:if="{{keyword}}">
        <view class="search-container">
          <view class="search-title">搜索"{{keyword}}"view>
          <view class="search-list">
            <view class="search-item" wx:for="{{searchList}}" wx:key="id">
              <image src="/static/images/search.png">image>
              <view class="content">{{item.content}}view>
            view>
          view>
        view>
      block>
      <block wx:else>
        <view class="history-container" wx:if="{{historyList.length}}">
          <view class="history-header">
            <view class="history-title">搜索历史view>
            <image src="/static/images/delete.png" bindtap="deleteHistory">image>
          view>
          <view class="history-list">
            <text class="history-item" wx:for="{{historyList}}" wx:key="*this">{{item}}text>
          view>
        view>
        <view class="hot-container">
        <view class="hot-title">热搜榜view>
        <view class="hot-list">
          <view class="hot-item" wx:for="{{hotList}}" wx:key="id">
            <text class="order" style="{{(index===0 || index ===1 || index==2) && 'color:#d81e06' }}">{{index+1}}text>
            <text class="name">{{item.keyword}}text>
            <image wx:if="{{item.iconUrl}}" src="{{item.iconUrl}}">image>
          view>
        view>
      view>
      block>
    view>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    pages/search/search.wxss

    .search-container{
      padding: 20rpx;
    }
    .input-box{
      background: #eee;
      border-radius: 28rpx;
      display: flex;
      align-items: center;
    }
    .input-box input{
      height: 60rpx;
      line-height: 60rpx;
      flex: 1;
      font-size: 27rpx;
    }
    .input-box image{
      width: 36rpx;
      height: 36rpx;
      padding: 0 20rpx;
    }
    .hot-container{
      margin: 20rpx 0;
    }
    .hot-container .hot-title{
      font-size: 26rpx;
      font-weight:550;
    }
    .hot-list{
      padding: 10rpx 0 ;
    }
    .hot-item{
      height: 60rpx;
      line-height: 60rpx;
      display: flex;
      align-items: center;
      margin-bottom: 20rpx;
    }
    .hot-item .order{
      display: inline-block;
      width: 40rpx;
      height: 60rpx;
      line-height: 60rpx;
      text-align: center;
      margin-right: 30rpx;
      color: #888;
    }
    .hot-item .name{
      font-weight: 550;
    }
    .hot-item image{
      width: 48rpx;
      height: 48rpx;
      margin-left: 20rpx;
    }
    .search-container .search-title{
      color: #d81e06;
      height: 80rpx;
      line-height: 80rpx;
      font-size: 28rpx;
    }
    .search-item{
      display: flex;
      align-items: center;
      height: 80rpx;
      line-height: 80rpx;
    }
    .search-item image{
      width: 28rpx;
      height: 28rpx;
      margin-right: 20rpx;
    }
    .search-item .content{
      flex:1;
      color: #666;
      font-size: 28rpx;
    }
    .history-container{
      margin-top: 20rpx;
    }
    .history-header {
      display: flex;
      align-items: center;
      justify-content: space-between;
    }
    .history-header .history-title{
      font-size: 26rpx;
      font-weight:550;
    }
    .history-header image{
      width: 36rpx;
      height: 36rpx;
    }
    .history-list{
      display: flex;
      flex-wrap: wrap;
      padding: 20rpx 0;
    }
    .history-item{
      font-size: 26rpx;
      height: 36rpx;
      line-height: 36rpx;
      text-align: center;
      padding: 6rpx 16rpx;
      background: #eee;
      border-radius: 16rpx;
      margin: 0 20rpx 20rpx 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107

    pages/search/search.js

    const host = "http://localhost:3000"
    let timer = null;
    
    Page({
      data:{
        hotList:[],
        keyword:'',
        searchList:[],
        historyList:[]
      },
      onLoad(){
        this.getHotList();
        const historyList = wx.getStorageSync('historyList');
        if(historyList){
          this.setData({historyList})
        }
      },
      getHotList(){
        const result = [
          {id:"001",keyword:"周杰伦",iconUrl:host+"/images/hot-fill.png"},
          {id:"002",keyword:"最伟大的作品"},
          {id:"003",keyword:"林俊杰"},
          {id:"004",keyword:"孤勇者"},
          {id:"005",keyword:"再见莫妮卡"},
          {id:"006",keyword:"陈奕迅"},
          {id:"007",keyword:"李荣浩"},
          {id:"008",keyword:"毛不易"}
        ]
        this.setData({hotList:result})
      },
      handleInput(event){
        const keyword = event.detail.value.trim();
        if(!keyword) {
          this.setData({keyword:''});
          return;
        }
        this.throttle(this.getSearchList,500);
    
        const {historyList} = this.data;
        const index = historyList.indexOf(keyword);
        if(index > -1){
          historyList.splice(index,1);
        }
        const newHistoryList = [keyword,...historyList].slice(0,10) //最多显示10条搜索历史,且后来者居上
        wx.setStorageSync("historyList",newHistoryList)
        this.setData({
          keyword,
          historyList:newHistoryList
        });
      },
      throttle(fn,delay){
        if(timer != null) return;
        timer = setTimeout(() => {
          timer = null
          fn();
        },delay) 
      },
      getSearchList(){
        const result = [
          {id:"001",content:"周杰伦"},
          {id:"002",content:"周杰伦 最伟大的作品"},
          {id:"003",content:"周杰伦 爷爷泡的茶"},
          {id:"004",content:"周杰伦 珊瑚海"},
          {id:"005",content:"周杰伦 夜的第七章"},
          {id:"006",content:"周杰伦 说好不哭"},
          {id:"007",content:"周杰伦 等你下课"},
          {id:"008",content:"周杰伦 我是如此相信"}
        ]
        this.setData({searchList:result})
      },
      removeKeyword(){
        this.setData({keyword:'',searchList:[]})
      },
      deleteHistory(){
        this.setData({historyList:[]});
        wx.removeStorageSync('historyList');
      }
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78

    pages/login/login.json

    {
      "navigationBarTitleText": "登录"
    }
    
    • 1
    • 2
    • 3

    pages/login/login.wxml

    <view class="login-container">
      <view class="list">
        <view class="item">
          <input type="text" data-type="username" bindinput="handleInput" placeholder="用户名" />
        view>
        <view class="item">
          <input type="text" password data-type="password" bindinput="handleInput" placeholder="密码" />
        view>
      view>
      <button size="mini" bindtap="login">登录button>
    view>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    pages/login/login.wxss

    .login-container{
      padding: 20rpx;
    }
    .list{
      margin: 20rpx auto;
    }
    .item{
      margin: 12rpx 0;
      padding: 0 20rpx;
      border: 1px solid #ddd;
      border-radius: 6rpx;
    }
    .item input{
      width: 100%;
      height: 60rpx;
      font-size: 28rpx;
    }
    button{
      font-weight: normal;
      font-size: 28rpx!important;
      color: #fff;
      background: #0149af;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    pages/login/login.js

    Page({
      data:{
        username:"",
        password:""
      },
      handleInput(e){
        const type = e.target.dataset.type;
        this.setData({
          [type]:e.detail.value
        })
      },
      login(){
        const {username,password} = this.data;
        if(!username){
          wx.showToast({
            title: '请输入您的用户名',
            icon:'error'
          })
          return;
        }
        if(!password){
          wx.showToast({
            title: '请输入您的密码',
            icon:'error'
          })
          return;
        }
        this.sendLoginRequest();
        wx.showToast({
          title: '登录成功',
          success:() => {
            setTimeout(() => {
              wx.reLaunch({
                url: '/music-package/pages/index/index',
              })
            },300)
          }
        })
      },
      sendLoginRequest(){
        const userInfo = {
          avatarUrl:"/static/images/avatar.png",
          nickname:"Duck先生"
        }
        wx.setStorageSync("userInfo",JSON.stringify(userInfo));
      }
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    分包

    music-package/pages/index/index.json

    {
      "usingComponents": {},
      "navigationBarTitleText": "音乐",
      "navigationBarBackgroundColor": "#624d2e",
      "navigationBarTextStyle": "white"
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    music-package/pages/index/index.wxml

    <view class="index-container">
      <view class="header">
        <image src="/static/images/icon-play-square.png">image>
        <text>播放全部text>
        <text>{{musicList.length}}text>
      view>
      <scroll-view class="music-list" enable-flex scroll-y >
        <view class="music-item" wx:for="{{musicList}}" wx:key="id" bindtap="handleTap" data-musicindex="{{index}}" data-musicitem="{{item}}">
          <view class="music-index">{{index+1}}view>
          <image class="music-image" src="{{item.picUrl}}">image>
          <view class="music-info">
            <view class="musci-name">{{item.name}}view>
            <view class="music-author">{{item.author}}view>
          view>
          <image class="btn-more" src="/static/images/icon-more.png">image>
        view>
      scroll-view>
    view>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    music-package/pages/index/index.wxss

    .index-container{
      padding: 20rpx;
      background:#624d2e;
    }
    .header{
      display: flex;
      align-items: center;
    }
    .header image{
      width: 28rpx;
      height: 28rpx;
      margin-right: 20rpx;
    }
    .header text{
      color: #fff;
      height: 28rpx;
      line-height: 28rpx;
      margin-right: 20rpx;
      font-size: 28rpx;
    }
    .music-list{
      margin-top: 20rpx;
      color: #fff;
      height: calc(100vh - 88rpx);
    }
    .music-item{
      display: flex;
      align-items: center;
      height: 100rpx;
      margin: 20rpx 0;
      position: relative;
    }
    .music-item .music-index{
      width: 50rpx;
      font-size: 28rpx;
      color: #aaa;
    }
    .music-item .music-image{
      width: 80rpx;
      height: 80rpx;
      border-radius: 6rpx;
      margin-right: 20rpx;
    }
    .music-item .music-info{
      display: flex;
      flex-direction: column;
    }
    .music-item .music-info .music-author{
      font-size: 24rpx;
      color: #aaa;
      margin-top: 10rpx;
    }
    .music-item .btn-more{
      width: 36rpx;
      height: 36rpx;
      position: absolute;
      right: 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58

    music-package/pages/index/index.js

    import PubSub from "pubsub-js";
    const host = "http://localhost:3000";
    
    Page({
      data:{
        musicList:[], //歌曲列表
        musicindex:0  //进入某首歌曲后,记录该歌曲在列表中的索引
      },
      onLoad(){
        this.getDataFromServer();
    
        //接收消息,接收来自页面pages/music/music的消息,根据“上一首”or“下一首”,确定当前应该显示哪首歌曲
        PubSub.subscribe("switchsong",(msgName,type) => {
          // console.log(msgName,type);
          let {musicindex,musicList} = this.data;
          if(type === "prev"){
            if(musicindex===0) {
              musicindex = musicList.length-1;
            }else{
              musicindex--;
            }
          }else if(type === "next"){
            if(musicindex === musicList.length-1){
              musicindex = 0;
            }else{
              musicindex++;
            }
          }
          this.setData({musicindex});
          const music = musicList[musicindex];
          //发送消息,告诉页面pages/music/music,告诉它切换完成后的歌曲的详细消息。
          PubSub.publish("refreshmusic",music);
        })
      },
      getDataFromServer(){
        const result = [
          {id:"001",name:"滂沱大雨里","author":"李若溪","picUrl":host+"/images/滂沱大雨里.jpg","url":host+"/audios/滂沱大雨里.mp3",duration:161000},
          {id:"002",name:"Last Dance","author":"伍佰","picUrl":host+"/images/Last Dance.jpg","url":host+"/audios/Last Dance.mp3",duration:271000},
          {id:"003",name:"国王与乞丐","author":"华晨宇","picUrl":host+"/images/国王与乞丐.jpg","url":host+"/audios/国王与乞丐.mp3",duration:178000},
          {id:"004",name:"奇洛李维斯回信","author":"薛凯琪","picUrl":host+"/images/奇洛李维斯回信.jpg","url":host+"/audios/奇洛李维斯回信.mp3",duration:243000},
          {id:"005",name:"红日","author":"李克勤","picUrl":host+"/images/红日.jpg","url":host+"/audios/红日.mp3",duration:291000},
          {id:"006",name:"快乐崇拜","author":"潘玮柏 张韶涵","picUrl":host+"/images/快乐崇拜.jpg","url":host+"/audios/快乐崇拜.mp3",duration:329000},
          {id:"007",name:"门没锁","author":"黄品冠","picUrl":host+"/images/门没锁.jpg","url":host+"/audios/门没锁.mp3",duration:233000},
          {id:"008",name:"就是爱你","author":"陶喆","picUrl":host+"/images/就是爱你.jpg","url":host+"/audios/就是爱你.mp3",duration:340000},
          {id:"009",name:"快乐的小青蛙","author":"刘士鸣","picUrl":host+"/images/快乐的小青蛙.jpg","url":host+"/audios/快乐的小青蛙.mp3",duration:130000},
          {id:"0010",name:"友情岁月","author":"陈小春 郑伊健 谢天华 林晓峰 钱嘉乐","picUrl":host+"/images/友情岁月.jpg","url":host+"/audios/友情岁月.mp3",duration:209000},
          {id:"0011",name:"温柔","author":"五月天","picUrl":host+"/images/温柔.jpg","url":host+"/audios/温柔.mp3",duration:269000}
        ];
        this.setData({musicList:result});
      },
      handleTap(event){
        const {musicitem,musicindex} = event.currentTarget.dataset;
        this.setData({musicindex})
        wx.navigateTo({
          url: '/music-package/pages/music/music?musicitem='+JSON.stringify(musicitem),
        })
      }
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58

    music-package/pages/music/music.json

    {
      "usingComponents": {},
      "navigationBarBackgroundColor": "#2f434e",
      "navigationBarTitleText": "音乐详情",
      "navigationBarTextStyle": "white"
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    music-package/pages/music/music.wxml

    <view class="music-container">
      <view class="music-name">{{music.name}}view>
      <view class="music-author">{{music.author}}view>
      <image class="arm {{isPlay&&'arm-reset'}}" src="/static/images/arm.png">image>
      <view class="disc-container {{isPlay&&'disc-animate'}}">
        <image class="disc" src="/static/images/disc.png">image>
        <image class="music-image" src="{{music.picUrl}}">image>
      view>
    
      <view class="progress-container">
        <text class="current-time">{{fmtCurrentTime}}text>
        <view class="bar-box">
          <view class="current-progress" style="width:{{currentWidth}}rpx">
            <view class="progress-circle">view>
          view>
        view>
        <text class="duration">{{fmtDuration}}text>
      view>
    
      <view class="player">
        <view class="btns">
          <image class="loop-btn" src="/static/images/loop.png">image>
          <image class="prev-btn" src="/static/images/prev.png" id="prev" bindtap="handleSwitch">image>
          <image class="play-btn" src="{{isPlay?'/static/images/stop.png':'/static/images/play.png'}}" bindtap="handlePlay">image>
          <image class="next-btn" src="/static/images/next.png" id="next" bindtap="handleSwitch">image>
          <image class="list-btn" src="/static/images/list.png">image>
        view>
      view>
    view>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    music-package/pages/music/music.wxss

    .music-container{
      height: 100%;
      display: flex;
      flex-direction: column;
      align-items: center;
      background: #2f434e;
      position: relative;
    }
    .music-container .music-name{
      margin: 10rpx 0;
      color: #fff;
      font-size: 36rpx;
    }
    .music-container .music-author{
      color: #bbb;
      font-size: 28rpx;
      margin: 6rpx 0;
    }
    .music-container .arm{
      width:204rpx;
      height: 358rpx;
      position: relative;
      left: 72rpx;
      z-index: 99;
      transform: rotate(-15deg);
      transform-origin: 30rpx 30rpx;
      transition: transform .7s linear;
    }
    .disc-container{
      position: relative;
      top: -128rpx;
      width: 490rpx;
      height: 490rpx;
    }
    .disc-container .disc{
      width: 100%;
      height: 100%;
    }
    .disc-container .music-image{
      width: 270rpx;
      height: 270rpx;
      border-radius: 100%;
      position: absolute;
      left: 0;right: 0;top: 0;bottom: 0;
      margin: auto;
    }
    .music-container .arm-reset{
      transform: rotate(0deg);
    }
    .disc-animate{
      animation: rotate 2.5s 1s linear infinite;
    }
    @keyframes rotate{
      from{
        transform: rotate(0deg);
      }
      to{
        transform: rotate(360deg);
      }
    }
    .player{
      width: 100%;
      position: absolute;
      bottom: 60rpx;
    }
    .btns{
      display: flex;
      align-items: center;
      justify-content: space-evenly;
    }
    .btns image{
      width: 36rpx;
      height: 36rpx;
    }
    .btns .play-btn,.btns .stop-btn{
      width: 90rpx;
      height: 90rpx;
    }
    .progress-container {
      width: 100%;
      display: flex;
      justify-content: space-around;
      align-items: center;
    }
    .progress-container .bar-box{
      width: 456rpx;
      height: 4rpx;
      line-height: 4rpx;
      background: #888;
    }
    .bar-box .current-progress{
      position: relative;
      height: 4rpx;
      line-height: 4rpx;
      background: #fff;
    }
    .progress-circle{
      width: 16rpx;
      height: 16rpx;
      border-radius: 50%;
      background: #fff;
      position: absolute;
      right: -16rpx;
      top: -6rpx;
    }
    .progress-container text{
      color: #ccc;
      font-size: 22rpx;
      margin: 0 40rpx;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110

    music-package/pages/music/music.js

    import PubSub from "pubsub-js";
    import moment from 'moment';
    const appInstance = getApp();
    
    Page({
      data:{
        isPlay:false,
        music:{},
        fmtDuration:"00:00",
        fmtCurrentTime:"00:00",
        currentWidth:0
      },
      onLoad(options){
        const music = JSON.parse(options.musicitem);
        let fmtDuration = moment(music.duration).format("mm:ss");
        this.setData({music,fmtDuration})
    
        const {isPlayGlobal,musicIdGlobal} = appInstance.globalData;
        const {id} = this.data.music;
        if(isPlayGlobal && musicIdGlobal === id) {
          this.setData({isPlay:true});
        }
      
        this.bam = wx.getBackgroundAudioManager();
        this.bam.onPlay(() => {
          this.setData({isPlay:true})
          appInstance.globalData.isPlayGlobal = true;
          appInstance.globalData.musicIdGlobal = this.data.music.id;
        })
        this.bam.onPause(() => {
          this.setData({isPlay:false})
          appInstance.globalData.isPlayGlobal = false;
        })
        this.bam.onStop(() => {
          this.setData({isPlay:false})
          appInstance.globalData.isPlayGlobal = false;
        })
        this.bam.onEnded(() => {
          // this.setData({isPlay:false});
          // appInstance.globalData.isPlayGlobal = false;
    
          // 音乐自然播放结束,则切换至下一首
          PubSub.publish("switchsong","next");
          //接收消息,接收来自pages/index/index的消息,显示切换后的歌曲详情
          const eventId = PubSub.subscribe("refreshmusic",(msgName,music) => {
            PubSub.unsubscribe(eventId);
            this.setData({music})
            this.musicControl();
    
            const fmtDuration = moment(this.data.music.duration).format("mm:ss");
            this.setData({fmtDuration,fmtCurrentTime:"00:00"})
          })  
        })
        this.bam.onTimeUpdate(() => {
          const currentWidth = Math.floor(456 * this.bam.currentTime / this.bam.duration);
          const fmtCurrentTime = moment(this.bam.currentTime * 1000).format("mm:ss");
          this.setData({currentWidth,fmtCurrentTime});
        })
      },
      handlePlay(){
        const isPlay = !this.data.isPlay;
        this.setData({isPlay});
        this.musicControl();
      },
      musicControl(){
        const {isPlay} = this.data;
        if(isPlay){
          this.bam.src = this.data.music.url;
          this.bam.title = this.data.music.name;
        }else{
          this.bam.pause();
        }
      },
      handleSwitch(event){
        const type = event.target.id;
        //发送消息,告诉pages/index/index:切上一首还是下一首。prev代表切上一首,next代表切下一首。
        PubSub.publish("switchsong",type);
    
        //接收消息,接收来自pages/index/index的消息,显示切换后的歌曲详情
        const eventId = PubSub.subscribe("refreshmusic",(msgName,music) => {
          PubSub.unsubscribe(eventId);
          this.setData({music})
          this.musicControl();
        })
      }
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86

    相关链接

    分包加载

  • 相关阅读:
    苹果 MacBook如何取消开盖自动开机功能?
    Unity碰撞检测(3D和2D)
    解决Adobe Premiere Pro CC 2018打开无反应,并出现.crash的文件问题
    【多线程案例】Java实现简单定时器(Timer)
    弘辽科技:拼多多改销量会降权吗?什么情况下会降权?
    安全协议之-TLS握手过程详解
    flink对状态ttl进行单元测试
    即时通讯或者推送消息的守护进程?开启几个进程?
    豫见大势,豫见智慧——第九届博博会侧记
    U-Net: Convolutional Networks for Biomedical Image Segmentation
  • 原文地址:https://blog.csdn.net/qzw752890913/article/details/126031285