一,电影页面功能
功能分析,1.点击搜索关键字,搜索电影
2.点击更多按钮,跳转到更多电影页面
3.点击电影图片,跳转到电影详情页
逻辑分析,1.在movies页面分别发送三个请求,获取正在上映的,不久上映的,top250的数据,传递给电影列表movie-list子组件
2.从lin-ui映入搜索组件l-search-bar,
3.点击更多,跳转到更多页面
4.引入movie-list组件
pages-->movies->movies
movies.wxml
在movie-lilst设置组件外链样式类
<l-search-bar bind:lincancel="onSearchCancel" bind:linconfirm="onConfirm" l-class="ex-search-bar" placeholder="盗梦空间、你的名字"/> <view wx:if="{{!searchResult}}">
电影列表 <movie-list data-type="in_theaters" bind:tap="onGotoMore" movies="{{inTheaters}}" title="正在热映" f-class="movie-list" /> <movie-list data-type="coming_soon" bind:tap="onGotoMore" movies="{{comingSoon}}" title="即将上映" f-class="movie-list" /> <movie-list data-type="top250" bind:tap="onGotoMore" movies="{{top250}}" title="豆瓣Top250" f-class="movie-list" /> </view> <!-- 搜索的结构 ,默认遍历每项为item--> <view class="search-container" wx:else> <block wx:for="{{searchData}}" wx:key="index"> <movie class="movie" movie="{{item}}" /> </block> </view>
movies.wxss
/* pages/movies/movies.wxss */
//movie-list组件的外链样式 .movie-list{ margin-bottom: 30rpx; /* background-color: teal !important; */ } //组件外链样式 .ex-search-bar{ height: 90rpx !important; } .search-container{ display: flex; flex-direction: row;
//换行,开启flex,不会自动换行 flex-wrap: wrap; padding: 30rpx 28rpx; justify-content: space-between; } /* 解决两端对齐的bug 每个电影图像宽200rpx*/ .search-container::after{ content:''; 200rpx; } page{ background-color: #f2f2f2; /* background-color: teal; */ }
movies.js, 从app.js中引入全局变量app.gBaseUrl,
// pages/movies/movies.js const app = getApp() Page({ /** * 页面的初始数据 */ data: { // 正在上映数据 inTheaters:[], // 不就上映数据 comingSoon:[], // top250数据 top250:[], // 搜索状态,影响结构 searchResult:false, // 搜索后的数据 searchData:[] }, /** * 生命周期函数--监听页面加载 */ onLoad: function (options) { // 不可用async, await,默认get请求,类型in_theaters(正在上映) wx.request({ url: app.gBaseUrl + 'in_theaters', // 请求参数,第0条开始加载,三条数据,问号?后的参数 data:{ start:0, count:3 }, // 箭头函数,this指向 success:(res)=>{ this.setData({ inTheaters:res.data.subjects }) } }) wx.request({ // 类型coming_soon(不久上映) url: app.gBaseUrl + 'coming_soon', data:{ start:0, count:3 }, success:(res)=>{ this.setData({ comingSoon:res.data.subjects }) } }) wx.request({ // 类型top250(top250) url: app.gBaseUrl + 'top250', data:{ start:0, count:3 }, success:(res)=>{ this.setData({ top250:res.data.subjects }) } }) }, // 去更多电影页面 onGotoMore(event){ console.log(event) // 获取自定义属性,作为参数 const type = event.currentTarget.dataset.type wx.navigateTo({ url: '/pages/more-movie/more-movie?type=' + type, }) }, // 真实机型输入框点击确定触发,小程序模拟器enter键触发,kin-ui的事件,event.detail.value可获取到输入字 onConfirm(event){ // 正在搜索 this.setData({ searchResult:true }) wx.request({ url: app.gBaseUrl + 'search', // 查询参数,?后的参数 data:{ q:event.detail.value }, success:(res)=>{ this.setData({ searchData:res.data.subjects }) }, }) }, // 取消搜索触发, onSearchCancel(event){ this.setData({ searchResult:false }) }, })
注册movies-list子组件,movies.json
{ "usingComponents": { "movie-list":"/components/movie-list/index", "l-search-bar":"/miniprogram_npm/lin-ui/search-bar/index" }, "navigationBarTitleText":"光与影" }
部分电影数据
{ "count": 20, "start": 0, "subjects": [ { "casts": [ { "avatars": { "large": "https://img3.doubanio.com/view/celebrity/s_ratio_celebrity/public/p230.jpg" }, "name": "弗兰克·德拉邦特" }, { "avatars": { "large": "https://img3.doubanio.com/view/celebrity/s_ratio_celebrity/public/p17525.jpg" }, "name": "蒂姆·罗宾斯" }, { "avatars": { "large": "https://img3.doubanio.com/view/celebrity/s_ratio_celebrity/public/p34642.jpg" }, "name": "摩根·弗里曼" } ], "comments_count": 222527, "countries": [ "美国" ], "directors": [ { "avatars": { "large": null }, "name": "弗兰克·德拉邦特" } ], "genres": [ "剧情", "犯罪" ], "id": 3, "images": { "large": "https://img3.doubanio.com/view/photo/s_ratio_poster/public/p480747492.jpg" }, "original_title": "肖申克的救赎 The Shawshank Redemption", "rating": { "average": 9.6, "max": 10, "min": 0, "stars": "50" }, "reviews_count": 5794, "summary": "", "title": "肖申克的救赎 The Shawshank Redemption", "warning": "数据来源于网络整理,仅供学习,禁止他用。如有侵权请联系公众号:小楼昨夜又秋风。我将及时删除。", "wish_count": 98814, "year": 1994 }
引入movie-list组件,电影列表
components--> movie-llist-->movie-llist
<view class="container f-class"> <view class="title-container"> <text>{{title}}</text> <text class="more-text">更多 ></text> </view> <view class="movie-container"> <block wx:for="{{movies}}" wx:key="index"> <movie movie="{{item}}" /> </block> </view> </view>
/* components/movie-list/index.wxss */ .container{ padding: 36rpx 36rpx; background-color: #ffffff; } .title-container{ display: flex; flex-direction: row; justify-content: space-between; margin-bottom: 28rpx; } .movie-container{ display: flex; flex-direction: row; justify-content: space-between; } .more-text{ color: #1f4ba5; }
movie-lilst.js
// components/movie-list/index.js Component({ /** * 组件的属性列表 */ // 设置组件外链样式类 externalClasses:['f-class'], properties: { title:String, movies:Array }, /** * 组件的初始数据 */ data: { }, /** * 组件的方法列表 */ methods: { } })
注册movie组件
movie-list.json
{ "component": true, "usingComponents": { "movie": "/components/movie/index" } }
引入movie组件,电影卡片
components--> movie-->movie
<view catch:tap="onGoToDetail" class="container"> <image class="poster" src="{{movie.images.large}}"></image> <text class="title">{{movie.title}}</text> <view class="rate-container"> <l-rate disabled="{{true}}" size="22" score="{{movie.rating.stars/10}}" /> <text class="score">{{movie.rating.average}}</text> </view> </view>
.container{ display: flex; flex-direction: column; 200rpx; } .poster{ 100%; height: 270rpx; margin-bottom: 22rpx; } .title{ white-space: nowrap; text-overflow: ellipsis; overflow: hidden; word-break: break-all; } .rate-container{ margin-top:6rpx; display: flex; flex-direction: row; /* 基线对齐 */ align-items: baseline; } .score{ margin-left:20rpx; font-size:24rpx; }
movie.js
点击电影卡片,跳转到电影详情页面,携带id参数
// components/movie/index.js Component({ /** * 组件的属性列表 */ properties: { movie:Object }, /** * 组件的初始数据 */ data: { }, /** * 组件的方法列表 */ methods: { onGoToDetail(event){ // console.log(this.properties.movie) const mid = this.properties.movie.id wx.navigateTo({ url: '/pages/movie-detail/movie-detail?mid=' + mid }) } } })
跳转到更多电影页面
pages-->more-movie-->more-movie
<view class="container"> <block wx:for="{{movies}}" wx:key="index"> <movie class="movie" movie="{{item}}" /> </block> </view>
/* pages/more-movie/more-movie.wxss */ .container{ display: flex; flex-direction: row; /* 换行,用flex布局不会自动换行 */ flex-wrap: wrap; padding: 30rpx 28rpx; justify-content: space-between; } .movie{ margin-bottom: 30rpx; }
more-movie.js
1.触顶下拉加载数据动画,要在json文件中引入enablePullDownRefresh属性,然后在函数中,调用小程序的方法wx.showNavigationBarLoading()
// pages/more-movie/more-movie.js const app = getApp() Page({ /** * 页面的初始数据 */ data: { movies:[], _type:'' }, /** * 生命周期函数--监听页面加载 */ onLoad: function (options) { // 获取自定义type属性,作为请求路劲 const type = options.type this.data._type = type wx.request({ url: app.gBaseUrl + type, // 从第0条开始加载,请求12条数据 data:{ start:0, count:12 }, success:(res)=>{ console.log(res) this.setData({ movies:res.data.subjects }) } }) }, /** * 生命周期函数--监听页面初次渲染完成 */ onReady: function () { let title = '电影' switch(this.data._type){ case 'in_theaters': title='正在热映' break case 'coming_soon': title = '即将上映' break case 'top250': title = '豆瓣Top250' break } // 动态设置顶部标题 wx.setNavigationBarTitle({ title: title, }) }, /** * 生命周期函数--监听页面显示 */ onShow: function () { }, /** * 生命周期函数--监听页面隐藏 */ onHide: function () { }, /** * 生命周期函数--监听页面卸载 */ onUnload: function () { }, /** * 页面相关事件处理函数--监听用户下拉动作 */ onPullDownRefresh: function () { wx.request({ url: app.gBaseUrl + this.data._type, // 页面下拉,获取最新的12条数据 data:{ start:0, count:12, }, success:(res)=>{ this.setData({ movies:res.data.subjects }) // 下拉效果停止 wx.stopPullDownRefresh() } }) }, /** * 页面上拉触底事件的处理函数 */ onReachBottom: function () { // [1,2,3...... 12,13,] // [0,1,2....11] + [12,13....23] + [24...35] + [36...47]
//每次上拉加载出来加载动画
wx.showNavigationBarLoading() wx.request({ url: app.gBaseUrl + this.data._type, // 从每次请求数据的最后一条开始加载,12条数据 data:{ start: this.data.movies.length, count:12 }, success:(res)=>{ console.log(res) this.setData({ // 数组追加合并 movies:this.data.movies.concat(res.data.subjects) }) // 每次追加数据后,顶部添加加载转圈效果 wx.hideNavigationBarLoading() } }) }, /** * 用户点击右上角分享 */ onShareAppMessage: function () { } })
json文件
{ "usingComponents": { "movie":"/components/movie/index" }, "enablePullDownRefresh":true }
跳转到电影详情页面
实现影人中图片左右滚动效果,引入小程序组件scroll-view组件,但是会放flex布局失效,需要添加enable-flex 让其生效,并且会是容器留有很大高度,给容器高度重新设置下
图片的mode模式,给image标签添加mode="aspectFill"缩放模式,官网https://developers.weixin.qq.com/miniprogram/dev/component/image.html
给图片添加模糊效果,-webkit-filter:blur(20px);
pages-->movie-detail-->movie-detail
<view class="container"> <image mode="aspectFill" class="head-img" src="{{movie.image}}"></image> <view class="head-img-hover"> <text class="main-title">{{movie.title}}</text> <text class="sub-title">{{movie.subtitle}}</text> <view class="like"> <text class="highlight-font">{{movie.wishCount}}</text> <text class="plain-font">人喜欢</text> <text class="highlight-font">{{movie.commentsCount}}</text> <text class="plain-font">条评论</text> </view> <image bind:tap="onViewPost" class="movie-img" src="{{movie.image}}"></image> </view> <view class="summary"> <view class="original-title"> <text>{{movie.title}}</text> </view> <view class="flex-row"> <text class="mark">评分</text> <view class="score-container"> <l-rate disabled="{{true}}" size="22" score="{{movie.rating}}" /> <text class="average">{{movie.average}}</text> </view> </view> <view class="flex-row"> <text class="mark">导演</text> <text>{{movie.directors}}</text> </view> <view class="flex-row"> <text class="mark">影人</text> <text>{{movie.casts}}</text> </view> <view class="flex-row"> <text class="mark">类型</text> <text>{{movie.genres}}</text> </view> </view> <view class="hr"></view> <view class="synopsis"> <text class="synopsis-font">剧情简介</text> <text class="summary-content">{{movie.summary}}</text> </view> <view class="hr"></view> <view class="casts"> <text class="cast-font">影人</text> <scroll-view enable-flex scroll-x class="casts-container"> <block wx:for="{{movie.castsInfo}}" wx:key="index"> <view class="cast-container"> <image class="cast-img" src="{{item.img}}"></image> <text>{{item.name}}</text> </view> <view class="cast-container"> <image class="cast-img" src="{{item.img}}"></image> <text>{{item.name}}</text> </view> <view class="cast-container"> <image class="cast-img" src="{{item.img}}"></image> <text>{{item.name}}</text> </view> </block> </scroll-view> </view> </view>
.container{ display: flex; flex-direction: column; } .head-img{ 100%; height: 320rpx; /* 图片模糊效果 */ -webkit-filter:blur(20px); } .head-img-hover{ 100%; height: 320rpx; position: absolute; display: flex; flex-direction: column; } .main-title{ font-size:38rpx; color:#fff; font-weight:bold; letter-spacing: 2px; margin-top: 50rpx; margin-left: 40rpx; } .sub-title{ font-size: 28rpx; color:#fff; margin-left: 40rpx; margin-top: 30rpx; } .like{ display:flex; flex-direction: row; margin-top: 30rpx; margin-left: 40rpx; } .highlight-font{ color: #f21146; font-size:22rpx; margin-right: 10rpx; } .plain-font{ color: #666; font-size:22rpx; margin-right: 30rpx; } .movie-img{ height:238rpx; 175rpx; position: absolute; top:160rpx; right: 30rpx; } .summary{ margin-left:40rpx; margin-top: 40rpx; color: #777777; } .original-title{ color: #1f3463; font-size: 24rpx; font-weight: bold; margin-bottom: 40rpx; } .flex-row{ display: flex; flex-direction: row; align-items: baseline; margin-bottom: 10rpx; } .mark{ margin-right: 30rpx; white-space:nowrap; color: #999999; } .score-container{ display: flex; flex-direction: row; align-items: baseline; } .average{ margin-left:20rpx; margin-top:4rpx; } .hr{ margin-top:45rpx; 100%; height: 1px; background-color: #d9d9d9; } .synopsis{ margin-left:40rpx; display:flex; flex-direction: column; margin-top: 50rpx; } .synopsis-font{ color:#999; } .summary-content{ margin-top: 20rpx; margin-right: 40rpx; line-height:40rpx; letter-spacing: 1px; } .casts{ display: flex; flex-direction: column; margin-top:50rpx; margin-left:40rpx; } .cast-font{ color: #999; margin-bottom: 40rpx; } .cast-img{ 170rpx; height: 210rpx; margin-bottom: 10rpx; } .casts-container{ display: flex; flex-direction: row; margin-bottom: 50rpx; margin-right: 40rpx; height: 300rpx; } .cast-container{ display: flex; flex-direction: column; align-items: center; margin-right: 40rpx; }
movie-detail.js
请求获取的电影详情数据,需要预处理
{ "casts": [ { "avatars": { "large": "https://img1.doubanio.com/view/celebrity/s_ratio_celebrity/public/p1470662353.8.jpg" }, "name": "约翰·卡尼" }, { "avatars": { "large": "https://img3.doubanio.com/view/celebrity/s_ratio_celebrity/public/p49301.jpg" }, "name": "格伦·汉塞德" }, { "avatars": { "large": "https://img3.doubanio.com/view/celebrity/s_ratio_celebrity/public/p40505.jpg" }, "name": "玛可塔·伊尔格洛娃" } ], "comments_count": 61027, "countries": [ "爱尔兰" ], "directors": [ { "avatars": { "large": null }, "name": "约翰·卡尼" } ], "genres": [ "剧情", "爱情", "音乐" ], "id": 1, "images": { "large": "https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2173720203.jpg" }, "original_title": "曾经 Once", "rating": { "average": 8.3, "max": 10, "min": 0, "stars": "40" }, "reviews_count": 2232, "summary": "", "title": "曾经 Once", "warning": "数据来源于网络整理,仅供学习,禁止他用。如有侵权请联系公众号:小楼昨夜又秋风。我将及时删除。", "wish_count": 96885, "year": 2007 }
1. 点击电影图片,有放大效果,调用小程序wx.previewImage预览函数
// pages/movie-detail/movie-detail.js import {convertToCastString, convertToCastInfos} from '../../utils/util.js' const app = getApp() Page({ /** * 页面的初始数据 */ data: { movie:{} }, /** * 生命周期函数--监听页面加载 */ onLoad: function (options) { const mid = options.mid wx.request({ url: app.gBaseUrl + 'subject/' + mid, success:(res)=>{ console.log(res.data) this.processMovieData(res.data) // this.setData({ // movie:res.data // }) } }) }, processMovieData(movie){ const data = {} // 前端字段与后台字段映射,数据预处理 // 处理小明 / 小红 / 小李 这种字符串,movie.directors是个数组 data.directors = convertToCastString(movie.directors) // movie.casts是个数组 data.casts = convertToCastString(movie.casts) data.image = movie.images.large data.title = movie.title data.subtitle = movie.countries[0]+'·'+movie.year data.wishCount = movie.wish_count data.commentsCount = movie.comments_count data.rating = movie.rating.stars/10 data.average = movie.rating.average // movie.genres是个数组,join() 方法用于把数组中的所有元素放入一个字符串,元素是通过指定的分隔符进行分隔的 data.genres = movie.genres.join('、') data.summary = movie.summary // movie.casts是个数组 data.castsInfo = convertToCastInfos(movie.casts) this.setData({ movie:data }) }, // 点击图片放大功能 onViewPost(event){ wx.previewImage({ urls: [this.data.movie.images.large], }) }, })
引入的utils.js工具
function convertToCastString(casts){ var castsjoin = ""; for (var idx in casts) { castsjoin = castsjoin + casts[idx].name + " / "; } // substring() 方法用于提取字符串中介于两个指定下标之间的字符。 return castsjoin.substring(0, castsjoin.length - 2); } function convertToCastInfos(casts) { var castsArray = [] for (var idx in casts) { // 过滤掉原来的avatars属性 var cast = { img: casts[idx].avatars ? casts[idx].avatars.large : "", name: casts[idx].name } castsArray.push(cast); } return castsArray; } export { convertToCastString, convertToCastInfos }