最近,工作没有那么忙,终于有时间学习微信小程序的开发了。学习的过程还是比较顺利的,入门相对简单,即便没有多少前端开发经验的人也能很快上手。
现在微信小程序的资料还比较少,查看官方文档是很好的方式。微信小程序的官方文档还是很容易阅读的,并没有什么阅读障碍。在一个悠闲的午后,泡一杯清茶,开始了我的小程序之旅。花了一下午的时间把官方文档刷了一遍。接着,下载官方提供的 Demo,在微信开发者工具运行,查看每个组件的效果,使用方式,调试着看界面布局,学习API接口,顺便将弹性盒子模式复习了下。
在项目中学习才是最有效的学习方式。于是,我开始思考要做些什么项目。小程序适合开发低频使用,用完即走,业务不能太复杂的应用。工具类的应用首先被考虑,其次希望能把关注点放在微信小程序的开发,最好能有现成的后台接口。确认了方向之后,最终决定仿豆瓣电影的功能,开发一款微信小程序版的豆瓣电影。
准备工作:
数据来源:豆瓣电影API:https://developers.douban.com/wiki/?title=movie_v2
开发工具:微信开发者工具 0.14.140900
功能:
- 电影榜单列表
- 电影搜索
- 电影条目信息
- 影人条目信息
预览:
电影首页:显示“影院热映”,“即将上映”以及“精选榜单”,测试之后发现精选榜单只有Top250的 API 接口能正常使用,于是,豆瓣Top250、口碑榜、新片榜、票房榜使用同一个 API 接口,只是请求的数据参数不一样。由于微信小程序的 request 的最大并发数是 5,所以用户进入程序时只加载“影院热映”、“即将上映”的数据,界面滑动之后再加载榜单数据。
/** 滑动屏幕 */
handleTouchMove: function (event) {
var offsetTop = event.target.offsetTop;
console.log("handleTouchMove offsetTop: " + offsetTop);
if (offsetTop > 10 && !this.data.acquiredSelected) {
this.getSelectedListData();
}
},
“更多”右侧的箭头这里并没有使用图标,而是使用样式来实现的。微信小程序对 app 的体积有限制,超过1M就不能上传。其实,不只是微信小程序,网页也一样。能用样式实现的就不用字体图标;能用字体图标实现的就不用 sprite 图;能用 sprite 图实现的就不用单一图标。样式 > 字体图标 > sprite 图 > 单一图标,不能说是绝对,但基本原则是这样。这样做的目的也是为了减少 app 的体积,减少 HTTP 的请求次数,减轻服务器的压力。
显示更多组件
<text class="session-header-more" bindtap="bindMore" data-type-id="intheaters">更多</text>
显示更多样式
.session-header-more {
font-size: 28rpx;
color: #32cd32;
font-weight: 500;
position: relative;
padding-right: 20rpx;}.session-header-more::after {
content: " ";
display: inline-block;
position: absolute;
top: 50%;
width: 12rpx;
height: 12rpx;
transform: matrix(0.71, 0.71, -0.71, 0.71, 0, 0);
margin-top: -8rpx;
border-width: 2rpx 2rpx 0px 0px;
border-color: #32cd32;
border-style: solid;}
微信小程序提供了模板功能,可以在模板中定义代码片段,在不同的地方调用。模板可以减少重复性代码,使代码结构更加清晰,增强代码的可读性,也方便以后维护代码。模板粒度的划分可以根据页面的复杂度跟组件被重用的频率。模板可以组合嵌套使用,大的功能组件可以利用模板划分成更小的功能组件。在电影首页中,“影院热映”,“即将上映”展示的是电影信息列表,可以将电影海报,电影名字,评分,想看人数作为一个大的“组件”抽象出来形成电影信息块模板组件。评分在首页电影,更多列表中多次被使用,可以抽象出评分模板组件。
评分模板
<!--pages/movie/movie-rating/movie-rating-template.wxml--><template name="rating-template">
<view class="rating">
<view class="rating-star allstar{{rating.average | 0}}"></view>
<text class="rating-average">{{rating.average}}</text>
</view></template>
电影信息块模板
<!--pages/movie/movie-grid/movie-grid-template.wxml--><import src="/pages/movie/movie-rating/movie-rating-template.wxml" /><template name="movie-grid-template">
<view class="movie-wrapper" bindtap="bindMovieDetail" data-id="{{id}}">
<view class="movie-content">
<image class="poster" src="{{images.medium}}"></image>
<text class="name">{{title}}</text>
<template is="rating-template" data="{{rating}}" />
<text class="wish-count">{{collect_count}}人想看</text>
</view>
</view></template>
评分用星星表示不同的分数,10分用5星表示,1分显示半星。这里使用 sprite 图,可以减少图片的数量。在组件样式中指定图片位置的偏移量,用于显示不同的图标。
评分样式
.rating {
display: flex;
flex-direction: row;
position: relative;
margin-top: 10rpx;
margin-bottom: 10rpx;}.rating-star {
content: "";
display: inline-block;
width: 55px;
height: 11px;
position: absolute;
background-repeat: no-repeat;
background-image: url(/images/icon/ic_rating_s.png);}.allstar10 {
background-position: 0px 0px;}.allstar9 {
background-position: 0px -11px;}.allstar8 {
background-position: 0px -22rpx;}.allstar7 {
background-position: 0px -33px;}.allstar6 {
background-position: 0px -44px;}.allstar5 {
background-position: 0px -55px;}.allstar4 {
background-position: 0px -66px;}.allstar3 {
background-position: 0px -77px;}.allstar2 {
background-position: 0px -88px;}.allstar1 {
background-position: 0px -99px;}.allstar0 {
background-position: 0px -110px;}.rating-average {
margin-left: 120rpx;
font-size: 20rpx;
line-height: 22rpx;
color: #878787;
height: 22rpx;}
电影列表:显示更多电影列表。这里使用标签页切换“正在热映”、“即将上映”。使用 wx:if 控制组件的显示与隐藏,电影列表可以抽象出电影信息模板。
<!--pages/movie/movie-list/movie-list.wxml--><import src="/pages/movie/movie-rating/movie-rating-template.wxml" /><template name="movie-list-template">
<view class="movie-wrapper" bindtap="bindMovieDetail" data-id="{{id}}">
<view class="movie-content">
<image class="poster" src="{{images.medium}}"></image>
<view class="movie-summary">
<text class="name">{{title}}</text>
<text class="directors">导演:{{directors}}</text>
<text class="casts">主演:{{casts}}</text>
<text class="genres">类型:{{genres}}</text>
<template is="rating-template" data="{{rating}}" />
<text class="wish-count" style="color:#9bdff9;">{{collectCount}}人想看</text>
</view>
<block wx:if="{{typeId == 'comingsoon'}}">
<text class="wish-btn" style="color:#de9703;border-color:#de9703" catchtap="handleWishtap">想看</text>
</block>
<block wx:if="{{typeId == 'intheaters'}}">
<text class="ticket-btn" style="color:#9bdff9;border-color:#9bdff9" catchtap="handleTickettap">购票</text>
</block>
</view>
</view></template>
精选榜单:显示电影榜单列表。这里要说的是列表序号。列表序号左右两边有横线,这里也是使用样式实现。前三个序号显示的颜色不一样。
列表序号组件
<text class="selected-index">{{index + 1}}</text>
列表序号样式
.selected-index::before {
content: "";
width: 80rpx;
border-bottom: 2rpx solid #e9e9e9;
box-sizing: border-box;
display: inline-block;
margin-right: 15rpx;
margin-bottom: 10rpx;}.selected-index::after {
content: "";
width: 80rpx;
border-bottom: 2rpx solid #e9e9e9;
box-sizing: border-box;
display: inline-block;
margin-left: 15rpx;
margin-bottom: 10rpx;}.selected-wrapper:first-child .selected-index {
color: #e14c63;}.selected-wrapper:nth-child(2) .selected-index {
color: #fca167;}.selected-wrapper:nth-child(3) .selected-index {
color: #f5c564;}
电影搜索:电影搜索没有太多要讲的。点击搜索区块跳到搜索界面,获取 input 的输入值,后台请求电影信息。当输入框有值时,显示 x,点击 x 之后可以清空输入框内容。
<icon class="search-icon" type="search" size="16"></icon><input class="search-content" placeholder-class="search-placeholder" bindinput="bindSearchInput" placeholder="搜索电影" value="{{searchValue}}" /><block wx:if="{{showDelete}}">
<text class="search-delete" bindtap="bindSearchDelete">x</text></block><text class="search-cancel" bindtap="bindSearchCancel">取消</text>
使用bindinput获取输入框的值
/** 搜索影视 */bindSearchInput: function (event) {
var value = event.detail.value;
var readyData = { showDelete: false };
if (value.length > 0) {
readyData = { showDelete: true };
this.handleSearchData(value);
}
this.setData(readyData);},/**清空输入框 */bindSearchDelete: function (event) {
var readyData = { searchValue: "", showDelete: false, result: {} };
this.setData(readyData);},
影片详情:可以查看影片信息,影人信息,给电影评分,但是评分 API 接口不支持,评分数据不能上传到服务器。影人信息的描述信息无法获取。这里要说一下评分,刚开始的想法是通过 sprite 图,手指在星星上滑动时可以显示不同的评分,但是,微信小程序是基于数据驱动的模式,没有 window、document 对象,不能使用 JavaScript 操作 DOM,无法获取组件的坐标值,这样就无法确定手指的坐标是在哪个星星上。于是,只好显示五个星星图标,通过获取 tap 事件来实现评分功能。评分描述则通过样式控制透明度实现显示与隐藏。
事件处理
bindMark: function (event) {
var id = event.target.dataset.id;
this.setData({ "mark": id });
},
<view class="rating">
<view class="rating-desc">
<text class="desc-text {{mark == 1 ? 'desc-text-show' : ''}}">很差</text>
<text class="desc-text {{mark == 2 ? 'desc-text-show' : ''}}">较差</text>
<text class="desc-text {{mark == 3 ? 'desc-text-show' : ''}}">还行</text>
<text class="desc-text {{mark == 4 ? 'desc-text-show' : ''}}">推荐</text>
<text class="desc-text {{mark == 5 ? 'desc-text-show' : ''}}">力荐</text>
</view>
<view class="rating-stars-wrapper" bindtap="bindMark">
<block wx:for="{{[1,2,3,4,5]}}" wx:for-item="id">
<image class="{{mark >= id ? 'star-HL' : 'star'}}" src="#" data-id="{{id}}"></image>
</block>
</view>
<!-- Don't use sprite, can't give a mark -->
<!--<image class="rating-star" bindtap="handleRating" src="#"></image>--></view>
评分组件样式
.desc-text {
width: 32px;
height: 22px;
font-size: 15px;
line-height: 22px;
text-align: center;
color: #9e9e9e;
opacity: 0;}.desc-text-show {
opacity: 1;}.star {
width: 32px;
height: 32px;
background: url("/images/icon/ic_rating_star.png") no-repeat;}.star-HL {
width: 32px;
height: 32px;
background: url("/images/icon/ic_rating_star_HL.png") no-repeat;}
这个项目还是比较简单的,前后花了5天的时间就完成了。所使用的接口是豆瓣电影的公开接口,限定40次请求/分钟。对于学习来说已经足够了。如果运行时出现400的错误,是请求过于频繁,稍等一会就好了。
最后,附上项目的Github地址:https://github.com/bruintong/wechat-webapp-douban-movie
作者:bruintong
来源:慕课网