11-云开发实战

8/16/2001

# 一,实现目标

实现目标:

# 二,创建项目并配置

# 1,创建项目

第一步,如下:

第二步:如下:

就可以创建出一个最基础的项目,如下:

默认pages下面有很多的页面,如下:

删除完后,如下:

创建一个首页面,如下:

# 3,指定环境ID

云开发和普通开发有一些地方不一样,先指定云环境,如下:

需要在小程序中指定环境ID,如下:

traceUser是true,表示记录哪些用户访问了我的小程序。

在项目根目录下,有一个project.config.json,这是项目的配置文件,如下:

# 4,配置基本结构

创建一个视频的页面,如下:

引入项目中用到的一些图片,如下:

配置窗口和tabbar,如下:

配置的参考如下(大家也可以直接copy):

 "window": {
    "backgroundTextStyle": "dark",
    "navigationBarBackgroundColor": "#fff",
    "navigationBarTitleText": "码路音乐",
    "navigationBarTextStyle": "black"
  },
  "tabBar": {
    "list": [{
        "pagePath": "pages/playList/playList",
        "iconPath": "images/music.png",
        "selectedIconPath": "images/music-active.png",
        "text": "音乐"
      },
      {
        "pagePath": "pages/vedio/vedio",
        "iconPath": "images/vedio.png",
        "selectedIconPath": "images/vedio-active.png",
        "text": "视频"
      }
    ]
  },
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 三,轮播图与playList组件

# 1,轮播图实现

准备一些假数据,如下:

参考的假数据如下:

const swiperImgUrls = [{
        url: ' http://p1.music.126.net/Pu0l_nScj5dfOvMCaBWIag==/109951167653146162.jpg'
    },
    {
        url: 'https://p1.music.126.net/W_T-HqFp9i5o0k1o63uRuw==/109951167652829089.jpg',
    },
    {
        url: 'http://p1.music.126.net/ZPdgbRn9TsnZDMiCV2Eksw==/109951167652921307.jpg',
    },
    {
        url: 'https://p1.music.126.net/78xGCPQSXKtpq4BpTEYM6Q==/109951167653164348.jpg',
    },
    {
        url: 'http://p1.music.126.net/KVCbjQbmHEk8tJNgbazSLw==/109951167652805156.jpg'
    }
]

module.exports = swiperImgUrls;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

在页面对应的JS中导入数据,如下:

有了数据,就可以绘制轮播图了,如下:

轮播图参考结构如下:

<view class="container">
    <swiper autoplay circular indicator-dots="{{true}}" indicator-active-color="#d43c43">
        <block wx:for="{{swiperImgUrls}}" wx:key="index">
            <swiper-item>
                <image src="{{item.url}}"></image>
            </swiper-item>
        </block>
    </swiper>
</view>
1
2
3
4
5
6
7
8
9

书写对应的样式,如下:

轮播图参考样式如下:

swiper,
swiper image {
    width: 100%;
    height: 360rpx;
}
1
2
3
4
5

# 2,songs组件实现

创建songs组件,如下:

在playList组件中,使用songs组件,如下:

使用之,如下:

然后,开始写songs组件,如下:

songs组件参考结构如下(大家也直接copy):

<view class="songList-container" bind:tap="goToDetails">
    <image src="http://p1.music.126.net/vsK4sDZa2GkAgUpEuSGsSQ==/109951164423646257.jpg?param=140y140"></image>
    <text class="collect">100</text>
    <text class="description">名字</text>
</view>
1
2
3
4
5

书写对应的样式,如下:

songs组件参考样式如下(大家也直接copy):

.songList-container {
    width: 220rpx;
    position: relative;
    padding-bottom: 20rpx;
}

.songList-container image {
    width: 100%;
    height: 220rpx;
    border-radius: 6rpx;
}

.songList-container .collect {
    position: absolute;
    right: 10rpx;
    top: 4rpx;
    color: #fff;
    font-size: 24rpx;
    padding-left: 26rpx;
    text-shadow: 1px 0 0 rgba(0, 0, 0);
}

.songList-container .description {
    overflow: hidden;
    text-overflow: ellipsis;
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    font-size: 26rpx;
    padding: 2px 0 0 6px;
    line-height: 1.2;
}
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

准备一些假数据,如下:

songs对应的假数据如下:

const songs = [{
        _id: '08560c9e5d042a5c0174f1ca26f1d7b2',
        copywrier: '热门推荐',
        playCount: 1.4641238e6,
        highQuality: false,
        type: 0.0,
        canDislike: true,
        name: '天气转热了,适合听点凉爽的歌。',
        alg: 'cityLevel_unknow',
        createTime: {
            $date: '2021-06-14T23:14:36.746Z'
        },
        id: 2.780381322e9,
        picUrl: 'http://p1.music.126.net/vsK4sDZa2GkAgUpEuSGsSQ==/109951164423646257.jpg?param=140y140',
        trackCount: 53.0,
    },
    {
        _id: '08560c9e5d042a5c0174f1da7aa357aa',
        highQuality: false,
        copywriter: '热门推荐',
        canDislike: true,
        playCount: 622822.6,
        id: 2.740107647e9,
        name: '「时空潜行」囿于昼夜的空想主义者',
        type: 0.0,
        alg: 'cityLevel_unknow',
        createTime: {
            $date: '2020-06-14T23:14:36.955Z'
        },
        picUrl: 'http://p1.music.126.net/Jl-xdjCyuEXnI2FedkLLMQ==/109951167578347447.jpg',
        trackCount: 20.0,
    },
    {
        _id: '08560c9e5d042a5c0174f1de21c7e79e',
        id: 2.828842343e9,
        type: 0.0,
        name: '粤语情诗:与你听风声,观赏过夜星',
        picUrl: 'http://p1.music.126.net/2K9Zk1jAoSnW7GgfWfy0Kw==/109951167132241424.jpg',
        highQuality: false,
        alg: 'cityLevel_unknow',
        playCount: 1.785097e6,
        trackCount: 52.0,
        copywriter: '热门推荐',
        canDislike: true,
        createTime: {
            $date: '2021-06-14T23:14:36.982Z'
        },
    },
    {
        _id: '08560c9e5d042a5d0174f1e67d1bb16f',
        playCount: 7.719329e6,
        highQuality: false,
        trackCount: 950.0,
        alg: 'cityLevel_unknow',
        id: 9.17794768e8,
        type: 0.0,
        name: '翻唱简史:日本四百首',
        canDislike: true,
        createTime: {
            $date: '2022-06-14T23:14:37.037Z'
        },
        copywriter: '热门推荐',
        picUrl: 'https://p2.music.126.net/NczCuurE5eVvObUjssoGjQ==/109951163788653124.jpg',
    },
    {
        _id: '08560c9e5d042a5d0174f1ea32c4c288',
        type: 0.0,
        copywriter: '热门推荐',
        highQuality: false,
        createTime: {
            $date: '2022-06-14T23:14:37.097Z'
        },
        id: 2.201879658e9,
        alg: 'cityLevel_unknow',
        playCount: 1.06749088e8,
        name: '你的青春里有没有属于你的一首歌?',
        picUrl: 'http://p1.music.126.net/y1wtpP_a3SwbL3zaFS-NbA==/109951167565069927.jpg',
        canDislike: true,
        trackCount: 169.0,
    },
    {
        _id: '08560c9e5d0829820362a79f4b049d2d',
        alg: 'cityLevel_unknow',
        name: '「乐队的夏天」参赛歌曲合集丨EP04更新',
        highQuality: false,
        picUrl: 'http://p2.music.126.net/ev-M6wpxsxEPR_t0Lx8ujw==/109951167526276853.jpg',
        trackCount: 158.0,
        createTime: {
            $date: '2021-06-18T00:00:02.553Z'
        },
        copywriter: '热门推荐',
        playCount: 1.5742008e6,
        canDislike: true,
        id: 2.79477263e9,
        type: 0.0,
    },
]

module.exports = songs
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

在页面中,导入这些假数据,如下:

有多少个数据,就渲染出多少个song组件,如下:

实现playList 类,如下:

把数据传递给子组件,如下:

子组件接收之,如下:

使用之,把数据替换一下,如下:

发现了一个问题,看一个播放量,如下:

我们期望,如下:

所以我们需要对数据进行处理,此时使用一个数据监听器。

# 3,数据监听器

对应的文档:

​ https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/observer.html

在处理之前,再看一个数据长什么样,如下:

开始处理数据,如下:

定义一个新的状态,把处理好的数据赋值给它,如下:

在模板中使用之,如下:

# 四,请求音乐数据的云函数

# 1,创建云函数并安装依赖

每一个云函数就是一个小项目,小项目中默认需要安装一个依赖,如下:

把这个依赖安装一下,如下:

在云函数中,还需要向第三方服务器,发请求,获取音乐数据,所以我们需要安装axios,如下:

# 2,在云函数中请求接口获取数据,存储到集合

接口如下:

http://47.94.210.129:3002/personalized

在云函数中指定环境ID,如下:

在云函数中请求如下:

把代码上传部署,如下:

测试一把,测试分本地测试和云端测试,如下:

进行本地测试,如下:

当然也可以云端测试,一般情况下不用,如下:

创建一个集合,如下:

把数据存储到集合中,如下 :

上传并部署,本地测试之,如下:

查看数据库,如下:

数据为中有多个少个数据,可以点高级操作,如下:

# 3,数据去重

再看一下,我们的代码,如下:

再本地测试一把,相当于又发了一个ajax请求,如下:

看数据库,如下:

每一次得到30条数据,问:第1次得到的30条数据 和 第2次得到的30条数据一样吗?

答:不好说 如果后端数据没有更新,得到数据是一样的,如果后端更新了,得到的数据是不一样的,换句话说,今天调用接口得到数据和你明天调用接口得到的数据可能是不一样的。

也就说,数据可能是一样的,也可能是不一样的,如果是一样的,就意味着数据库中存储的数据有很多重复数据,所以我们就需要去重。

接下来,我们从数据库取数据,取出来后,需要去重。分析如下:

去重的逻辑,在vscode中写,如下:

每一个文档,都有一个唯一的ID,叫_id,每个音乐也都是有下个唯一的ID,我们也是根据ID来去重的,如下:

把去重逻辑代码copy到云函数中,如下:

测试一下,把集合全部清空,如下:

集合中没有任何元素,如下:

把代码上传并布署。

调用一次云函数,此时会存储30条数据到数据库,如下:

现在再次调用之,按去重前的逻辑,会存储60条数据,现在去重了,测试如下:

看数据库,如下:

现在第2次去调用时,由于所有的音乐和第1次存储到数据库中的音乐是一样,所以第2次也是得到30条数据。

# 4,突破云函数中获取数据条件的限制

在云函数中获取数据库中的数据是由条件限制的,如下:

文档如下:

https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-sdk-api/database/collection/Collection.get.html

我们需要突破这个限制,假如数据库中有500条数据,一次可以拿100条件,拿5次就OK了,如下:

测试OK。

# 5,利用定时触发器每天获取最新的数据

在云开发中,提供了触发器,文档,如下:

https://developers.weixin.qq.com/miniprogram/dev/wxcloud/guide/functions/triggers.html

创建一个触发器,如下:

把触发器,上传到云端,如下:

第二天,它会自动调用云函数,获取最新的数据,第三天同理....

# 五,小程序端获取音乐数据

# 1,创建云函数获取音乐的数据

直接创建,如下:

本地测试之,如下:

# 2,在小程序端调用云函数

在小程序端,获取数据,如下:

在调用之前,开启Loading,如下:

# 3,上拉加载更多与下拉刷新

先实现上拉加载更多,把上面调用云函数的代码封装成一个函数,如下:

在onLoad中调用之,如下:

实现上拉加载更多,当们往上拽时,会触发下面的函数,如下:

在这个函数中,也是调用getPlayList,如下:

是由于我们的getPlayList方法,有问题,修改之,如下:

到此就实现了上拉加载更多。

然后,我们实现下拉刷新,所谓的下拉刷新,就是重新调用云函数。要实现上拉刷新,需要在JSON文件,开启,如下:

也有一个钩子函数,对应下拉动作,如下:

下拉刷新,下拉时间有点,可以修改之,如下:

此时,下拉就很丝滑了。

# 六,云函数中的路由

# 1,云函数中的路由的使用

进入music云函数,安装默认依赖,如下:

云函数中也是有路由的,和koa中路由是一样的,如下:

使用之如下:

在小程序端,调用云函数时,需要加上url,如下:

测试,OK,如下:

# 七,歌曲详情开发

点击歌曲,需要到详情,详情如下:

# 1,歌曲详情组件开发

创建出详情组件,如下:

给首页面中的每一个小歌单绑定一个点击事件,如下:

实现对应的方法,如下:

跳转时,需要把歌曲的ID传递到详情页面,获取ID如下:

测试ID是可以获取的,传递,如下:

在详情页面中获取ID,如下:

在云端函数端定义根据ID获取音乐详情的数据,需要安装axios,如下:

定义路由,根据ID获取数据,如下:

在小程序端,调用云函数,如下:

在小程序端测试之,如下:

定义一个状态,如下:

给上面的两个状态赋值,如下:

查看之,如下:

有数据了,就需要渲染数据,so easy~

实现对应的结构,如下:

详情页面对应的结构如下(大家可以直接copy):

<view class="container">
    <image src="xx.png" class="coverImg"></image>
    <view class="detail">
        <view class="detail-name">音乐的名字</view>
        <view class="detail-description">音乐的描述</view>
    </view>
</view>
1
2
3
4
5
6
7

实现对应的样式,如下:

详情页对应的样式如下(大家可以直接copy):

.container {
    display: flex;
    width: 100%;
    height: 320rpx;
    padding: 20rpx;
    flex-direction: row;
    align-items: center;
    background-color: #666;
}

.detail {
    line-height: 60rpx;
    width: 400rpx;
    margin-top: 20rpx;
}

.coverImg {
    width: 280rpx;
    height: 280rpx;
    border-radius: 6rpx;
    margin-right: 24rpx;
}

.detail-name {
    color: #fff;
    font-size: 34rpx;
    font-weight: 400;
}

.detail-description {
    color: #fff;
    margin-top: 20rpx;
    font-size: 24rpx;
    line-height: 40rpx;
    overflow: hidden;
    text-overflow: ellipsis;
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 3;
}
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

在调用云函数时,也可以添加loading效果,如下:

# 2,实现songList组件

songList组件效果,如下:

定义一个组件,如下:

在详情页中,使用组件,如下:

使用之,如下:

把数据传递给组件,如下:

组件进行接收,如下:

查看之,如下:

可以数据数据渲染出来,如下:

songList组件对应的结构如下(大家直接copy):

<block>
    <view class="song">
        <view class="serial-number">1</view>
        <view class="song-detail">
            <view class="song-name">名字</view>
            <view class="song-singer">作者</view>
        </view>
    </view>
</block>
1
2
3
4
5
6
7
8
9

实现对应的样式,如下:

songList组件对应的样式如下(大家直接copy):

.song {
    display: flex;
    padding: 20rpx;
    align-items: cneter;
    font-family: "微软雅黑";
}

.serial-number {
    color: #666;
    font-size: 34rpx;
    width: 80rpx;
    line-height: 80rpx;
}

.song-name {
    font-size: 34rpx;
    color: #000;
}

.song-singer {
    margin-top: 5rpx;
    font-size: 26rpx;
    color: #666;
}

.checked view {
    color: #d34c34;
}
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

# 3,高亮效果

点谁让谁高亮,绑定点击事件并传参,传ID,如下:

实现上面的方法,如下:

测试之,如下:

实现高亮,如下:

# 4,点击某个音乐去 播放页面

不能只是高亮,还需要进行跳转,就跳转到音乐的播放页面了,至少需要把音乐的ID传递过来,因为在播放页面,需要根据ID来获取音乐数据,进行音乐播放,假如把音乐的ID传递给播放页,播放页,按理说,需要根据ID获取音乐数据,进行音乐操作。当然,这样也可以,但是音乐数据,在详情页中都有,我们没有必要在播放页再去请求数据,可以把数据存储在本地,在播放页,直接从本地获取。在详情页,数据如下:

把ID和索引,都传递给播放页,如下:

跳到播放器,如下:

把播放页,创建出来,如下:

测试一下,能否跳转,如下:

可以得到传递过来的数据,如下:

# 5,在详情页把数据存储到本地

为什么要存储到本地?

答:在播放页需要获取数据,进行音乐播放。在播放页就可以从本地获取数据。没有必须再发网络请求了。

存储数据如下:

查看如下:

# 八,播放器实现

# 1,播放器页面布局

在播放页面,可以得到传递过来的数据,如下:

有10个音乐数据,索引为0表示第1个数据,索引为1表示第2个数据.... 要得到第1个音乐数据,如下:

得到音乐的名字,可以放到小程序头部,如下:

在data中,还有播放页的背景图,定义一个状态,如下:

给状态赋值,如下:

在HTML中绘制这个背景图,如下:

把页面中其它的结构,直接copy了,如下:

#

播放器对应的结构如下(大家直接copy):

<!--pages/player/player.wxml-->
<view class="container" style="background: url(./xx.png) center/cover no-repeat;"></view>
<view class="mask"></view>

<view class="disc play">
    <image class="cover roration}}" src="xx.png" bind:tap="cutoverState"></image>
</view>

<view class="control-panel">
    <image src="../../images/previous.png"></image>
    <image class="play" src="../../images/play.png"></image>
    <image src="../../images/next.png"></image>
</view>
1
2
3
4
5
6
7
8
9
10
11
12
13

样式,我们也不写,因为之前,vue音乐播放器,我们写过了,如下:

播放器对应的样式如下(大家直接copy):

/* pages/player/player.wxss */
.container {
    position: absolute;
    left: 0;
    right: 0;
    top: 0;
    bottom: 0;
    filter: blur(40rpx);
    opacity: 0.5;
    z-index: -1;
}

.mask {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-color: #333;
    z-index: -2;
}

.disc::after {
    content: '';
    width: 192rpx;
    height: 274rpx;
    position: absolute;
    top: -150rpx;
    left: 266rpx;
    background: url("https://s3.music.126.net/m/s/img/needle.png?702cf6d95f29e2e594f53a3caab50e12") no-repeat center/contain;
    transform: rotate(-15deg);
    transform-origin: 24rpx 10rpx;
    transition: transform 1s ease;
}

.play.disc::after {
    transform: rotate(0deg);
}

.disc {
    width: 590rpx;
    height: 590rpx;
    background: url("https://s3.music.126.net/mobile-new/img/disc.png?d3bdd1080a72129346aa0b4b4964b75f=") no-repeat center/contain;
    position: absolute;
    left: 50%;
    transform: translate(-50%, 140rpx);
}

.cover {
    width: 368rpx;
    height: 368rpx;
    border-radius: 50%;
    position: absolute;
    left: 50%;
    top: 50%;
    margin-top: -184rpx;
    margin-left: -184rpx;
}

.roration {
    animation: roration 16s linear infinite;
}

.roration-stop {
    animation-play-state: paused;
}

@keyframes roration {
    from {
        transform: rotate(0deg);
    }

    to {
        transform: rotate(360deg);
    }
}

.control-panel {
    position: absolute;
    bottom: 8%;
    left: 50%;
    transform: translateX(-50%);
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
}

.control-panel image {
    width: 80rpx;
    height: 80rpx;
    margin-left: 40rpx;
}

.control-panel image.play {
    width: 100rpx;
    height: 100rpx;
}

.progress-bar {
    position: absolute;
    width: 90%;
    left: 50%;
    transform: translateX(-50%);
    bottom: 24%;
    color: #fff;
}
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

# 2,控制面板功能实现

定义一个云函数,这个云函数可以根据id获取音乐的mp3数据,如下:

在小程序端,调用云函数,如下:

控制台查看之,如下:

测试一下,这个mp3文件,如下:

在小程序中,有一个背景音乐管理器,对应的文档,如下:

https://developers.weixin.qq.com/miniprogram/dev/api/media/background-audio/BackgroundAudioManager.html

使用之,如下:

就上面一条代码,就可以实现音乐播放。

但是在控制台有一个后错,如下:

配置如下 :

配置完毕后,就没有错误了。

把data定义成全局变量,data中就保存音乐的信息,如下:

在另一个函数,就可以使用data中的数据,如下:

定义一个状态,表示是否处于播放模式,如下:

当播放音乐时,需要变成true,如下:

此时,就可以根据这个状态,修改HTML中的数据了,如下:

点击按钮,可以暂停音乐,绑定点击事件,如下:

实现对应的方法,如下:

当音乐播放时,需要让中间的转盘转起来,如下:

实现上一曲和下一曲,分别给按钮绑定点击事件,如下:

实现上面的方法,在方法,需要得到索引,还需要得到音乐列表数据,定义成全局变量,如下:

在其它函数,就可以使用之,如下:

测试之,如下:

当点击下一曲或上一曲,有一个bug,如下:

为什么?分析:

在获取音乐的mp3时,可以先把音乐停下来,如下:

再次测试之,完美。

# 3,总时长与播放时间实现

需求:

创建一个进度条组件,如下:

在播放页,引入进度条,如下:

在播放页,使用之,如下:

然后,实现进度条组件,如下:

进度条对应的结构如下(直接copy):

<view class="container">
    <text class="time">0</text>
    <view class="content">
        <movable-area class="movable-area">
            <movable-view class="movable-view" direction="horizontal" x="0" damping="1000"></movable-view>
        </movable-area>
        <progress stroke-width="4" backgroundColor="#969696" activeColor="#fff" percent="0"></progress>
    </view>
    <text class="time">0</text>
</view>
1
2
3
4
5
6
7
8
9
10

书写对应的样式,如下:

进度条对应的样式如下(直接copy):

.container {
    display: flex;
    align-items: center;
}

.content {
    flex: 1;
    position: relative;
}

.movable-area {
    width: 100%;
    height: 34rpx;
    position: absolute;
    bottom: -14rpx;
    left: 0;
}

.movable-view {
    width: 36rpx;
    height: 36rpx;
    position: absolute;
    background-color: #fefefe;
    border-radius: 50%;
}

.time {
    width: 64rpx;
    padding: 0 20rpx;
    font-size: 24rpx;
    font-weight: 400;
    line-height: 30rpx;
}
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

在进度条组件中,有一个钩子函数,如下:

在这里,可以得到组件相关的信息,参考文档如下:

代码如下:

结果如下:

上面得到的两个宽度,后面使用,先放一下。

我们要得到音乐相关的信息,需要引入BAM,如下:

我们需要得到音乐的总时长,如下:

下面的方式,获取总时长是不可取的,如下:

正确的处理办法,如下:

总时长是一个秒数,需要换算,把时间格式化,抽离成一个函数,如下:

测试是可以得到分钟和秒数,如下:

定义两个状态,如下:

给总时间赋值,如下:

在页面中就可以使用之,如下:

随着音乐的播放,开始时间也需要变化,需求:

音乐播放时,会调用一个钩子函数,如下:

得到音乐播放的当前时间,如下:

对时间也进行格式化更新startTime,如下:

效果如下:

现在需要对上面的那一片代码进行降频,如下:

# 4,进度条自动移动

再次看一下,进度条与滑块,如下:

定义两个状态,分别代表滑块和进度条的位置,如下:

再定义两个变量,代表滑块的宽度和滑块所在区域的宽度,如下:

之前我们已经得到的宽度,给上面的两个变量赋值,如下:

在音乐播放期间,就需要设置设置moveViewX和percent状态,在设置这两个位置,需要有总时长,把总时长定义成一个全局变量,如下:

在一开始播放音乐时,是可以得到总时长的,如下:

在播放音乐时,就可以得到总时长,如下:

有了总时长,就可以计算滑块的位置和进度条的位置 了,如下:

在HTML中,使用这两个状态,如下:

测试如下:

# 5,进度条拖拽

我们可以人为的移动滑块,目的是改变音乐播放,给滑块绑定change事件,如下:

实现chagne方法,如下:

定义两个全局变量,如下:

计算出这两个变量的值,如下:

当滑动结束,也对应的一个事件,绑定之,如下:

实现对应的方法,如下:

在拖拽滑块时,有一个回弹的效果,处理如下:

如果在拖拽时,赋值为true,拖拽结束时,赋值为false,如下:

在播放音乐时,判断一下flag,如下:

测试之,OK。

# 6,自动播放下一曲

需求:

当音乐播放完毕后,会触发一个钩子,如下:

给进度条绑定自定义事件,如下:

上面的next就是下一曲,之前是点击按钮调用next,现在播放完毕,调用next。

测试OK。

# 7,歌词获取与显示

创建歌词对应的组件,如下:

在播放页中,引入,如下:

使用之,如下:

定义一个状态,控制是否显示歌词,如下:

定义一个方法,改变状态,如下:

在HTML中,使用状态,如下:

在词组件中,接收之,如下:

使用之,如下:

现在只会显示转盘,如下:

给转盘和歌词分别绑定事件,如下:

测试之,如下:

定义一个云函数,可以根据歌词的ID,获取对应的歌词,如下:

云函数需要上传部署。

在播放页面中,获取歌词,就是去调用云函数,如下:

测试之,如下:

定义一个状态,来存储歌词,如下:

给状态赋值,如下:

查看状态,如下:

把歌词传递给歌词对应的组件,如下:

子进行接收之,如下:

如果有数据,就需要对歌词进行解析,如下:

定义一个状态,保存歌词,对歌词进行解析,如下:

解析歌词对应的方式,如下(自行分析):

 parselyric(template) {
     let container = template.split('\n');
     console.log(container);
     let list = [];
     container.forEach(row => {
         let res = /\[(\d{2,}):(\d{2,})\.(\d{1,3})\](.*)/.exec(row);
         if (res !== null) {
             const second = parseInt(res[1]) * 60 + parseInt(res[2]) + parseInt(res[3]) / 1000;
             list.push({
                 time: second,
                 lyric: res[4]
             })
         }
     });
     this.setData({
         list
     })
 }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

查看状态,如下:

有了歌词,要页面中就需要显示之,如下:

歌词对应的结构,如下(直接copy):

<scroll-view hidden="{{isShow}}" class="scroll-view" scroll-y scroll-with-animation="{{true}}" scroll-top="{{0}}">
    <view class="lyrics">
        <block wx:for="{{list}}" wx:key="index">
            <view class="lyric">{{item.lyric}}</view>
        </block>
    </view>
</scroll-view>
1
2
3
4
5
6
7

歌词对应的样式,如下:

歌词对应的样式,如下(直接copy):

.scroll-view {
    width: 100%;
    color: #ccc;
    font-size: 32rpx;
}

.lyrics {
    width: 100%;
    height: 560rpx;
    text-align: center;
    margin-top: 280rpx;
}

.lyric {
    height: 64rpx;
}

.ative {
    color: #fff;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 8,歌词歌曲联动

分析:

子触发自定义事件,如下:

父绑定自定义事件,如下:

当自定义事件发生时,调用timeUpdate方法,实现方法,如下:

得到歌词组件,调用歌词组件的方法,如下:

得到歌词组件,如下:

实现子组件的update方法,如下:

需要让对应的歌词高亮,如下:

在HTM中使用之,如下:

效果如下:

现在还有一个问题,在播放歌词时,需要让它自动滚动,看HTML,如下:

不要设备滚动的距离是不一样的,所以我们需要得到设备的信息,如下:

在HTML中,使用scrollTop,如下:

效果如下:

# 9,其它问题解决(自学)

我们在点击下一曲的时候,再点击返回,发现,选中的还是上一次的歌曲,如下:

为什么,原因是我们歌曲选中,是通过checkId属性来实现的,如下:

在全局定义一个musicId,并定义获取和改变的对应的方法,如下:

在播放页,对musicId赋值,如下:

在songList组件中,使用之,如下:

测试OK。

还有一个问题,在播放一个音乐时,如果如果返回 到列表中,再点击同一个音乐,到播放器,不是重新开始播放,而是接着上次播放的位置开始播放,定义一个状态,表示是否是同一个音乐,如下:

获取歌曲时,给isSame赋值,如下:

同理,获取音乐信息时,也判断是否是同一个音乐,如下:

测试OK。

但是还有一个问题,就是再次点进来时,总时间变成了0,如下:

解决:

解决OK

Last Updated: 12/25/2022, 10:02:14 PM