小程序项目文字与电影(一)
一,首页页面功能
pages-->welcome页面组件
注意;顶部颜色在welcome.json中配置,
2.每个页面组件都有个影藏的page标签包裹在外头,需要给page也设置颜色
welcome.wxml
<!--pages/welcome/welcome.wxml--> <view class="container"> <image class="avatar" src="/images/avatar/1.png"></image> <text class="motto">Hello,七月</text> <!-- <button></button> --> <!-- 自定义button --> <view bind:tap="onTap" class="journey-container"> <text class="journey">开启小程序之旅</text> </view> </view> <!-- flex 容器布局 --> <!-- 路由API --> <!-- 事件的回调函数 -->
welcome.wxss
.container{ display: flex; flex-direction: column; align-items: center; background-color: #b3d4db; /* height: 667px; */ } .avatar{ width:200rpx; height:200rpx; margin-top:160rpx; } .motto{ margin-top: 100rpx; font-size: 32rpx; font-weight: bold; } .journey-container{ border: 1px solid #405f80; width: 200rpx; height: 80rpx; border-radius: 5px; text-align: center; margin-top: 200rpx; } .journey{ font-size: 22rpx; color:#405f80; line-height: 80rpx; font-weight: bold; } page{ background-color: #b3d4db; }
顶部颜色配置和标题,welcome.json
{ "usingComponents": {}, "navigationBarBackgroundColor": "#b3d4db", "navigationBarTitleText": "首页", "navigationBarTextStyle": "black" }
全局样式在app.wxss中配置
text{ color:#666; font-size: 24rpx; }
二,引入lin-ui样式组件库,官网:https://doc.mini.talelin.com/start/
lin-ui 是为小程序设计的组件库
打开小程序的项目根目录,在控制台中执行下面的命令 npm init, 此时,会生成一个package.json文件
安装;npm i lin-ui,
然后用小程序官方IDE打开我们的小程序项目,找到 工具
选项,点击下拉选中 构建npm
,等待构建完成即可。
可以看到小程序IDE工具的目录结构里多出了一个文件夹 miniprogram_npm
(之后所有通过 npm
引入的组件和 js
库都会出现在这里),
打开后可以看到 lin-ui
文件夹,也就是我们所需要的组件。
在welcome组件中使用第三方lin-ui组件,使用avatar图片组件
welcome.json配置组件路径,自定义组件名称,即可在页面中使用
{ "usingComponents": { "l-avatar": "/miniprogram_npm/lin-ui/avatar/index" }, "navigationBarBackgroundColor": "#b3d4db", "navigationBarTitleText": "首页", "navigationBarTextStyle": "white" }
welcome.wxml直接使用
通过在<l-avatar/>
设置size
属性来设置头像的大小,单位为rpx
<view class="container"> <!-- <image class="avatar" src="/images/avatar/1.png"></image> --> <l-avatar l-text-class="l-avatar-text" class="l-avatar" placement="bottom" open-data="{{['userAvatarUrl','userNickName']}}" size="180"/> <text class="motto">Hello,七月</text> <!-- <button></button> --> <!-- 自定义button --> <view bind:tap="onTap" class="journey-container"> <text class="journey">开启小程序之旅</text> </view> </view>
welcome.wxss
.l-avatar{ margin-top: 160rpx; }
2.2, 设置启动页组件
第一种;可在app.json全局配置页面组件启动顺序,通过enteyPagePath属性配置
{ "pages": [ "pages/welcome/welcome", "pages/posts/posts" ], "entryPagePath": "pages/posts/posts", "window": { "backgroundTextStyle": "light", "navigationBarBackgroundColor": "#fff", "navigationBarTitleText": "winxin", "navigationBarTextStyle": "white" }, "style": "v2", "sitemapLocation": "sitemap.json" }
第二种,在微信IDE中添加编译模式中配置,建议在这里配置,配置启动页面路劲
2.3.点击开启小程序之旅按钮,跳转到posts页
welcome.wxml,绑定事件bind
<view class="container"> <!-- <image class="avatar" src="/images/avatar/1.png"></image> --> <l-avatar l-text-class="l-avatar-text" class="l-avatar" placement="bottom" open-data="{{['userAvatarUrl','userNickName']}}" size="180"/> <text class="motto">Hello,七月</text> <!-- <button></button> --> <!-- 自定义button --> <view bind:tap="onTap" class="journey-container"> <text class="journey">开启小程序之旅</text> </view> </view>
welcome.js
注,普通页面跳转到带有tarbar页面,不能用wx.redirect跳转,使用wx.switchTab
Page({ /** * 页面的初始数据 */ data: { }, onTap:function(params) { wx.switchTab({ url:"/pages/posts/posts" }) },
底部tabbar配置,app.json配置,官网:https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/app.html#tabBar
"tabBar": { "borderStyle": "white", "selectedColor": "#333333", "position": "bottom", "color": "#999999", "list": [ { "text": "阅读", "pagePath": "pages/posts/posts", "iconPath": "/images/tab/post.png", "selectedIconPath": "/images/tab/post@highlight.png" }, { "text": "电影", "pagePath": "pages/movies/movies", "iconPath": "/images/tab/movie.png", "selectedIconPath": "/images/tab/movie@highlight.png" } ] },
三,阅读页面
3.1,pages-->新建post目录--》posts页面
注;1.vertical="true",是为真, vetical="false", 也是为真,引号里头是字符串。 2.如果该属性要为假,加双花括号,里头为js表达式,这个是小程序语法,如果直接加属性,代表为真
2.顶部为轮播图,底部封装成一个post组件,为信息列表页,点击列表页,跳转到详情页
逻辑,每个信息列表页自定义一个post-id属性,跳转到对应id的详情页,跳转时加上id
wx:key="postId", 直接加属性就可以,不需要item.postId
<view> <swiper interval="3000" circular vertical="{{false}}" indicator-dots="{{true}}" autoplay="{{true}}"> <swiper-item> <image data-post-id="3" bind:tap="onGoToDetail" src="/images/bestplayers.png"></image> </swiper-item> <swiper-item> <image data-post-id="0" bind:tap="onGoToDetail" src="/images/lpl.png"> </image> </swiper-item> <swiper-item> <image data-post-id="4" bind:tap="onGoToDetail" src="/images/jumpfly.png"> </image> </swiper-item> </swiper> <block wx:for="{{postList}}" wx:key="postId" wx:for-item="item" wx:for-index="idx"> <post res="{{item}}"/> </block> </view>
posts.js
注,部分data数据导出
var postList = [ { title: "2020LPL夏季赛季后赛观赛指南", content: "8月9号,LPL常规赛收官之战结束,在事关季后赛轮次的比赛中关键对局中,SN战胜了FPX,为本赛季常规赛画上句号。进入季后赛的战队依次为,TES、JDG、IG、SN、V5、LGD、WE、FPX", imgSrc: "/images/lpl.png", reading: 102, detail: "8月9号,LPL常规赛收官之战结束,在事关季后赛轮次的比赛中关键对局中,SN战胜了FPX,为本赛季常规赛画上句号。进入季后赛的战队依次为,TES、JDG、IG、SN、V5、LGD、WE、FPX。既有传统四强,又有新崛起的黑马。本文主要是从上路的大改动展开,引发对所有其他的影响。牵一发而动全身,上路一旦回归carry上单版本,对野区和中路的影响是显而易见的。而下路在艾希大砍一刀之后,女警的过于强势,使她只能出现在BAN位上,因此主流下路还是会回归功能性下路英雄。由此,可以对应各位选手的英雄池,对应各支战队的战术储备,漫长的季后赛,考验的就是各队适应版本的能力。", collection: 92, dateTime: "24小时前", headImgSrc: "/images/lpl.png", author: "猫是猫的猫", date: "Nov 20 2020", avatar: "/images/avatar/5.png", postId: 0, music: { url: "http://music.163.com/song/media/outer/url?id=1372060183.mp3", title: "空-徐海俏", coverImg: "https://y.gtimg.cn/music/photo_new/T002R300x300M000002sNbWp3royJG_1.jpg?max_age=2592000", } },
this.setData的数据会加入到page.data中,用于数据绑定
event中可以获取自定义属性postid
import { postList } from "../../data/data.js"; Page({ /** * 页面的初始数据 */ data: {}, /** * 生命周期函数--监听页面加载 * 钩子函数 hook function * 顺序 * 条件渲染 列表渲染 */ async onLoad(options) { // setData // 更新 // 创建+更新 // JSON // ES6 // 同步存储 wx.setStorageSync("flag", 2); // 异步存储,返回的是一个promise const flag = await wx.getStorage({ key: "flag", // success(value){ // console.log(value.data) // } }); // flag.then((value)=>{ // console.log(value) // }) console.log(flag); this.setData({ postList, }); }, onGoToDetail(event) { // 获取自定义属性 const pid = event.currentTarget.dataset.postId wx.navigateTo({ url: "/pages/post-detail/post-detail?pid=" + pid, }); }, /** * 生命周期函数--监听页面初次渲染完成 */ onReady() { // console.log("onready") }, /** * 生命周期函数--监听页面显示 */ onShow: function () { // console.log("onshow") }, /** * 生命周期函数--监听页面隐藏 * 条件触发 */ onHide: function () { // console.log("onhide") }, /** * 生命周期函数--监听页面卸载 */ onUnload: function () { // console.log("onunload") }, /** * 页面相关事件处理函数--监听用户下拉动作 */ onPullDownRefresh: function () {}, /** * 页面上拉触底事件的处理函数 */ onReachBottom: function () { console.log("onreach"); }, /** * 用户点击右上角分享 */ onShareAppMessage: function () {}, });
posts.json中导入子组件post,以及第三方的lin-ui库的组件
{ "usingComponents": { "l-icon":"/miniprogram_npm/lin-ui/icon/index", "post":"/components/post/index" }, "navigationBarBackgroundColor": "#C22A1E", "navigationBarTitleText":"文与字" }
新建components-->post-->post
post.wxml
有个假设需求,如果点击整个view父容器,跳转到信息详情页,但是点击图片头像需要跳转到个人中心页,此时该如何做呢
给view父容器绑定bindtap事件,他是可以冒泡的, 给image绑定catchtap事件,他是阻止冒泡的,避免传递给父容器
<view bind:tap="onGODetail" data-post-id={{res.postId}} class="post-container"> <view class="post-author-date"> <image catch:tap="onMaxImage" class="post-author" src="{{res.avatar}}"></image> <text class="post-date">{{res.date}}</text> </view> <text class="post-title">{{res.title}}</text> <image class="post-image" src="{{res.imgSrc}}"></image> <text class="post-content">{{res.content}}</text> <view class="post-like"> <!-- <image class="post-like-image" src="/images/icon/chat.png"></image> --> <l-icon class="post-like-image" color="#666" size="28" name="favor" /> <text class="post-like-font">{{item.collection}}</text> <!-- <image class="post-like-image" src="/images/icon/view.png"></image> --> <l-icon class="post-like-image" color="#666" size="32" name="eye" /> <text class="post-like-font">{{item.reading}}</text> </view> </view>
post.wxss
.post-container{ display: flex; flex-direction: column; margin-top: 20rpx; margin-bottom: 40rpx; background-color: #fff; border-top:1px solid #ededed; border-bottom:1px solid #ededed; padding-bottom: 10rpx; } .post-author-date{ /* margin-top:10rpx; margin-bottom: 20rpx; margin-left: 10rpx; */ margin: 10rpx 0 20rpx 10rpx; display: flex; flex-direction: row; align-items: center; } .post-author{ width:60rpx; height:60rpx; /* vertical-align: middle; */ } .post-date{ margin-left:20rpx; font-size: 26rpx; /* vertical-align: middle; */ } .post-title{ font-size: 34rpx; font-weight: 600; margin-bottom: 20rpx; margin-left: 20rpx; color:#333; } .post-image{ width: 100%; height:340rpx; margin-bottom: 30rpx; } .post-content{ color: #666; font-size:28rpx; margin-bottom: 20rpx; margin-left:20rpx; line-height: 40rpx; letter-spacing: 2rpx; } .post-like{ display: flex; flex-direction: row; align-items: center; margin-left:20rpx; } .post-like-image{ /* height:32rpx; width:32rpx; */ margin-right:16rpx; } /* html */ .post-like-font{ margin-right: 40rpx; font-size:26rpx; }
post.js,接收父组件的数据,点击信息栏,跳转到详情页
// components/post/index.js Component({ /** * 组件的属性列表 */ properties: { res:Object },
第二种方法(跳转到详情页),posts页面组件,监听子post组件的自定义posttap事件,当子组件post点击信息栏后,触发posttap事件,然后父组件posts监听到了,再去跳转到详情页(子向父传递数据).保持组件独立性
posts.wxml
<view> <swiper interval="3000" circular vertical="{{false}}" indicator-dots="{{true}}" autoplay="{{true}}"> <swiper-item> <image data-post-id="3" bind:tap="onGoToDetail1" src="/images/bestplayers.png"></image> </swiper-item> <swiper-item> <image data-post-id="0" bind:tap="onGoToDetail" src="/images/lpl.png"> </image> </swiper-item> <swiper-item> <image data-post-id="4" bind:tap="onGoToDetail" src="/images/jumpfly.png"> </image> </swiper-item> </swiper> <block wx:for="{{postList}}" wx:key="index" wx:for-item="item" wx:for-index="idx"> <!-- 给post绑定posttap事件,监听 --> <post bind:posttap = "onGoToDetail" res="{{item}}"/> </block> </view>
posts.js
onGoToDetail(event) { // 获取自定义属性id, event.detail.pid,自定义事件传递过来的id const pid = event.currentTarget.dataset.postId | event.detail.pid; wx.navigateTo({ url: "/pages/post-detail/post-detail?pid=" + pid, }); },
post.wxml
<view bind:tap="onTap" class="post-container"> <view class="post-author-date"> <image catch:tap="onMaxImage" class="post-author" src="{{res.avatar}}"></image> <text class="post-date">{{res.date}}</text> </view>
post.js
// components/post/index.js Component({ /** * 组件的属性列表 */ properties: { res:Object }, /** * 组件的初始数据 */ data: { }, /** * 组件的方法列表 * 组件的开发者不应该决定 * 点击之后做什么事情 不应该 * 组件的使用者 * 自定义事件 */ methods: { onTap(event){ const pid = this.properties.res.postId // 触发自定义事件posttap,传递pid this.triggerEvent('posttap',{ pid }) }, } })
跳转到详情页面post-detail
注,传递过来的postid,在onload函数中options中获取
pages--》post-detail->post-detail
post-detail.wxml
<view class="container"> <image class="head-image" src="{{postData.headImgSrc}}"></image>
播放图片 <image wx:if="{{!isPlaying}}" bind:tap="onMusicStart" class="audio" src="/images/music/music-start.png" />
停止图片 <image bind:tap="onMusicStop" wx:else class="audio" src="/images/music/music-stop.png" /> <!-- <image bind:tap="onMusic" class="audio" src="{{isPlaying?'/images/music/music-stop.png':'/images/music/music-start.png'}}" /> --> <view class="author-date"> <image class="avatar" src="{{postData.avatar}}"></image> <text class="author">{{postData.author}}</text> <text class="const-text">发表于</text> <text class="date">{{postData.dateTime}}</text> </view> <text class="title">{{postData.title}}</text> <view class="tool"> <view class="circle"> <image wx:if="{{collected}}" bind:tap="onCollect" class="" src="/images/icon/collection.png"></image> <!-- 未收藏图片 --> <image wx:else bind:tap="onCollect" class="" src="/images/icon/collection-anti.png"></image> <image bind:tap="onShare" class="share-img" src="/images/icon/share.png"></image> </view> <view class="horizon"></view> </view> <text class="detail">{{postData.detail}}</text> </view>
post-detail.wxss
.container{ display: flex; flex-direction: column; } .head-image{ width: 100%; height: 460rpx; } .author-date{ display: flex; flex-direction: row; align-items: center; margin-top: 20rpx; margin-left: 30rpx; } .avatar{ width: 64rpx; height: 64rpx; } .author{ font-size: 30rpx; font-weight: 300; margin-left:20rpx; color:#666; } .const-text{ font-size: 24rpx; color: #999; margin-left: 20rpx; } .date{ font-size: 24rpx; margin-left: 30rpx; color:#999; } .title{ margin-left: 40rpx; font-size: 36rpx; font-weight: 700; margin-top: 30rpx; letter-spacing: 2px; color:#4b556c; } .tool{ display: flex; flex-direction: column; align-items: center; justify-content: center; margin-top:20rpx; } .circle{ display: flex; width: 660rpx; flex-direction: row; justify-content: flex-end; } .circle image{ width: 90rpx; height: 90rpx; } .share-img{ margin-left: 30rpx; } /* 主轴 交叉轴 */ .horizon{ width: 660rpx; height: 1px; background-color: #e5e5e5; position: absolute; z-index: -99; } .detail { color: #666; margin-left: 30rpx; margin-top: 20rpx; margin-right: 30rpx; line-height: 44rpx; letter-spacing: 2px; } /* 230 */ .audio{ width: 102rpx; height: 110rpx; position: absolute; left:50%; margin-left: -51rpx; top:185rpx; opacity: 0.6; }
post-detail.js
逻辑分析
收藏功能
1.,点击收藏图片,将每个文章的id号和收藏状态组成一个对象,保存在storage中,
2.当页面加载onload函数中,读取storage中的信息,然后通过postid文章id判断是否收藏过该文章
音乐播放功能
1.在页面加载时,就创建背景音乐实例,监听背景音乐的播放和暂停
2.当点击播放音乐的图片,背景音乐播放,当点击暂停音乐图片,背景音乐暂停
3.如果在详情页,背景音乐在播放,退回详情页(背景音乐还在播放状态的),在次进来时,我们发现图片状态不是正在播放状态中,
此时我们需要在app.js中定义全局变量gIspalyingMusic(背景音乐播放状态),gIsplayingPostId(文章id),来判断isplaying的状态
全局变量修改后,但在小程序再次启动后,他的变量会再次变成初始值,不会已经改变的值。
分享功能,点击分享图片,弹出分享框
app.js
App( { // 小程序启动的函数 onLaunch(){ console.log("小程序启动") }, gIsPlayingMusic:false, gIsPlayingPostId:-1, } )
// pages/post-detail/post-detail.js import {postList} from '../../data/data.js' // 获取app.js的全局变量 const app = getApp() Page({ /** * 页面的初始数据 */ data: { postData:{}, // 收藏的状态 collected:false, // 播放状态,图片状态 isPlaying:false, // 保存文章id _pid:null, _postsCollected:{}, // BackgroundAudioManager 实例 _mgr:null }, /** * 生命周期函数--监听页面加载 */ onLoad: function (options) { // const postData = postList[options.pid] this.data._pid = options.pid // 页面加载后,从缓存读取收藏状态 const postsCollected = wx.getStorageSync('posts_collected') console.log(postsCollected) // 判断缓存是否有状态,有的话从缓存读取的状态赋值给页面的data if(postsCollected){ this.data._postsCollected = postsCollected } let collected = postsCollected[this.data._pid] // 如果第一篇文章收藏过,第二篇文章进来未收藏过,需要判断 if(collected === undefined){ // 如果undefined 说明文章从来没有被收藏过 collected = false } this.setData({ postData, collected, isPlaying: this.currentMusicIsPlaying() }) // 页面加载,创建BackgroundAudioManager 实例 const mgr = wx.getBackgroundAudioManager() this.data._mgr = mgr // 监听背景音乐播放函数 mgr.onPlay(this.onMusicStart) // 监听背景音乐停止函数 // mgr.onStop(this.onMusicStop) // 监听背景音乐暂停函数 mgr.onPause(this.onMusicStop) }, // 记录每篇文章的歌的播放状态, currentMusicIsPlaying(){ if(app.gIsPlayingMusic && app.gIsPlayingPostId === this.data._pid ){ return true } return false }, // 开始播放 onMusicStart(event){ const mgr = this.data._mgr // mgr.onPlay(()=>{ // console.log(123) // }) const music = postList[this.data._pid].music // 只要赋值了src ,就会播放 mgr.src = music.url mgr.title = music.title mgr.coverImgUrl = music.coverImg // 改变背景音乐的播放状态 app.gIsPlayingMusic = true app.gIsPlayingPostId = this.data._pid this.setData({ isPlaying:true }) }, // 播放停止 onMusicStop(event){ const mgr = this.data._mgr // 背景音乐暂停 mgr.pause() app.gIsPlayingMusic = false app.gIsPlayingPostId = -1 this.setData({ isPlaying:false }) // 音乐停止 - start // 音乐播放 - stop }, // 点击分享按钮 async onShare(event){ // 菜单弹出 const result = await wx.showActionSheet({ itemList: ['分享到QQ','分享到微信','分享到朋友圈'] }) // 可以获取到itemList索引 console.log(result) }, // 点击收藏按钮 async onCollect(event){ // 这样会覆盖原来的数据,会新开辟空间建对象 // const postsCollected ={} // 一个对象保存多个状态 const postsCollected = this.data._postsCollected // 保存的value值,点击一次取反 postsCollected[this.data._pid] = !this.data.collected this.setData({ collected:!this.data.collected }) // 将文章的Id和状态保存,postsCollected // { // 1::true // } wx.setStorageSync('posts_collected',postsCollected) // 弹出框提示 wx.showToast({ title: this.data.collected?'收藏成功':'取消收藏', duration: 3000 }) // 模态对话框,返回的是一个promise // const result =await wx.showModal({ // title:'是否收藏文章' // }) // if(result.confirm){ // } }, /** * 生命周期函数--监听页面初次渲染完成 */ onReady: function () { }, /** * 生命周期函数--监听页面显示 */ onShow: function () { }, /** * 生命周期函数--监听页面隐藏 */ onHide: function () { }, /** * 生命周期函数--监听页面卸载 */ onUnload: function () { }, /** * 页面相关事件处理函数--监听用户下拉动作 */ onPullDownRefresh: function () { }, /** * 页面上拉触底事件的处理函数 */ onReachBottom: function () { }, /** * 用户点击右上角分享 */ onShareAppMessage: function () { } })