温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

第三个页面:构建新闻详情页面

发布时间:2020-07-17 06:11:53 来源:网络 阅读:3492 作者:ZeroOne01 栏目:移动开发

笔记内容:构建新闻详情页面
笔记日期:2018-01-09


从文章列表跳转到新闻详情页(组件自定义属性及获取属性)

在编写从文章列表跳转到新闻详情页的代码之前,先来修改一下之前的页面,之前我们编写了两个模板文件,但是还有两个细节没有完善好,一个是post.wxss中的.post-container样式没有移植到模板文件中,另一个是wxml模板文件中每句数据绑定代码都需要通过item这个子元素去调用属性,显得有点麻烦,我们可以使用一种语法去解决这个问题。

1.首先将post.wxss中的.post-container样式移植到模板文件中

2.解决item重复的问题,在post.wxml中将之前的 template 代码修改为以下内容:

<template is="postItem" data="{{...item}}" />

然后模板文件中的数据绑定代码就不需要重复使用item子元素进行属性的调用了:

<!-- 模板文件需要使用template标签包围 -->
<template name="postItem">
  <view class='post-container'>
      <view class='post-author-date'>
        <image src='{{avatar}}' class='post-author'></image>
        <text class="post-date">{{date}}</text>
      </view>
      <text class='post-title'>{{title}}</text>
      <image class='post-image' src='{{imgSrc}}'></image>
      <text class='post-content'>{{content}}</text>
      <view class='post-like'>
        <image src='../../images/icon/chat.png' class='post-like-img'></image>
        <text class='post-like-font'>{{reading}}</text>
        <image src='../../images/icon/view.png' class='post-like-img'></image>
        <text class='post-like-font'>{{collection}}</text>
      </view>
    </view>
</template>

完成以上的修改后,就可以开始编写新闻详情页的代码了:

1.构建目录文件结构:
第三个页面:构建新闻详情页面

2.由于我们需要实现点击一个文章列表中的文章就跳转到该文章的详情页面,所以我们还得给每一个文章做一个标识符,不然谁知道你点的是哪篇文章。这个标识符可以写在数据文件中,作为一个属性存在,所以需要在数据文件中为每一个文章数据都加上一个属性,我定义的属性名称是postId:

// 将数据整合成数组类型
var local_database = [
  {
    date: "Jan 06 2018",
    title: "正是虾肥蟹壮时",
    imgSrc: "/images/post/crab.png",
    avatar: "/images/avatar/1.png",
    content: "“山明水净夜来霜,数树深红出浅黄。试上高楼清入骨,岂如春色嗾人狂。”金秋时节,天高云淡,秋风送爽,气候宜人。秋风秋阳中,硕果坠挂枝头,玉米抚须含笑,高粱引颈高歌,豆荚饱满圆润。",
    reading: "112",
    collection: "96",
    postId:0,
  },
  {
    date: "Jan 03 2018",
    title: "比利·林恩的中场战事",
    imgSrc: "/images/post/bl.png",
    avatar: "/images/avatar/2.png",
    content: "伊拉克战争时期,来自美国德州的19岁技术兵比利·林恩(乔·阿尔文 Joe Alwyn 饰)因为一段偶然拍摄的视频而家喻户晓。那是一次规模不大却激烈非常的遭遇战,战斗中林恩所在的B班班长(范·迪塞尔 Vin Diesel 饰)遭到当地武装分子的伏击和劫持,而林恩为了营救班长不惜铤而走险冲锋陷阵。",
    reading: "92",
    collection: "65",
    postId: 1,
  },
  {
    date: "Jan 05 2018",
    title: "肖申克的救赎",
    imgSrc: "/images/post/xs.jpg",
    avatar: "/images/avatar/3.png",
    content: "20世纪40年代末,小有成就的青年银行家安迪(蒂姆·罗宾斯 Tim Robbins 饰)因涉嫌杀害妻子及她的情人而锒铛入狱。在这座名为肖申克的监狱内,希望似乎虚无缥缈,终身监禁的惩罚无疑注定了安迪接下来灰暗绝望的人生。",
    reading: "92",
    collection: "65",
    postId: 2,
  },
  {
    date: "Jan 01 2018",
    title: "霸王别姬",
    imgSrc: "/images/post/bj.jpg",
    avatar: "/images/avatar/4.png",
    content: "段小楼(张丰毅)与程蝶衣(张国荣)是一对打小一起长大的师兄弟,两人一个演生,一个饰旦,一向配合天衣无缝,尤其一出《霸王别姬》,更是誉满京城,为此,两人约定合演一辈子《霸王别姬》。但两人对戏剧与人生关系的理解有本质不同,段小楼深知戏非人生,程蝶衣则是人戏不分。",
    reading: "92",
    collection: "65",
    postId: 3,
  },
  {
    date: "Jan 08 2018",
    title: "这个杀手不太冷",
    imgSrc: "/images/post/ss.jpg",
    avatar: "/images/avatar/5.png",
    content: "里昂(让·雷诺饰)是名孤独的×××,受人雇佣。一天,邻居家小姑娘马蒂尔达(纳塔丽·波特曼饰)敲开他的房门,要求在他那里暂避杀身之祸。原来邻居家的主人是警方缉毒组的眼线,只因贪污了一小包×××而遭恶警(加里·奥德曼饰)杀害全家的惩罚。",
    reading: "92",
    collection: "65",
    postId: 4,
  },
  {
    date: "Jan 04 2018",
    title: "阿甘正传",
    imgSrc: "/images/post/ag.jpg",
    avatar: "/images/avatar/1.png",
    content: "阿甘(汤姆·汉克斯 饰)于二战结束后不久出生在美国南方阿拉巴马州一个闭塞的小镇,他先天弱智,智商只有75,然而他的妈妈是一个性格坚强的女性,她常常鼓励阿甘“傻人有傻福”,要他自强不息。",
    reading: "92",
    collection: "65",
    postId: 5,
  },
]

// 设置一个数据出口
module.exports = {
  // 输出的是一个Array对象
  postList: local_database,
}

3.在post.wxml文件中增加一个view标签,把template标签包围起来,因为template标签只是相当于一个占位符,编译之后这个标签是不存在的。然后给这个view标签注册一个tap事件,并且自定义一个属性进行postId的数据绑定:

<!-- 自定义属性必须以 data- 为前缀,后面连接的单词可以自定义 -->
<view catchtap="onPostTap" data-postId="{{item.postId}}">
      <template is="postItem" data="{{...item}}"/>
</view>

4.然后到post.js文件中增加一个事件函数:

onPostTap: function(event){
    // 从事件源中获取postId数据
    var postId = event.currentTarget.dataset.postid;
    console.log("on post id is " + postId);
 },

event是事件源对象,是默认会传入的参数,currentTarget是当前被触发事件的目标对象,dataset是数据集对象,数据要从数据集对象中获取。至于为什么是通过postid获取数据而不是通过data-postId或者postId获取,请参考下图:
第三个页面:构建新闻详情页面

如图,可以看到,自定义属性被编译之后的名称并不是原本的名称,所以我们通过dataset数据集对象获取自定义属性的数据时需要以编译后的名称为准。

5.接着就可以开始实现页面跳转了,先在post-detail.wxml中编写一段话:

<text>
  这是文章详情页面
</text>

然后再app.json中加上一段post-detail的信息配置,每当我们新增页面都需要如此进行配置:

"pages/posts/post-detail/post-detail"

回到post.js中,编写页面跳转代码:

  onPostTap: function(event){
    // 从事件源中获取postId数据
    var postId = event.currentTarget.dataset.postid;
    wx.navigateTo({
      url: 'post-detail/post-detail',
    })
  },

完成以上操作编译之后现在点击文章列表中的文章就可以跳转到文章详情页面了:
第三个页面:构建新闻详情页面


先静后动,先构建新闻详情页面样式

编写post-detail.wxml代码:

<view class='container'>
  <image src='/images/post/sls.jpg' class='head-iamge'></image>
  <image class='audio' src='/images/music/music-start.png'></image>
  <view class='author-date'>
    <image src='/images/avatar/2.png' class='avatar' ></image>
    <text class='author'>zero</text>
    <text class='const-text'>发表于</text>
    <text class='date'>3天前</text>
  </view>
  <text class='title'>审美的进化机制</text>
  <view class='tool'>
    <view class='circle-img'>
      <image src='/images/icon/collection.png'></image>
      <image class='share-img' src='/images/icon/share.png'></image>
    </view>
    <view class='horizon'></view>
  </view>
  <text class='detail'>2017年读的书中,有一本书是《纽约时报》年度十佳(2017 Ten Best Books),即:《美的进化——达尔文被遗忘的择偶理论如何塑造动物世界乃至我们》(The Evolution of Beauty: How Darwin’s Forgotten Theory of Mate Choice Shapes the Animal World — and Us)。时报网站刊登了英文版评介的译文:如果一本科学书籍能做到有颠覆性,倡导女权主义,还能改变我们看待自己身体的方式,但同时主要还是关于鸟类的,那就是这本书了。普鲁姆是一位鸟类学家,他为达尔文的另一个基本上被忽略了的雌雄淘汰理论进行了辩护。</text>
</view>

post-detail.wxss代码:

.container {
  display: flex;
  flex-direction: column;
}

.head-iamge {
  width: 750rpx;
  height: 460rpx;
}

.author-date {
  flex-direction: row;
  margin-left: 30rpx;
  margin-top: 20rpx;
}

.avatar {
  height: 64rpx;
  width: 64rpx;
  vertical-align: middle;
}

.author {
  font-size: 30rpx;
  font-weight: 300;
  margin-left: 20rpx;
  vertical-align: middle;
  color: #666;
}

.const-text {
  font-size: 24rpx;
  color: #999;
  margin-left: 20rpx;
}

.date {
  font-size: 24rpx;
  margin-left: 30rpx;
  color: #999;
  vertical-align: middle;
}

.title {
  margin-left: 40rpx;
  font-size: 36rpx;
  font-weight: 700;
  margin-top: 30rpx;
  letter-spacing: 2px;
  color: #4b556c;
}

.tool {
  margin-top: 20rpx;
}

.circle-img {
  float: right;
  margin-right: 40rpx;
  vertical-align: middle;
}
.circle-img image{
  width: 90rpx;
  height: 90rpx;
}
.share-img {
  margin-left: 30rpx;
}

.horizon {
  width: 660rpx;
  height: 1px;
  background-color: #e5e5e5;
  vertical-align: middle;
  position: relative;
  top: 46rpx;
  margin: 0 auto;
  z-index: -99;
}

.detail{
  color: #666;
  margin-left: 30rpx;
  margin-top: 20rpx;
  margin-right: 30rpx;
  line-height: 44rpx;
  letter-spacing: 2px;
}

.audio{
  width: 102rpx;
  height: 110rpx;
  position: absolute;
  left: 50%;
  margin-left: -51rpx;
  top: 180rpx;
  opacity: 0.6;  // 设置透明度为0.6
}

app.wxss代码:

text{
  font-family: MicroSoft Yahei;
  font-size: 24rpx;
}

完成效果:
第三个页面:构建新闻详情页面


配置全局导航栏颜色

上面把基本的静态页面做完了,但是导航栏颜色还不太对,而且少了个标识文字,所以现在就来把这个位置的样式完善好。
app.json文件内容:

{
  "pages": [
    "pages/welcome/welcome",
    "pages/posts/post",
    "pages/posts/post-detail/post-detail"
  ],
  "window": {
    "navigationBarBackgroundColor": "#405f80"
  }
}

welcome.json文件内容:

{
  "navigationBarBackgroundColor": "#b3d4db"
}

post.json文件内容:

{
    "navigationBarTitleText": "文与字"
}

post-detail.json文件内容:

{
  "navigationBarTitleText": "阅读"
}

完成效果:
第三个页面:构建新闻详情页面


使用数据填充新闻详情页面

首先是postId的获得,因为不同的postId需要输出不同的文章详情数据:
1.在post.js的navigateTo方法的url参数中,加上一个id参数:

onPostTap: function(event){
    // 从事件源中获取postId数据
    var postId = event.currentTarget.dataset.postid;
    //console.log("on post id is " + postId);
    wx.navigateTo({
      url: 'post-detail/post-detail?id=' + postId,  // 通过url传递postId
    })
  },

2.然后在post-detail.js中,写一个onLoad函数把id获得到手:

Page({
  onLoad:function(option){
    var postId = option.id; // 这里的id对应的是url参数上的id
    console.log(postId);
  }
})

3.编译之后,点击不同的文章,看看控制台中是否有输出相应的postId,有输出的话就证明获得成功。

完成以上操作后,就可以把新闻详情页面的数据放进数据文件中,然后进行数据绑定:
post-data.js文件内容:

// 将数据整合成数组类型
var local_database = [
  {
    date: "Jan 06 2018",
    title: "正是虾肥蟹壮时",
    imgSrc: "/images/post/crab.png",
    avatar: "/images/avatar/1.png",
    content: "“山明水净夜来霜,数树深红出浅黄。试上高楼清入骨,岂如春色嗾人狂。”金秋时节,天高云淡,秋风送爽,气候宜人。秋风秋阳中,硕果坠挂枝头,玉米抚须含笑,高粱引颈高歌,豆荚饱满圆润。",
    reading: "112",
    collection: "96",
    headImgSrc: "/images/post/crab.png",
    author: "zero",
    dataTime:"24小时前",
    detail: "“山明水净夜来霜,数树深红出浅黄。试上高楼清入骨,岂如春色嗾人狂。”金秋时节,天高云淡,秋风送爽,气候宜人。秋风秋阳中,硕果坠挂枝头,玉米抚须含笑,高粱引颈高歌,豆荚饱满圆润。棉桃鼓胀欲裂,水稻灌浆初熟,世间万物经过春的孕育,夏的生长,即将抵达收获的季节。在这瓜果飘香、稻黍起舞的召唤声中,又是一度蟹肥虾壮时。地处黄海之滨的小城,在秋风的抚慰、秋阳的光照下,瞬间也喧嚣起来。任意走进城中的每一个菜市场,在显眼的位置上,冲入耳际的是此起彼伏的吆喝声,映入眼帘的是那些小商小贩们抢占有利地势将一只只塑料箱一字排开的情景。浅箱中,健壮的对虾、竹节虾在水中跳跃,舒展着弯曲的身体;深箱中,一贯横行霸道的螃蟹拥挤在狭小的空间里,相互肆意践踏,有些不甘蜗居的螃蟹,顺着笔直的箱壁艰难地攀爬着,虽经百般努力,终以失败而告终。那些聚集在网兜里的螃蟹,更是不甘寂寞,身体被束缚着无法动弹,便利用可以自由呼吸的嘴巴,于窸窸窣窣中不停地吐着一串串泡沫,以示抗议,也以此证明自己是个活物。特别是那些个头较大的螃蟹,仿佛知道自己的身价不菲,为此,更是气宇轩昂,自以为是。也许,它们是得到垂青和恩宠的一类吧,受到了特别的眷顾,活动的空间相对较大,所以也更加肆无忌惮。只要有人试图靠近,便会举着那两只肥硕的大螯向你示威,仿佛在警告你:别碰我,否则休怪我无礼!",
    postId: 0,
  },
  {
    date: "Jan 03 2018",
    title: "比利·林恩的中场战事",
    imgSrc: "/images/post/bl.png",
    avatar: "/images/avatar/2.png",
    content: "伊拉克战争时期,来自美国德州的19岁技术兵比利·林恩(乔·阿尔文 Joe Alwyn 饰)因为一段偶然拍摄的视频而家喻户晓。那是一次规模不大却激烈非常的遭遇战,战斗中林恩所在的B班班长(范·迪塞尔 Vin Diesel 饰)遭到当地武装分子的伏击和劫持,而林恩为了营救班长不惜铤而走险冲锋陷阵。",
    reading: "92",
    collection: "65",
    headImgSrc: "/images/post/bl.png",
    dataTime: "一天前",
    author: "妮可",
    detail: "伊拉克战争时期,来自美国德州的19岁技术兵比利·林恩(乔·阿尔文 Joe Alwyn 饰)因为一段偶然拍摄的视频而家喻户晓。那是一次规模不大却激烈非常的遭遇战,战斗中林恩所在的B班班长(范·迪塞尔 Vin Diesel 饰)遭到当地武装分子的伏击和劫持,而林恩为了营救班长不惜铤而走险冲锋陷阵。视频公布于世让他成为全美民众所崇拜的英雄,然而却鲜有人理解他和战友们所经历的一切。为了安葬班长,B班得到了短暂的休假,因此他们得以受邀参加一场在德州举行的橄榄球比赛。林恩的姐姐因某事件深感愧疚,她希望弟弟能借此机缘回归普通生活。而周围的经纪人、球迷、大老板、普通民众则对战争、卫国、士兵有着各种各样想当然的理解。球场上的庆典盛大开幕,林恩和战友们的心却愈加沉重与焦躁…… ",
    postId: 1,
  },
  {
    date: "Jan 05 2018",
    title: "肖申克的救赎",
    imgSrc: "/images/post/xs.jpg",
    avatar: "/images/avatar/3.png",
    content: "20世纪40年代末,小有成就的青年银行家安迪(蒂姆·罗宾斯 Tim Robbins 饰)因涉嫌杀害妻子及她的情人而锒铛入狱。在这座名为肖申克的监狱内,希望似乎虚无缥缈,终身监禁的惩罚无疑注定了安迪接下来灰暗绝望的人生。",
    reading: "92",
    collection: "65",
    headImgSrc: "/images/post/xs.jpg",
    dataTime: "两天前",
    author: "John",
    detail: "20世纪40年代末,小有成就的青年银行家安迪(蒂姆·罗宾斯 Tim Robbins 饰)因涉嫌杀害妻子及她的情人而锒铛入狱。在这座名为肖申克的监狱内,希望似乎虚无缥缈,终身监禁的惩罚无疑注定了安迪接下来灰暗绝望的人生。未过多久,安迪尝试接近囚犯中颇有声望的瑞德(摩根·弗里曼 Morgan Freeman 饰),请求对方帮自己搞来小锤子。以此为契机,二人逐渐熟稔,安迪也仿佛在鱼龙混杂、罪恶横生、黑白混淆的牢狱中找到属于自己的求生之道。他利用自身的专业知识,帮助监狱管理层逃税、洗黑钱,同时凭借与瑞德的交往在×××中间也渐渐受到礼遇。表面看来,他已如瑞德那样对那堵高墙从憎恨转变为处之泰然,但是对自由的渴望仍促使他朝着心中的希望和目标前进。而关于其罪行的真相,似乎更使这一切朝前推进了一步…… ",
    postId: 2,
  },
  {
    date: "Jan 01 2018",
    title: "霸王别姬",
    imgSrc: "/images/post/bj.jpg",
    avatar: "/images/avatar/4.png",
    content: "段小楼(张丰毅)与程蝶衣(张国荣)是一对打小一起长大的师兄弟,两人一个演生,一个饰旦,一向配合天衣无缝,尤其一出《霸王别姬》,更是誉满京城,为此,两人约定合演一辈子《霸王别姬》。但两人对戏剧与人生关系的理解有本质不同,段小楼深知戏非人生,程蝶衣则是人戏不分。",
    reading: "92",
    collection: "65",
    headImgSrc: "/images/post/bj.jpg",
    dataTime: "三天前",
    author: "Jack",
    detail: "段小楼(张丰毅)与程蝶衣(张国荣)是一对打小一起长大的师兄弟,两人一个演生,一个饰旦,一向配合天衣无缝,尤其一出《霸王别姬》,更是誉满京城,为此,两人约定合演一辈子《霸王别姬》。但两人对戏剧与人生关系的理解有本质不同,段小楼深知戏非人生,程蝶衣则是人戏不分。段小楼在认为该成家立业之时迎娶了名妓菊仙(巩俐),致使程蝶衣认定菊仙是可耻的第三者,使段小楼做了叛徒,自此,三人围绕一出《霸王别姬》生出的爱恨情仇战开始随着时代风云的变迁不断升级,终酿成悲剧。",
    postId: 3,
  },
  {
    date: "Jan 08 2018",
    title: "这个杀手不太冷",
    imgSrc: "/images/post/ss.jpg",
    avatar: "/images/avatar/5.png",
    content: "里昂(让·雷诺饰)是名孤独的×××,受人雇佣。一天,邻居家小姑娘马蒂尔达(纳塔丽·波特曼饰)敲开他的房门,要求在他那里暂避杀身之祸。原来邻居家的主人是警方缉毒组的眼线,只因贪污了一小包×××而遭恶警(加里·奥德曼饰)杀害全家的惩罚。",
    reading: "92",
    collection: "65",
    headImgSrc: "/images/post/ss.jpg",
    dataTime: "四天前",
    author: "Bill",
    detail: "里昂(让·雷诺饰)是名孤独的×××,受人雇佣。一天,邻居家小姑娘马蒂尔达(纳塔丽·波特曼饰)敲开他的房门,要求在他那里暂避杀身之祸。原来邻居家的主人是警方缉毒组的眼线,只因贪污了一小包×××而遭恶警(加里·奥德曼饰)杀害全家的惩罚。马蒂尔达得到里昂的留救,幸免于难,并留在里昂那里。里昂教小女孩使枪,她教里昂法文,两人关系日趋亲密,相处融洽。女孩想着去×××,反倒被抓,里昂及时赶到,将女孩救回。混杂着哀怨情仇的正邪之战渐次升级,更大的冲突在所难免…… ",
    postId: 4,
  },
  {
    date: "Jan 04 2018",
    title: "阿甘正传",
    imgSrc: "/images/post/ag.jpg",
    avatar: "/images/avatar/1.png",
    content: "阿甘(汤姆·汉克斯 饰)于二战结束后不久出生在美国南方阿拉巴马州一个闭塞的小镇,他先天弱智,智商只有75,然而他的妈妈是一个性格坚强的女性,她常常鼓励阿甘“傻人有傻福”,要他自强不息。",
    reading: "92",
    collection: "65",
    headImgSrc: "/images/post/ag.jpg",
    dataTime: "五天前",
    author: "Tony",
    detail: "阿甘(汤姆·汉克斯 饰)于二战结束后不久出生在美国南方阿拉巴马州一个闭塞的小镇,他先天弱智,智商只有75,然而他的妈妈是一个性格坚强的女性,她常常鼓励阿甘“傻人有傻福”,要他自强不息。阿甘像普通孩子一样上学,并且认识了一生的朋友和至爱珍妮(罗宾·莱特·潘 饰),在珍妮和妈妈的爱护下,阿甘凭着上帝赐予的“飞毛腿”开始了一生不停的奔跑。阿甘成为橄榄球巨星、越战英雄、乒乓球外交使者、亿万富翁,但是,他始终忘不了珍妮,几次匆匆的相聚和离别,更是加深了阿甘的思念。有一天,阿甘收到珍妮的信,他们终于又要见面了……",
    postId: 5,
  },
]

// 设置一个数据出口
module.exports = {
  // 输出的是一个Array对象
  postList: local_database,
}

post-detail.js文件内容:

var postsData = require('../../../data/posts-data.js')

Page({
  onLoad:function(option){
    var postId = option.id; // 这里的id对应的是url参数上的id
    var postData=postsData.postList[postId];
    this.setData({
        postData
    });
  }
})

post-detail.wxml文件内容:

<view class='container'>
  <image src='{{postData.headImgSrc}}' class='head-iamge'></image>
  <image class='audio' src='/images/music/music-start.png'></image>
  <view class='author-date'>
    <image src='{{postData.avatar}}' class='avatar' ></image>
    <text class='author'>{{postData.author}}</text>
    <text class='const-text'>发表于</text>
    <text class='date'>{{postData.dataTime}}</text>
  </view>
  <text class='title'>{{postData.title}}</text>
  <view class='tool'>
    <view class='circle-img'>
      <image src='/images/icon/collection.png'></image>
      <image class='share-img' src='/images/icon/share.png'></image>
    </view>
    <view class='horizon'></view>
  </view>
  <text class='detail'>{{postData.detail}}</text>
</view>

运行效果:
第三个页面:构建新闻详情页面


缓存Storage的基本用法

在文章详情页中我们需要实现一个文章收藏的功能,由于我们没有使用到服务器,所以使用本地缓存来记录这个文章是否被用户收藏的一个状态。

小程序中提供了一个setStorageSync方法来实现缓存,从方法名也可以看出这个方法是带有同步的。除此之外还有一个异步的缓存方法setStorage,这个方法可以用于异步缓存数据。

注:和缓存相关的方法,例如得到缓存数据、删除缓存数据等方法,都有同步和异步两个,方法名末尾有Sync的表示同步,否则是异步。

首先演示一下setStorageSync方法的使用方式:

// 第一个参数是键,第二个参数则是需要存储的数据
wx.setStorageSync('key', "Test");

我在post-detail.js文件中的onLoad方法里加入了以上这段代码,此时我点击进入文章详情页面,就会缓存这个数据,缓存数据在Storage界面查看:
第三个页面:构建新闻详情页面

在小程序中,如果用户不去主动清除缓存数据,那么数据就会一直存在,所以现在即便我关闭开发工具或者重新进行编译,这个数据都会存在,除非我主动删掉它。

通过键可以改变缓存数据:

wx.setStorageSync('key', {
      game:"eat chicken",
      developer:"LD",
});

运行结果:
第三个页面:构建新闻详情页面

获取缓存的数据:

<!-- 在收藏图标上加上一个事件 -->
<image catchtap='onCollectionTap' src='/images/icon/collection.png'></image>

使用getStorageSync方法即可得到缓存数据:

onCollectionTap:function(event){
    var game=wx.getStorageSync('key');
    console.log(game);
}

运行结果:
第三个页面:构建新闻详情页面

删除缓存数据:

<!-- 在分享图标上加上一个事件 -->
<image catchtap='onShareTap' class='share-img' src='/images/icon/share.png'></image>

使用removeStorageSync方法即可删除缓存数据:

onShareTap:function(event){
    wx.removeStorageSync('key');
},

运行结果,可以看到数据已经不存在了:
第三个页面:构建新闻详情页面

删除所有缓存数据的方法:

wx.clearStorageSync();

注:小程序规定缓存数据的大小上限是10MB


使用缓存实现文章收藏功能

实现这个功能我们需要使用到两个图标进行状态的轮换,由于小程序中没有document,我们需要使用if判断来实现这个功能。

post-detail.wxml代码如下:

<!-- 判断collected的值是否为真,是的话就显示src中指定的图片 -->
<image wx:if="{{collected}}" catchtap='onCollectionTap' src='/images/icon/collection.png'></image>
<!-- 否则显示这张图片 -->
<image wx:else catchtap='onCollectionTap' src='/images/icon/collection-anti.png'></image>

post-detail.js代码如下:

var postsData = require('../../../data/posts-data.js')

Page({
  data:{

  },
  onLoad: function (option) {
    var postId = option.id; // 这里的id对应的是url参数上的id
    // 把postId设置到数据集里,这样就能够全局获取
    this.data.currentPostId = postId;
    var postData = postsData.postList[postId];
    this.setData({
      postData
    });

    // 从缓存中获取数据,键值对形式的
    var postsCollected = wx.getStorageSync('posts_collected');
    // 判断数据是否不为空
    if (postsCollected) {
      // 不为空就拿出与postId对应的下标值
      var postsCollected = postsCollected[postId];
      // 并将值更新到数据绑定里
      this.setData({
        collected: postsCollected
      });
    } else {
      // 如果为空就赋值一个空对象
      var postsCollected = {}
      // 并把与postId对应的下标中的值设置为false
      postsCollected[postId] = false;
      // 更新到缓存里
      wx.setStorageSync('posts_collected', postsCollected);
    }
  },

  // 点击事件方法
  onCollectionTap: function (event) {
    // 获取缓存数据
    var postsCollected = wx.getStorageSync('posts_collected');
    // 从数据集中获取postId
    var postCollected = postsCollected[this.data.currentPostId];
    // 收藏变成未收藏,未收藏变成收藏
    postCollected = !postCollected;
    postsCollected[this.data.currentPostId] = postCollected;
    // 更新文章是否收藏的缓存值
    wx.setStorageSync('posts_collected', postsCollected);
    // 更新数据绑定变量,从而实现切换图片
    this.setData({
      collected: postCollected
    });
  },

})

运行效果:

未收藏状态:
第三个页面:构建新闻详情页面

收藏状态:
第三个页面:构建新闻详情页面


交互反馈 wx.showToast

以上我们实现了收藏和未收藏图标的一个轮换功能,但是还缺少了个提示功能,在用户点击收藏时要提示用户收藏成功,再次点击则需要提示用户取消成功。

小程序提供了几个实现交互反馈功能的API,详情参考以下官方文档:

https://mp.weixin.qq.com/debug/wxadoc/dev/api/api-react.html

我们需要使用到其中三个API,分别是wx.showToast、wx.showModal、wx.showActionSheet。

首先来应用wx.showToast这个API,在事件方法中,加入如下代码:

wx.showToast({
      // 使用三元表达式来判断状态
      title: postCollected ? '收藏成功!' :'取消成功!',
      // 设置图标停留的时间,单位是毫秒
      duration: 1000,
      // icon可以设置图标,默认就是success
      icon: "success",
});

运行效果:
第三个页面:构建新闻详情页面
第三个页面:构建新闻详情页面


操作反馈wx.showModal

wx.showModal可以显示模态弹窗,我们可以把wx.showModal与wx.showToast相结合使用。

修改代码如下:

var postsData = require('../../../data/posts-data.js')

Page({
  data: {

  },
  onLoad: function (option) {
    var postId = option.id; // 这里的id对应的是url参数上的id
    // 把postId设置到数据集里,这样就能够全局获取
    this.data.currentPostId = postId;
    var postData = postsData.postList[postId];
    this.setData({
      postData
    });

    // 从缓存中获取数据
    var postsCollected = wx.getStorageSync('posts_collected');
    // 判断数据是否不为空
    if (postsCollected) {
      // 不为空就拿出与postId对应的下标值
      var postsCollected = postsCollected[postId];
      // 并将值更新到数据绑定里
      this.setData({
        collected: postsCollected
      });
    } else {
      // 如果为空就赋值一个空对象
      var postsCollected = {}
      // 并把与postId对应的下标中的值设置为false
      postsCollected[postId] = false;
      // 更新到缓存里
      wx.setStorageSync('posts_collected', postsCollected);
    }
  },

  // 点击事件方法
  onCollectionTap: function (event) {
    // 获取缓存数据
    var postsCollected = wx.getStorageSync('posts_collected');
    // 获取postId
    var postCollected = postsCollected[this.data.currentPostId];
    // 收藏变成未收藏,未收藏变成收藏
    postCollected = !postCollected;
    postsCollected[this.data.currentPostId] = postCollected;

    // 自定义函数也需要使用this来访问
    this.showModal(postsCollected, postCollected);
  },

  showModal: function (postsCollected, postCollected){
    // 把this指代的Page对象先存储起来
    var that = this;
    wx.showModal({
      title: '收藏',
      content: postCollected ? '是否收藏该文章?' :'是否取消收藏该文章?',
      showCancel: 'true',
      cancelText: '取消',
      cancelColor: '#333',
      confirmText: '确认',
      confirmColor: '#405f80',
      success:function(res){
        if(res.confirm){
          // 更新文章是否收藏的缓存值
          wx.setStorageSync('posts_collected', postsCollected);
          // 更新数据绑定变量,从而实现切换图片
          that.setData({
            collected: postCollected
          });
          that.showToast(postsCollected, postCollected);
        }
      },
    });
  },

  showToast: function (postsCollected, postCollected){
    wx.showToast({
      // 使用三元表达式来判断状态
      title: postCollected ? '收藏成功!' : '取消成功!',
      // 设置图标停留的时间,单位是毫秒
      duration: 1000,
      // icon可以设置图标,默认就是success
      icon: "success",
    });
  },
})

运行效果:
收藏:
第三个页面:构建新闻详情页面
第三个页面:构建新闻详情页面

取消收藏:
第三个页面:构建新闻详情页面
第三个页面:构建新闻详情页面

注:在实际开发中这种成本低的操作是不需要把交互反馈做得这么麻烦的,一般只使用showToast即可。所谓成本指的是误操作带来的损失,如果成本低的操作交互反馈太麻烦的话,会感觉体验不好。


交互反馈wx.showActionSheet

​showActionSheet可以显示操作菜单,以下使用实际示例演示一下showActionSheet的使用:

1.在分享图标上加上一个点击事件:

<image catchtap='onShareTap' class='share-img' src='/images/icon/share.png'></image>

2.事件代码如下:

onShareTap:function(event){
    var itemList = [
      "分享给微信好友",
      "分享到朋友圈",
      "分享到QQ",
      "分享到微博",
    ]
    wx.showActionSheet({
      itemList: itemList,  // 按钮的文字数组,数组长度最大为6个
      itemColor:"#405f80",  // 设置字体颜色
      success:function(res){
        //res.tapIndex  用户点击的按钮,从上到下的顺序,从0开始
        wx.showModal({
          title: itemList[res.tapIndex],
          content: '现在无法实现分享功能,什么时候能支持还未知',
        })
      }, 
    })
  },

运行效果:
第三个页面:构建新闻详情页面
第三个页面:构建新闻详情页面

注:到目前为止,微信小程序官方还尚未提供能够将小程序直接分享到朋友圈的相关api,不过有一些曲线救国的方案,可以参考以下两篇文章,或者使用百度、谷歌等搜索引擎搜索解决方案:

https://segmentfault.com/a/1190000012316892
http://blog.csdn.net/baozhuona/article/details/78570483


同步异步方法对比

我们把之前的onCollectionTap方法中的同步获取缓存的方法改为异步获取缓存的方法,以此来演示同步与异步方法之间的区别,修改代码如下:

onCollectionTap: function (event) {
    // 把当前this指代的当前对象先存储起来
    var that = this;
    // 异步获取缓存数据
    var postsCollected = wx.getStorage({
      key: 'posts_collected',
      success: function (res) {
        var postsCollected = res.data;
        // 获取postId
        var postCollected = postsCollected[that.data.currentPostId];
        // 收藏变成未收藏,未收藏变成收藏
        postCollected = !postCollected;
        postsCollected[that.data.currentPostId] = postCollected;

        that.showModal(postsCollected, postCollected);
      },
    });
  },

以上就是异步方法的实现方式,与同步方法的主要区别在于,同步会等待wx.getStorageSync('posts_collected');方法执行完之后才会往下执行,所以如果当获取缓存得很慢的时候,操作界面就会卡在那。而异步则不会,异步获取缓存数据的时候,代码还会继续往下执行,异步获取完成之后再执行success里的方法。

注:通常情况下,在小程序中必须要使用异步方法的情况比较少,建议如果对异步方法不熟悉的话,最好不要使用异步方法,不然不仅会让你的代码变得难以阅读,而且很容易埋下一些隐藏bug,或者难以解决的错误。至于使用异步还是同步,需要根据业务来决定,当可以使用同步的情况下,优先使用同步。


音乐播放基本实现

以上我们已经完成了文章详情页的大部分内容,现在还剩一个音乐播放功能还未实现,官方也提供了一个audio组件可以实现音乐播放。除了组件之外还有相关的API可以使用,在这里我们使用API来实现音乐播放功能,因为使用API的方式比较方便于自定义。

官方文档:

API:https://mp.weixin.qq.com/debug/wxadoc/dev/api/media-voice.html
组件:https://mp.weixin.qq.com/debug/wxadoc/dev/component/audio.html

我们需要使用到两个API,playBackgroundAudio以及pauseBackgroundAudio。前者是用于音乐的播放,后者是用于音乐的暂停。

先给音乐图标添加一个事件,并且使用三元运算符来判断图标是显示暂停图标还是启动图标:

<image catchtap='onMusicTap' class='audio' src='{{isPlayingMusic ? "/images/music/music-stop.png" : "/images/music/music-start.png"}}'></image>

事件方法实现代码如下:

data: {
    isPlayingMusic: false
},

onMusicTap: function (event) {
    // 获取数据文件中的数据
    var currentPostId = this.data.currentPostId;
    var postData = postsData.postList[currentPostId];

    // 使用变量来记录音乐的状态
    var isPlayingMusic = this.data.isPlayingMusic;
    if (isPlayingMusic) {
      // 暂停音乐
      wx.pauseBackgroundAudio();
      // 改变状态
      this.setData({
        isPlayingMusic : false
      });

    } else {
      wx.playBackgroundAudio({
        // 流媒体文件的URL,目前支持的格式有 m4a, aac, mp3, wav
        dataUrl: postData.music.url,
        // 音乐标题
        title: postData.music.title,
        // 音乐封面URL
        coverImgUrl: postData.music.coverImg,
      })
      // 改变状态
      this.setData({
        isPlayingMusic: true
      });
    }
  },

注:dataUrl只能够是引用流媒体文件,不能够使用本地的音乐文件,coverImgUrl也是如此,因为小程序的大小限制是1M,一个音乐文件都不止1M了,所以只能使用流媒体文件的URL形式引入音乐。coverImgUrl引入的图片只有在真机上才能看到得到。

数据文件内容如下,增加了音乐连接、音乐标题、音乐图片属性:

// 将数据整合成数组类型
var local_database = [
  {
    date: "Jan 06 2018",
    title: "正是虾肥蟹壮时",
    imgSrc: "/images/post/crab.png",
    avatar: "/images/avatar/1.png",
    content: "“山明水净夜来霜,数树深红出浅黄。试上高楼清入骨,岂如春色嗾人狂。”金秋时节,天高云淡,秋风送爽,气候宜人。秋风秋阳中,硕果坠挂枝头,玉米抚须含笑,高粱引颈高歌,豆荚饱满圆润。",
    reading: "112",
    collection: "96",
    headImgSrc: "/images/post/crab.png",
    author: "zero",
    dataTime:"24小时前",
    detail: "“山明水净夜来霜,数树深红出浅黄。试上高楼清入骨,岂如春色嗾人狂。”金秋时节,天高云淡,秋风送爽,气候宜人。秋风秋阳中,硕果坠挂枝头,玉米抚须含笑,高粱引颈高歌,豆荚饱满圆润。棉桃鼓胀欲裂,水稻灌浆初熟,世间万物经过春的孕育,夏的生长,即将抵达收获的季节。在这瓜果飘香、稻黍起舞的召唤声中,又是一度蟹肥虾壮时。地处黄海之滨的小城,在秋风的抚慰、秋阳的光照下,瞬间也喧嚣起来。任意走进城中的每一个菜市场,在显眼的位置上,冲入耳际的是此起彼伏的吆喝声,映入眼帘的是那些小商小贩们抢占有利地势将一只只塑料箱一字排开的情景。浅箱中,健壮的对虾、竹节虾在水中跳跃,舒展着弯曲的身体;深箱中,一贯横行霸道的螃蟹拥挤在狭小的空间里,相互肆意践踏,有些不甘蜗居的螃蟹,顺着笔直的箱壁艰难地攀爬着,虽经百般努力,终以失败而告终。那些聚集在网兜里的螃蟹,更是不甘寂寞,身体被束缚着无法动弹,便利用可以自由呼吸的嘴巴,于窸窸窣窣中不停地吐着一串串泡沫,以示抗议,也以此证明自己是个活物。特别是那些个头较大的螃蟹,仿佛知道自己的身价不菲,为此,更是气宇轩昂,自以为是。也许,它们是得到垂青和恩宠的一类吧,受到了特别的眷顾,活动的空间相对较大,所以也更加肆无忌惮。只要有人试图靠近,便会举着那两只肥硕的大螯向你示威,仿佛在警告你:别碰我,否则休怪我无礼!",
    postId: 0,
    music: {
      url: "http://ws.stream.qqmusic.qq.com/C100003507bR0gDKBm.m4a?fromtag=38",
      title: "夜夜夜夜-齐秦",
      coverImg: "http://y.gtimg.cn/music/photo_new/T002R150x150M000001TEc6V0kjpVC.jpg?max_age=2592000"
    }
  },
  {
    date: "Jan 03 2018",
    title: "比利·林恩的中场战事",
    imgSrc: "/images/post/bl.png",
    avatar: "/images/avatar/2.png",
    content: "伊拉克战争时期,来自美国德州的19岁技术兵比利·林恩(乔·阿尔文 Joe Alwyn 饰)因为一段偶然拍摄的视频而家喻户晓。那是一次规模不大却激烈非常的遭遇战,战斗中林恩所在的B班班长(范·迪塞尔 Vin Diesel 饰)遭到当地武装分子的伏击和劫持,而林恩为了营救班长不惜铤而走险冲锋陷阵。",
    reading: "92",
    collection: "65",
    headImgSrc: "/images/post/bl.png",
    dataTime: "一天前",
    author: "妮可",
    detail: "伊拉克战争时期,来自美国德州的19岁技术兵比利·林恩(乔·阿尔文 Joe Alwyn 饰)因为一段偶然拍摄的视频而家喻户晓。那是一次规模不大却激烈非常的遭遇战,战斗中林恩所在的B班班长(范·迪塞尔 Vin Diesel 饰)遭到当地武装分子的伏击和劫持,而林恩为了营救班长不惜铤而走险冲锋陷阵。视频公布于世让他成为全美民众所崇拜的英雄,然而却鲜有人理解他和战友们所经历的一切。为了安葬班长,B班得到了短暂的休假,因此他们得以受邀参加一场在德州举行的橄榄球比赛。林恩的姐姐因某事件深感愧疚,她希望弟弟能借此机缘回归普通生活。而周围的经纪人、球迷、大老板、普通民众则对战争、卫国、士兵有着各种各样想当然的理解。球场上的庆典盛大开幕,林恩和战友们的心却愈加沉重与焦躁…… ",
    postId: 1,
    music: {
      url: "http://ws.stream.qqmusic.qq.com/C100003GdCmG4NkEOR.m4a?fromtag=38",
      title: "鬼迷心窍-李宗盛",
      coverImg: "http://y.gtimg.cn/music/photo_new/T002R150x150M000002xOmp62kqSic.jpg?max_age=2592000"
    }
  },
  {
    date: "Jan 05 2018",
    title: "肖申克的救赎",
    imgSrc: "/images/post/xs.jpg",
    avatar: "/images/avatar/3.png",
    content: "20世纪40年代末,小有成就的青年银行家安迪(蒂姆·罗宾斯 Tim Robbins 饰)因涉嫌杀害妻子及她的情人而锒铛入狱。在这座名为肖申克的监狱内,希望似乎虚无缥缈,终身监禁的惩罚无疑注定了安迪接下来灰暗绝望的人生。",
    reading: "92",
    collection: "65",
    headImgSrc: "/images/post/xs.jpg",
    dataTime: "两天前",
    author: "John",
    detail: "20世纪40年代末,小有成就的青年银行家安迪(蒂姆·罗宾斯 Tim Robbins 饰)因涉嫌杀害妻子及她的情人而锒铛入狱。在这座名为肖申克的监狱内,希望似乎虚无缥缈,终身监禁的惩罚无疑注定了安迪接下来灰暗绝望的人生。未过多久,安迪尝试接近囚犯中颇有声望的瑞德(摩根·弗里曼 Morgan Freeman 饰),请求对方帮自己搞来小锤子。以此为契机,二人逐渐熟稔,安迪也仿佛在鱼龙混杂、罪恶横生、黑白混淆的牢狱中找到属于自己的求生之道。他利用自身的专业知识,帮助监狱管理层逃税、洗黑钱,同时凭借与瑞德的交往在×××中间也渐渐受到礼遇。表面看来,他已如瑞德那样对那堵高墙从憎恨转变为处之泰然,但是对自由的渴望仍促使他朝着心中的希望和目标前进。而关于其罪行的真相,似乎更使这一切朝前推进了一步…… ",
    postId: 2,
    music: {
      url: "http://ws.stream.qqmusic.qq.com/C100004HLusI2lLjZy.m4a?fromtag=38",
      title: "女儿情-万晓利",
      coverImg: "http://y.gtimg.cn/music/photo_new/T002R150x150M000004Wv5BO30pPc0.jpg?max_age=2592000"
    }
  },
  {
    date: "Jan 01 2018",
    title: "霸王别姬",
    imgSrc: "/images/post/bj.jpg",
    avatar: "/images/avatar/4.png",
    content: "段小楼(张丰毅)与程蝶衣(张国荣)是一对打小一起长大的师兄弟,两人一个演生,一个饰旦,一向配合天衣无缝,尤其一出《霸王别姬》,更是誉满京城,为此,两人约定合演一辈子《霸王别姬》。但两人对戏剧与人生关系的理解有本质不同,段小楼深知戏非人生,程蝶衣则是人戏不分。",
    reading: "92",
    collection: "65",
    headImgSrc: "/images/post/bj.jpg",
    dataTime: "三天前",
    author: "Jack",
    detail: "段小楼(张丰毅)与程蝶衣(张国荣)是一对打小一起长大的师兄弟,两人一个演生,一个饰旦,一向配合天衣无缝,尤其一出《霸王别姬》,更是誉满京城,为此,两人约定合演一辈子《霸王别姬》。但两人对戏剧与人生关系的理解有本质不同,段小楼深知戏非人生,程蝶衣则是人戏不分。段小楼在认为该成家立业之时迎娶了名妓菊仙(巩俐),致使程蝶衣认定菊仙是可耻的第三者,使段小楼做了叛徒,自此,三人围绕一出《霸王别姬》生出的爱恨情仇战开始随着时代风云的变迁不断升级,终酿成悲剧。",
    postId: 3,
    music: {
      url: "http://ws.stream.qqmusic.qq.com/C100002mWVx72p8Ugp.m4a?fromtag=38",
      title: "恋恋风尘-老狼",
      coverImg: "http://y.gtimg.cn/music/photo_new/T002R150x150M000001VaXQX1Z1Imq.jpg?max_age=2592000",
    }
  },
  {
    date: "Jan 08 2018",
    title: "这个杀手不太冷",
    imgSrc: "/images/post/ss.jpg",
    avatar: "/images/avatar/5.png",
    content: "里昂(让·雷诺饰)是名孤独的×××,受人雇佣。一天,邻居家小姑娘马蒂尔达(纳塔丽·波特曼饰)敲开他的房门,要求在他那里暂避杀身之祸。原来邻居家的主人是警方缉毒组的眼线,只因贪污了一小包×××而遭恶警(加里·奥德曼饰)杀害全家的惩罚。",
    reading: "92",
    collection: "65",
    headImgSrc: "/images/post/ss.jpg",
    dataTime: "四天前",
    author: "Bill",
    detail: "里昂(让·雷诺饰)是名孤独的×××,受人雇佣。一天,邻居家小姑娘马蒂尔达(纳塔丽·波特曼饰)敲开他的房门,要求在他那里暂避杀身之祸。原来邻居家的主人是警方缉毒组的眼线,只因贪污了一小包×××而遭恶警(加里·奥德曼饰)杀害全家的惩罚。马蒂尔达得到里昂的留救,幸免于难,并留在里昂那里。里昂教小女孩使枪,她教里昂法文,两人关系日趋亲密,相处融洽。女孩想着去×××,反倒被抓,里昂及时赶到,将女孩救回。混杂着哀怨情仇的正邪之战渐次升级,更大的冲突在所难免…… ",
    postId: 4,
    music: {
      url: "http://ws.stream.qqmusic.qq.com/C100000Zn0vS4fKKo8.m4a?fromtag=38",
      title: "沉默是金-张国荣",
      coverImg: "http://y.gtimg.cn/music/photo_new/T002R150x150M000003at0mJ2YrR2H.jpg?max_age=2592000"
    }
  },
  {
    date: "Jan 04 2018",
    title: "阿甘正传",
    imgSrc: "/images/post/ag.jpg",
    avatar: "/images/avatar/1.png",
    content: "阿甘(汤姆·汉克斯 饰)于二战结束后不久出生在美国南方阿拉巴马州一个闭塞的小镇,他先天弱智,智商只有75,然而他的妈妈是一个性格坚强的女性,她常常鼓励阿甘“傻人有傻福”,要他自强不息。",
    reading: "92",
    collection: "65",
    headImgSrc: "/images/post/ag.jpg",
    dataTime: "五天前",
    author: "Tony",
    detail: "阿甘(汤姆·汉克斯 饰)于二战结束后不久出生在美国南方阿拉巴马州一个闭塞的小镇,他先天弱智,智商只有75,然而他的妈妈是一个性格坚强的女性,她常常鼓励阿甘“傻人有傻福”,要他自强不息。阿甘像普通孩子一样上学,并且认识了一生的朋友和至爱珍妮(罗宾·莱特·潘 饰),在珍妮和妈妈的爱护下,阿甘凭着上帝赐予的“飞毛腿”开始了一生不停的奔跑。阿甘成为橄榄球巨星、越战英雄、乒乓球外交使者、亿万富翁,但是,他始终忘不了珍妮,几次匆匆的相聚和离别,更是加深了阿甘的思念。有一天,阿甘收到珍妮的信,他们终于又要见面了……",
    postId: 5,
    music: {
      url: "http://ws.stream.qqmusic.qq.com/C100002I8eGJ28BI17.m4a?fromtag=38",
      title: "朋友-谭咏麟",
      coverImg: "http://y.gtimg.cn/music/photo_new/T002R150x150M000004eGsCN3SUheO.jpg?max_age=2592000"
    }
  },
]

// 设置一个数据出口
module.exports = {
  // 输出的是一个Array对象
  postList: local_database,
}

运行效果:
播放:
第三个页面:构建新闻详情页面

暂停:
第三个页面:构建新闻详情页面


切换文章图片

以上我们完成了简单的音乐播放和暂停以及音乐图标的切换,而且也说明了coverImg中引入的图片只有在真机上,进入音乐界面后才能够看到,但是我们也可以将coverImg引入的图片显示在文章详情页上,只需要做一个简单的图片切换即可:

<image src='{{isPlayingMusic ? postData.music.coverImg : postData.headImgSrc}}' class='head-iamge'></image>

运行效果:
播放:
第三个页面:构建新闻详情页面

暂停:
第三个页面:构建新闻详情页面


监听播放事件完善音乐播放

在音乐播放的时候,可以看到会弹出来一个音乐播放的总控开关,我们点击音乐图标的时候能够正常的切换图片,但是点击总控开关的时候不会切换图片,这是因为我们只监听了图标上的事件,没有监听音乐播放、暂停的事件。所以我们还需要完善这点小细节,让点击总控开关的时候也能切换图片,实现这一步需要使用到两个API,onBackgroundAudioPlay以及onBackgroundAudioPause,前者用于监听音乐播放,后者用于监听音乐暂停。

在onLoad生命周期方法中增加以下代码:

    var that = this;
    // 当音乐播放时将isPlayingMusic状态改为true
    wx.onBackgroundAudioPlay(function(){
      that.setData({
        isPlayingMusic: true
      });
    });

    // 当音乐暂停时将isPlayingMusic状态改为false
    wx.onBackgroundAudioPause(function(){
      that.setData({
        isPlayingMusic: false
      });
    })

加入以上代码后就可以实现点击总控开关也能切换图片,而且以上代码也体现出了数据绑定机制的好处,只需要修改数据集中相应数据的值即可,无需每次都去获得节点对象后才能操作相应的数据的值。


应用程序生命周期

文章详情页中的音乐播放功能看起来基本是没什么问题了,不过这也仅限于文章详情这一个页面内而已,如果我点击了播放音乐,然后返回到上一级页面,再点击进入文章详情页的话,页面的状态就会是初始化时的状态,这时音乐图片就不会自动切换到播放状态的图片。这是因为我们的状态代码写在js文件的Page对象里,会受到页面生命周期的影响,当我们返回上一级页面,再点击进入文章详情页时,Page对象会被加载,页面代码就会被重新执行一遍,所以音乐图标的状态就会是初始时的状态。

解决这个问题我们需要用到全局变量来保存状态,全局变量不会受页面生命周期的影响,而且在任何页面中都可以获取到全局变量的值。在小程序中全局变量需要写在app.js文件中,该文件中的代码需要写在App对象里,就像我们的页面代码需要写在Page中一样,Page代表的是一个页面,而App对象则是代表着整个应用程序,该对象的生命周期也就是应用程序的生命周期。以下是该对象的生命周期方法:

App({

  /**
   * 当小程序初始化完成时,会触发 onLaunch(全局只触发一次)
   */
  onLaunch: function () {

  },

  /**
   * 当小程序启动,或从后台进入前台显示,会触发 onShow
   */
  onShow: function (options) {

  },

  /**
   * 当小程序从前台进入后台,会触发 onHide
   */
  onHide: function () {

  },

  /**
   * 当小程序发生脚本错误,或者 api 调用失败时,会触发 onError 并带上错误信息
   */
  onError: function (msg) {

  }
})

继续完善音乐播放

以上我们简单介绍了一下关于页面状态与全局变量以及应用程序生命周期,现在就可以使用全局变量来继续完善音乐播放的功能了:

app.js代码如下:

App({

  globalData:{
    g_isPlayingMusic:false
  },

});

post-detail.js代码如下:

var postsData = require('../../../data/posts-data.js')
// 获得全局的app对象
var app = getApp();

Page({
  data: {
    isPlayingMusic: false
  },

  onLoad: function (option) {

        ......以上代码省略......

    // 当全局变量的状态为播放时,也把页面的状态设置为teur 
    if (app.globalData.g_isPlayingMusic){
      this.setData({
        isPlayingMusic: true
      });
    }

    this.setMusicMonitor();
  },

  // 把监听音乐的事件代码提取出来
  setMusicMonitor:function(){
    var that = this;
    // 当音乐播放时将页面以及全局状态状态改为true
    wx.onBackgroundAudioPlay(function () {
      that.setData({
        isPlayingMusic: true
      });
      app.globalData.g_isPlayingMusic = true;
    });

    // 当音乐暂停时将页面以及全局状态都改为false
    wx.onBackgroundAudioPause(function () {
      that.setData({
        isPlayingMusic: false
      });
      app.globalData.g_isPlayingMusic = false;
    });
  },

              ......以下代码省略......

})

使用全局变量记录状态后,就不会出现之前的问题了,这时我们就可以在页面加载时根据全局变量来设置页面变量的状态。


音乐播放最终章

除了以上的问题之外,我还找到了三个问题,第一个问题是当我们点击音乐播放时,背景图片会切换,但是我们只需要切换当前页面的图片,别的页面不应该也跟着切换,而这个问题就是别的页面也会跟着切换图片,这个问题我们可以通过把页面id存储到全局变量里,根据id来决定是哪个页面才会切换图片,这样就可以解决这个问题。

第二个问题是当我们关闭音乐播放器时,图片不会切换,依旧停留在播放状态,这个问题我们可以通过wx.onBackgroundAudioStop来解决。

第三个问题就是当我们在文章A里播放了音乐,然后到文章B上点击音乐播放器的总控开关时,文章B的图片也会跟着切换,解决这个问题稍微有点麻烦,因为要考虑到点击图标开关和点击音乐播放器开关两种情况,以及回到原本的页面时还需切换图片,我的解决思路是使用一个全局变量记录上一个页面,也就是原始页面的id,通过这个id来决定切不切换图片。

app.js文件内容如下:

App({

  globalData:{
    g_isPlayingMusic:false,
    g_currentMusicPostId:"",
    g_beforeMusicPostId: ""
  },

});

以下是修改后的post-detail.js文件内容:

var postsData = require('../../../data/posts-data.js')
// 获得全局的app对象
var app = getApp();

Page({
  data: {
    isPlayingMusic: false
  },

  onLoad: function (option) {
    var postId = option.id; // 这里的id对应的是url参数上的id
    // 把postId设置到数据集里,这样就能够全局获取
    this.data.currentPostId = postId;
    var postData = postsData.postList[postId];
    this.setData({
      postData
    });

    // 从缓存中获取数据
    var postsCollected = wx.getStorageSync('posts_collected');
    // 判断数据是否不为空
    if (postsCollected) {
      // 不为空就拿出与postId对应的下标值
      var postsCollected = postsCollected[postId];
      // 并将值更新到数据绑定里
      this.setData({
        collected: postsCollected
      });
    } else {
      // 如果为空就赋值一个空对象
      var postsCollected = {}
      // 并把与postId对应的下标中的值设置为false
      postsCollected[postId] = false;
      // 更新到缓存里
      wx.setStorageSync('posts_collected', postsCollected);
    }

    // 音乐在播放时改变状态为true 
    if (app.globalData.g_isPlayingMusic && app.globalData.g_currentMusicPostId === postId){
      this.setData({
        isPlayingMusic: true
      });
    }

    this.setMusicMonitor();
  },

  // 把监听音乐的事件代码提取出来
  setMusicMonitor:function(){
    var that = this;

    // 监听音乐播放
    wx.onBackgroundAudioPlay(function () {
      // 在原始页面触发播放事件时,切换页面图片,并且记录当前文章的id
      if (app.globalData.g_beforeMusicPostId != "" && app.globalData.g_beforeMusicPostId === that.data.currentPostId){
        that.setData({
          isPlayingMusic: true
        });
        app.globalData.g_isPlayingMusic = true;
        app.globalData.g_currentMusicPostId = that.data.currentPostId;
        app.globalData.g_beforeMusicPostId = app.globalData.g_beforeMusicPostId;

      // 产生原始页面时,或者图标开关被点击时,切换页面图片,并且记录当前文章的id
      } else if (app.globalData.g_beforeMusicPostId == "" || that.data.isPlayingMusic){
        that.setData({
          isPlayingMusic: true
        });
        app.globalData.g_isPlayingMusic = true;
        app.globalData.g_currentMusicPostId = that.data.currentPostId;
        app.globalData.g_beforeMusicPostId = that.data.currentPostId;

      // 在非原始页面触发播放事件时,不切换该页面的图片
      } else{
        that.setData({
          isPlayingMusic: false
        });
        app.globalData.g_isPlayingMusic = true;
        app.globalData.g_currentMusicPostId = app.globalData.g_beforeMusicPostId;
      }
    });

    // 当音乐暂停时将页面以及全局状态都改为false,并且把当前文章的id清空
    wx.onBackgroundAudioPause(function () {
      that.setData({
        isPlayingMusic: false
      });
      app.globalData.g_isPlayingMusic = false;
      app.globalData.g_currentMusicPostId = "";
    });

    // 当音乐停止时将页面以及全局状态都改为false,并且把当前文章以及上一篇文章的id清空
    wx.onBackgroundAudioStop(function(){
      that.setData({
        isPlayingMusic: false
      });
      app.globalData.g_isPlayingMusic = false;
      app.globalData.g_currentMusicPostId = "";
      app.globalData.g_beforeMusicPostId = "";
    });
  },

  //点击事件方法
  onCollectionTap: function (event) {
    // 获取缓存数据
    var postsCollected = wx.getStorageSync('posts_collected');
    // 获取postId
    var postCollected = postsCollected[this.data.currentPostId];
    // 收藏变成未收藏,未收藏变成收藏
    postCollected = !postCollected;
    postsCollected[this.data.currentPostId] = postCollected;

    // 自定义函数也需要使用this来访问
    this.showModal(postsCollected, postCollected);
  },

  showModal: function (postsCollected, postCollected) {
    // 把this指代的Page对象先存储起来
    var that = this;
    wx.showModal({
      title: '收藏',
      content: postCollected ? '是否收藏该文章?' : '是否取消收藏该文章?',
      showCancel: 'true',
      cancelText: '取消',
      cancelColor: '#333',
      confirmText: '确认',
      confirmColor: '#405f80',
      success: function (res) {
        if (res.confirm) {
          // 更新文章是否收藏的缓存值
          wx.setStorageSync('posts_collected', postsCollected);
          // 更新数据绑定变量,从而实现切换图片
          that.setData({
            collected: postCollected
          });
          that.showToast(postsCollected, postCollected);
        }
      },
    });
  },

  showToast: function (postsCollected, postCollected) {
    wx.showToast({
      // 使用三元表达式来判断状态
      title: postCollected ? '收藏成功!' : '取消成功!',
      // 设置图标停留的时间,单位是毫秒
      duration: 1000,
      // icon可以设置图标,默认就是success
      icon: "success",
    });
  },

  onShareTap: function (event) {
    var itemList = [
      "分享给微信好友",
      "分享到朋友圈",
      "分享到QQ",
      "分享到微博",
    ]
    wx.showActionSheet({
      itemList: itemList,
      itemColor: "#405f80",
      success: function (res) {
        //res.cancel  用户是否点击了取消按钮
        //res.tapIndex  数组元素的索引
        wx.showModal({
          title: itemList[res.tapIndex],
          content: '现在无法实现分享功能,什么时候能支持还未知',
        })
      },
    })
  },

  onMusicTap: function (event) {
    var currentPostId = this.data.currentPostId;
    var postData = postsData.postList[currentPostId];

    // 使用变量来记录音乐的状态
    var isPlayingMusic = this.data.isPlayingMusic;
    if (isPlayingMusic) {
      // 暂停音乐
      wx.pauseBackgroundAudio();
      this.setData({
        isPlayingMusic : false
      });

    } else {
      wx.playBackgroundAudio({
        // 流媒体文件的URL,目前支持的格式有 m4a, aac, mp3, wav
        dataUrl: postData.music.url,
        // 音乐标题
        title: postData.music.title,
        // 音乐封面URL
        coverImgUrl: postData.music.coverImg,
      });
      this.setData({
        isPlayingMusic: true
      });
    }
  },
})

以上就算是完成了一个基本的音乐播放效果,这个文章详情页面也就是算是完成了,虽然我感觉可能还存在一些问题,毕竟没有完美的代码,如果后续出现问题后再进行修复。我个人觉得开发项目应该先开发出一个能够上线运行的原型,在运营的过程中再逐步去修复bug,迭代版本。


真机如何清除缓存与template的路径问题

在小程序中的缓存数据都是没有时效期的,不主动清除的话就会一直存在,在模拟器上我们可以点击工具提供的清除缓存按钮清除缓存数据:
第三个页面:构建新闻详情页面

但是如果在真机上,则需要自己手动编写一个清除缓存的按钮,需要使用到wx.clearStorageSync()或wx.clearStorage()方法,前者是同步通清理本地数据缓存,后者则是异步清理本地数据缓存。当点击这个按钮的时候就能触发一个点击事件去执行这个清除缓存的方法。

template的路径问题:

我们都知道template文件中的代码目的是为了给其他页面文件复用的,所以template代码中的所包含的文件路径不要写相对路径,写绝对路径比较好,因为如果文件A引用了template文件中的代码,但是文件A和template文件并不是同一级的,那么这时候如果使用相对路径就会有问题。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI