? ? ? ? 这个小程序是模仿网易云音乐,实现其一些功能的。
? ? ? ? 本文是为了记录做这个项目的学习过程。
????????主页:
? ? ? ? ? ? ? ? 1.页面顶端轮播图区域
? ? ? ? ? ? ? ? ? ? ? ? 直接用了swiper插件。内容区使用了列表渲染,引入了图片。
<!-- 轮播图区域-->
<swiper class="banners" indicator-dots="true" indicator-color="ivory" indicator-active-color="#EC4141" autoplay>
<swiper-item wx:for="{{bannerList}}" wx:key="bannerId">
<!--列表渲染-->
<image src="{{item.pic}}"></image>
</swiper-item>
</swiper>
? ? ? ? ? ? ? ? 2.五个图标导航区域,这里使用了iconfont。
<!-- 五个图标导航区域 -->
<view class="navContainer">
<view class="navItem">
<text class="iconfont icon-meirituijian"></text>
<text>每日推荐</text>
</view>
<view class="navItem">
<text class="iconfont icon-gedan"></text>
<text>歌单</text>
</view>
<view class="navItem">
<text class="iconfont icon-paihangbang"></text>
<text>排行榜</text>
</view>
<view class="navItem">
<text class="iconfont icon-diantai"></text>
<text>电台</text>
</view>
<view class="navItem">
<text class="iconfont icon-zhibobofangshexiangjitianxianxianxing"></text>
<text>直播</text>
</view>
?????????????????3.推荐歌曲区域
<!-- 推荐歌曲区域 -->
<view class="recommendContainer">
<!-- 头部区域 -->
<navHeader title="推荐歌曲" nav="为你精心推荐"></navHeader>
<!-- 内容区 -->
<scroll-view class="recommendScroll" enable-flex scroll-x>
<view class="scrollItem" wx:for="{{recommendList}}" wx:key="id">
<image src="{{item.picUrl}}"></image>
<text>{{item.name}}</text>
</view>
</scroll-view>
</view>
? ? ? ? ? ? ? ? ? ? ? ? 这里头部区域自定义了一个组件navHeader用于提高代码的复用性,内容很简单,写完之后在index.json里写入以下代码?,向页面中引入这个组件。
{
"usingComponents": {
"navHeader":"/components/navHeader/navHeader"
}
}
? ? ? ? ? ? ? ? ? ? ? ? 内容区域也是使用了列表渲染。
? ? ? ? ? ? ? ? 4.排行榜区域
<!-- 排行榜区域 -->
<view class="topList">
<!-- 头部区域 -->
<navHeader title="排行榜" nav="热歌风向标"></navHeader>
<!-- 内容区域 -->
<swiper class="topListSwiper" circular next-margin="50rpx" previous-margin="50rpx">
<swiper-item wx:for="{{topList}}" wx:key="name">
<view class="swiperItem">
<view class="title">{{item.name}}</view>
<view class="musicItem" wx:for="{{item.tracks}}" wx:key="id" wx:for-item="musicItem">
<image src="{{musicItem.al.picUrl}}"></image>
<text class="count">{{index+1}}</text>
<text class="musicName">{{musicItem.name}}</text>
</view>
</view>
</swiper-item>
</swiper>
</view>
? ? ? ? ? ? ? ? ? ? ? ?头部区域使用了刚才的navHeader组件。
????????????????????????内容区域也是使用了列表渲染。添加了next-margin 和 previous-margin用来实现当显示在当前的排行榜时可以看见上一个和下一个排行榜的一部分。
? ? ? ? 样式的设计就不展示啦。下面是初步的样子。
? ? ? ? ? ? ? ? ? ? ??
? ? ? ? 底部导航:
? ? ? ? ? ? ? ? ? 使用了tabBar来制作底部导航
? ? ? ? ? ? ? ? ? ? ?在app.json中添加了以下代码
"tabBar": {
"color": "#333",
"selectedColor": "#d43c33",
"backgroundColor": "#fff",
"list": [
{
"pagePath": "pages/index/index",
"text": "主页",
"iconPath": "/static/images/tabs/tab-home.png",
"selectedIconPath": "/static/images/tabs/tab-home-current.png"
},
{
"pagePath": "pages/vedio/vedio",
"text": "视频",
"iconPath": "/static/images/tabs/select.png",
"selectedIconPath": "/static/images/tabs/selected.png"
},
{
"pagePath": "pages/personal/personal",
"text": "个人中心",
"iconPath": "/static/images/tabs/tab-my.png",
"selectedIconPath": "/static/images/tabs/tab-my-current.png"
}
]
}
? ? ? ? ? ? ? ? 效果如下:
? ? ? ? ?个人中心页:
? ? ? ? ? ? ? ? ? ? ? ? 布局和样式略过。
? ? ? ? ? ? ? ? ? ? ? ? 个人中心页有一个下滑的交互:
? ? ? ? ? ? ? ? ? ? ? ? 首先在下半部分的view标签里添加了如下三个属性:
bindtouchstart="handleTouchStart"
bindtouchmove="handleTouchMove"
bindtouchend="handleTouchEnd"
ransistyle="transform: {{coverTransform}}; transition:{{coverTtion}}"
? ? ? ? ? ? ? ? ? ? ? ? 在页面的js文件的data中写入了以下代码
coverTransform:'translateY(0)',
coverTransition:''
? ? ? ? ? ? ? ? ? ? ? ? ?用于设置动画的效果
? ? ? ? ? ? ? ? ? ? ? ? 接下来就是三个方法
handleTouchStart(event) {
// 获取手指的起始坐标
startY=event.touches[0].clientY;
},
handleTouchMove(event) {
moveY=event.touches[0].clientY;
moveDistance=moveY-startY;
// 判断手指移动的距离,控制在一个范围内
if(moveDistance<=0){
return;
}else if(moveDistance>=80){
moveDistance=80;
}
// 动态更新coverTransform的状态值
this.setData({
coverTransform:`translateY(${moveDistance}rpx)`
})
},
handleTouchEnd() {
// 手指松开后回到原位
this.setData({
coverTransform:`translateY(0rpx)`,
coverTransition:`transform 1s linear`
})
},
????????????????????????(这里出了个问题,在第二次以及之后滑动时,出现了还原时的缓慢的动画效果,我们不想要这样的效果,所以在handleTouchStart的第一行添加了如下代码,让coverTransition重置)
this.setData({
coverTransition:``,
})
? ? ? ? ? ? ? ? 这样个人中心页的交互效果就完成了。
? ? ? ? 登陆页面:
? ? ? ? ? ? ? ? 登录页面主要实现的是:
? ? ? ? ? ? ? ? ? ? ? ? 1.收集表单项数据
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (1)给两个input标签绑定回调(bandtap="handleInput")
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (2)在data里初始化手机号和密码
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (3)表单项内容发生改变的回调函数:
handleInput(events){
let type=events.currentTarget.id;//取值: phone||password
this.setData({
[type]:events.detail.value
});
},
? ? ? ? ? ? ? ? ? ? ? ? 2.前端验证
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ?(1)给登录按钮添加一个回调函数login
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (2)先收集表单的数据,开始验证:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? - 如果手机号为空,提示用户(这里用到了wx.showToast来提示用户)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? - 如果手机号格式错误,提示用户
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? - 如果密码为空,提示用户
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? - 验证通过,提示用户
// 登陆的回调
login() {
// 1.收集表单项数据
let { phone, password } = this.data;
// 2.前端验证
/*
手机号验证:
1.内容为空
2.手机号格式不正确
3.手机号格式正确,验证通过
*/
//定义正则表达式
let phoneReg = /^1(3|4|5|6|7|8|9)\d{9}$/;
if (!phone) {
// 提示用户
wx.showToast({
title: '手机号不能为空',
icon: 'error'
})
return;
}
if (!phoneReg.test(phone)) {
wx.showToast({
title: '手机号格式错误',
icon: 'error'
})
return;
}
if (!password) {
wx.showToast({
title: '密码不能为空',
icon: 'error'
})
return;
}
wx.showToast({
title: '前端验证通过',
})
}
? ? ?
? ? ? ? ? ? ? ? ? ? ? ? 3.后端验证
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 这里一开始出了错误,之后发现是login前面没有加async,加上之后就好了。
// 后端验证
let result=await request('/login/cellphone',{phone,password})
if (result.code===200) {
wx.showToast({
title:'登录成功'
})
}else if(result.code===400){
wx.showToast({
title:'手机号错误',
icon:'error'
})
}else if(result.code===502){
wx.showToast({
title:'密码错误',
icon:'error'
})
}else{
wx.showToast({
title:'登陆失败,请重新登录',
icon:'error'
})
}
? ? ? ? ? ?个人中心与登陆页面的交互,本地存储
? ? ? ? ? ? ? ? ? ? ? ? 首先点击头像处会切换到登陆页面,在登陆成功时,将用户的信息存储到本地,然后跳转到个人中心页面,这里的跳转得使用reLaunch,不然personal页面只会显示一开始加载的未登陆的页面,使用reLaunch会关闭所有页面(包括一开始打开的未登陆的页面),跳转至接受了数据的页面,这样用户的头像以及昵称就可以呈现了。
? ? ? ? ? ? 用户播放记录展示
? ? ? ? ? ? ? ? ? ? ? ? 首先在读取用户信息时,获取到用户的播放记录,这里写了一个功能函数来实现其功能
//获取用户播放记录的功能函数
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
});
}
? ? ? ? ? ? ? ? ? ? ? ? 然后再wxml文件中的对应标签加上wx:for和wx:key以及其内容相关代码即可。
? ? ? ? ? ? ? ? 下面是个人中心页和登录页:
?
?
? ? ? ? ?视频页
? ? ? ? ? ? ? ? ? ? ? ? 导航条
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 这里涉及的主要是获取导航数据和切换导航条时,文字的下方出现横线,与前面的大体一样,只是出现了一个问题,在判断navId是否与Item.id相等时,一开始用了===,这判断的是值和类型,而navId是string类型,item.id是number类型,不相等,后来改为==即可。
<!-- 导航区域 -->
<scroll-view scroll-x class="navScroll" enable-flex>
<view class="navItem" wx:for="{{videoGroupList}}" wx:key="id">
<view class="navContent {{navId==item.id ? 'active':''}}" bindtap="changeNav" id="{{item.id}}">
{{item.name}}
</view>
</view>
</scroll-view>
onLoad: function (options) {
// 获取导航数据
this.getVideoGroupListData();
},
//获取导航数据
async getVideoGroupListData(){
let videoGroupListData= await request('/video/group/list');
this.setData({
videoGroupList:videoGroupListData.data.slice(0,14),
navId:videoGroupListData.data[0].id
});
},
//点击切换导航的回调
changeNav(event){
let navId=event.currentTarget.id;
this.setData({
navId:navId
});
},
? ? ? ? ? ? ? ? ? ? ? ? 接着是通过cookie获取视频数据。
? ? ? ? ? ? ? ? ? ? ? ? 然后写入视频页即可。
<!-- 视频列表区域 -->
<scroll-view scroll-y class="videoScroll">
<view class="videoItem" wx:for="{{videoList}}" wx:key="id">
<video src="{{item.data.urlInfo.url}}"></video>
</view>
</scroll-view>
? ? ? ? ? ? ? ? ? ? ? ? 接着是点击切换视频的实现
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 只需在切换导航的回调中动态获取当前导航对应的视频数据即可,这里为了提高用户体验,还使用了wx.showLoading和wx.hideLoading来显示和关闭加载提示框,并且将videoList设置为空,为了使切换时上一个视频数据不显示。
? ? ? ? ? ? ? ? 总结:这个项目只是大致学习到这里,了解了微信小程序开发的相关知识,受益匪浅。
|