微信小程序样式indicatordots的样式怎么改

微信小程序快速入门教程 | VR视频推荐DEMO - 简书
<div class="fixed-btn note-fixed-download" data-toggle="popover" data-placement="left" data-html="true" data-trigger="hover" data-content=''>
写了87279字,被284人关注,获得了373个喜欢
微信小程序快速入门教程 | VR视频推荐DEMO
DEMO效果图1
这周,你被微信小程序刷屏了吗? 反正本猿被刷屏了~~~~本猿最先看到微信小程序相关的文章,应该是冯大辉老师的, 据MAC君透露,这篇文章寥寥几百字,一天内阅读量过百万。。。这是要全猿开发微信小程序的节奏撒!!本猿表示绝不能落伍,于是趁着周末,开始微信小程序开发之旅!各位猿们,上车咯~
一言不合就上效果图哈,辣这个demo是肿么做出来的咧?莫急莫急,且听本猿慢慢道来哈~
开发微信小程序,首先肯定是搭建环境啦,你可以选择根据github上的这个进行下载安装,也可以下载这个,快速完成安装哈,这个安装包里有如下文件:
接下来的步骤是:(Update: 已经不用做下面这三个步骤啦,直接进repo链接下载最新的9.0版,直接安装哈)
安装wechat_web_devtools_0.7.0_x64,并用它跟手机微信实现登录
覆盖安装wechat_web_devtools_0.9.
将weapp-ide-crack-master文件夹中的三个文件,放到安装程序目录对应的地方:
微信web开发者工具\package.nw\app\dist\components\create\reatestep.js
微信web开发者工具\package.nw\app\dist\stroes\projectStores.js
微信web开发者工具\package.nw\app\dist\weapp\appservice\asdebug.js
做完上述步骤,就可以愉快的打开微信的IDE啦~
接下来,就是程序员的惯例:helloworld!
创建项目,填好相关input选项,点击添加项目,搞定!
太简单?没错,就是这么简单,哈哈,如果要复杂点,用IDE打开本猿提供的压缩包中的webapp-demo。
它提供了所有组件和API的使用范例,如下图:
相信玩过react native的童鞋一定会觉得似曾相识,没错,这个DEMO就像react native官方提供的UIExplorer一样哈!通过这个demo,我们就可以照着做一个DEMO啦。
代码结构概览分析
搭完环境,就可以愉快的码代码了,我们先来看下刚新建项目的代码结构:
我们先关注根目录下最主要的三个文件:
app.js我们暂时不需要改,首先配置好app.json,要点如下:
将你要建立的page路径加到pages配置项上
配置window配置项上的标题栏颜色 / 风格 / 内容等
配置tabbar配置项上的内容 / icon等
设置debug配置项,方便打log
"pages/index/index",
"pages/index/view/info-list",
"pages/index/view/info-detail",
"pages/logs/logs",
"window":{
"backgroundTextStyle":"light",
"navigationBarBackgroundColor": "#4DBBFF",
"navigationBarTitleText": "微信VR视频资源推荐",
"navigationBarTextStyle":"white"
"tabBar": {
"color": "#dddddd",
"selectedColor": "#4DBBFF",
"borderStyle": "white",
"backgroundColor": "#ffffff",
"list": [{
"pagePath": "pages/index/index",
"iconPath": "image/wechat.png",
"selectedIconPath": "image/wechatHL_blue.png",
"text": "首页"
"pagePath": "pages/index/view/info-list",
"iconPath": "image/wechat.png",
"selectedIconPath": "image/wechatHL_blue.png",
"text": "VR列表"
"networkTimeout": {
"request": 10000,
"connectSocket": 10000,
"uploadFile": 10000,
"downloadFile": 10000
"debug": true
至于app.wxss, 如果多个页面中的样式有重复,需要复用,可以在app.wxss上写哈~~
接下来就写页面啦,通过app.json,大家可以看出,我总共就三个page而已哈(另外一个自带的,log记录之类的,忽略)
我们先来了解下page的生命周期回调函数:
生命周期函数--监听页面加载
生命周期函数--监听页面渲染完成
生命周期函数--监听页面显示
生命周期函数--监听页面隐藏
生命周期函数--监听页面卸载
在写这三个page时,主要用到了三个组件,第一个是swiper,也就是滑动面板,代码如下:
//index.wxml
&!--Swiper--&
class="section section_gap swiper"&
&swiper style="height: 200" indicator-dots="{{indicatorDots}}" vertical="{{vertical}}"
autoplay="{{autoplay}}" interval="{{interval}}" duration="{{duration}}"&
&block wx:for-items="{{swiper_list.data}}"&
&swiper-item&
&navigator class="swiper_nav" url="view/info-detail?title={{item.article.title}}&id={{item.article._id}}&req_type=swiper" hover-class="navigator-hover"&
&text class="swiper_txt"&{{item.article.title}}&/text&
&image class="swiper_img" src="{{item.article.cover}}"&&/image&
&/navigator&
&/swiper-item&
//index.js
get_vr_swiper_info: function() {
console.log('get_vr_info')
var swiper_offset =
parseInt( 800 * Math.random() );
var that = this
wx.request({
url: API_URL + swiper_offset,
success: function(response) {
that.setData({
swiper_list: response.data.result
第二个是可滚动视图scrollview,代码如下
//index.wxml
&scroll-view class="widgets" scroll-y="true" style="height: 400" bindscrolltoupper="upper" bindscrolltolower="lower" bindscroll="scroll" scroll-into-view="{{toView}}" scroll-top="{{scrollTop}}"&
&block wx:for-items="{{vr_list.data}}"&
&navigator url="view/info-detail?title={{item.article.title}}&id={{item.article._id}}&req_type=vr_list" hover-class="navigator-hover"&
&view class="widgets__item scroll-view-item"&
&view id="form" class="widgets__info" bindtap="widgetsToggle"&
&text class="widgets__info-name"&{{item.article.title}}&/text&
&image class="widgets__info-icon" src="resources/kind/form.png" background-size="cover" /&
&image class="widgets__info-img" src="{{item.article.cover}}" /&
&/navigator&
&/scroll-view&
//index.js
upper: function(e) {
console.log(e)
lower: function(e) {
console.log(e)
scroll: function(e) {
console.log(e)
scrollToTop: function(e) {
this.setAction({
scrollTop: 0
tap: function(e) {
for (var i = 0; i & order. ++i) {
if (order[i] === this.data.toView) {
this.setData({
toView: order[i + 1],
scrollTop: (i + 1) * 200
tapMove: function(e) {
this.setData({
scrollTop: this.data.scrollTop + 10
第三个是navigator,负责页面间的跳转,代码上面其实也有了,主要就是声明好标签,并写上url路径就行啦。
&navigator url="view/info-detail?id={{item.article._id}}"&
&/navigator&
另外还需要了解三个主要接口:
wx.request()
wx.setStorage()
wx.getStorage()
wx.request({
url: API_URL + vr_list_offset,
success: function(response) {
that.setData({
vr_list: response.data.result
wx.setStorage({
key:"vr_list",
data:that.data.vr_list
wx.getStorage({
key:'vr_list',
success:function(res){
console.log(res.data);
了解完这几个点之后,就可以动手做一个基础的demo啦,祝各位好运撒!
本猿之前也玩过react native,据我粗浅的理解,微信小程序的DEMO搭建相对RN会容易些,开发效率更高,但当前的太过简陋了,相关的组件描述也太少。
IDE有待改进,还木有linux环境,不嗨森!!BTW,强烈建议支持VIM
navigator实现page间的跳转,但缺少能跳转并传递对象的方法啊,难道只能通过setStorage()? 还是我没找到??
组件少了点啊,木有webview,木有iframe~~~
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
打开微信“扫一扫”,打开网页后点击屏幕右上角分享按钮
被以下专题收入,发现更多相似内容:
如果你是程序员,或者有一颗喜欢写程序的心,喜欢分享技术干货、项目经验、程序员日常囧事等等,欢迎投稿《程序员》专题。
专题主编:小...
· 227266人关注
@IT 专题 由 IT大分类,转定位于IT·互联网行业观察与思考,数码产品极客体验。
主编:向右奔跑 http://www.ji...
· 211311人关注
聊聊创业这点事
· 190276人关注
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
选择支付方式:微信小程序之知乎日报_Javascript教程-织梦者
当前位置:&>&&>& > 微信小程序之知乎日报
微信小程序之知乎日报
本文将为关注织梦者的朋友提供的是的微信小程序之知乎日报相关教程,具体实例代码请看下文:上一次的《微信小程序之小豆瓣图书》制作了一个图书的查询功能,只是简单地应用到了网络请求,其他大多数小程序应有的知识。而本次的示例是知乎日报,功能点比较多,页面也比上次复杂了许多。在我编写这个DEMO之前,网上已经有很多网友弄出了相同的DEMO,也是非常不错的,毕竟这个案例很经典,有比较完整的API,很值得模仿学习。本次个人的DEMO也算是一次小小的练习吧。
由于知乎日报是一个资讯类的App,UI的布局主要是以资讯列表页、资讯详情页和评论页为主,当然本次也附带了应用设置页,不过现阶段功能尚未编写,过段时间会更新补充,继续完善。
本次应用使用了知乎日报的API,相比上次豆瓣图书的数量比较多了,但是部分仍然有限制,而且自己没有找到评论接口的分页参数,所以评论这块没有做数据的分页。
以下是使用到的具体API,更加详细参数和返回结构可参照网上网友分享的 知乎日报-API-分析 ,在此就不做再次分析了。
启动界面图片
http://news-/api/4/start-image/{size}
图片尺寸,格式:宽*高。例如: 768*1024
获取刚进入应用时的显示封面,可以根据传递的尺寸参数来获取适配用户屏幕的封面。
获取最新日报
http://news-/api/4/news/latest
返回的数据用于日报的首页列表,首页的结构有上下部分,上部分是图片滑动模块,用于展示热门日报,下部分是首页日报列表,以上接口返回的数据有热门日报和首页日报
获取日报详细
http://news-/api/4/news/{id}
在点击日报列表也的日报项时,需要跳转到日报详情页展示日报的具体信息,这个接口用来获取日报的展示封面和具体内容。
http://news./api/4/news/before/{date}
年月日格式时间yyyyMMdd,例如:161202
这个接口也是用与首页列表的日报展示,但是不同的是此接口需要传一个日期参数,如格式。获取最新日报接口只能获取当天的日报列表,如果需要获取前天或者更久之前的日报,则需要这个接口单独获取。
日报额外信息
http://news-/api/4/story-extra/{id}
在日报详情页面中,不仅要展示日报的内容,好需要额外获取此日报的评论数目和推荐人数等额外信息。
http://news-/api/4/story/{id}/long-comments
日报的评论页面展示长评用到的接口(没有找到分页参数,分页没有做)
http://news-/api/4/story/{id}/short-comments
日报的评论页面展示段评用到的接口(没有找到分页参数,分页没有做)
主题日报栏目列表
http://news-/api/4/themes
主页的侧边栏显示有主题日报的列表,需要通过这个接口获取主题日报栏目列表
主题日报具体内容列表
http://news-/api/4/theme/{themeId}
主题日报栏目id
在主页侧栏点击主题日报进入主题日报的内容页,需要展示此主题日报下的日报列表。
作为一个仿制知乎日报的伪APP,高大上的启动封面是必须的,哈哈。启动页面很简单,请求一个应用启动封面接口,获取封面路径和版权信息。当进入页面,在onLoad事件中获取屏幕的宽和高来请求适合尺寸的图片,在onReady中请求加载图片,在请求成果之后,延迟2s进入首页,防止页面一闪而过。
onLoad: function( options ) {
var _this =
wx.getSystemInfo( {
success: function( res ) {
_this.setData( {
screenHeight: res.windowHeight,
screenWidth: res.windowWidth,
onReady: function() {
var _this =
var size = this.data.screenWidth + &#39;*&#39; + this.data.screenH
requests.getSplashCover( size, ( data ) =& {
_this.setData( { splash: data });
}, null, () =& {
toIndexPage.call(_this);
* 跳转到首页
function toIndexPage() {
setTimeout( function() {
wx.redirectTo( {
url: &#39;../index/index&#39;
}, 2000 );
首页顶部需要用到轮播图来展示热门日报,小程序中的Swipe组件可以实现。
&swiper class=&index-swiper& indicator-dots=&true& interval=&10000&&
&block wx:for=&{{sliderData}}&&
&swiper-item data-id=&{{item.id}}& bindtap=&toDetailPage&&
&image mode=&aspectFill& src=&{{item.image}}& style=&width:100%& /&
&view class=&mask&&&/view&
&view class=&desc&&&text&{{item.title}}&/text&&/view&
&/swiper-item&
所有的内容都必须要在swiper-item标签中,因为我们的图片不止有一张,而是有多个热门日报信息,需要用循环来展示数据。这里需要指定的是image里的属性mode设置为aspectFill是为了适应组件的宽度,这需要牺牲他的高度,即有可能裁剪,但这是最好的展示效果。toDetailPage是点击事件,触发跳转到日报详情页。在跳转到日报详情页需要附带日报的id过去,我们在循环列表的时候把当前日报的id存到标签的data中,用data-id标识,这有点类似与html5中的data-*API。当在这个标签上发生点击事件的时候,我们可以通过Event.currentTarget.dataset.id来获取data-id的值。
列表的布局大同小异,不过这里的列表涉及到分页,我们可以毫不犹豫地使用scroll-view组件,它的scrolltolower是非常好用的,当组件滚动到底部就会触发这个事件。上次的小豆瓣图书也是使用了这个组件分页。不过这次的分页动画跟上次不一样,而是用一个附带旋转动画的刷新图标,使用官方的动画api来实现旋转。
&view class=&refresh-block& wx:if=&{{loadingMore}}&&
&image animation=&{{refreshAnimation}}& src=&../../images/refresh.png&&&/image&
代码中有一个显眼的animation属性,这个属性就是用来控制动画的。
* 旋转上拉加载图标
function updateRefreshIcon() {
var deg = 360;
var _this =
var animation = wx.createAnimation( {
duration: 1000
var timer = setInterval( function() {
if( !_this.data.loadingMore )
clearInterval( timer );
animation.rotateZ( deg ).step();
deg += 360;
_this.setData( {
refreshAnimation: animation.export()
}, 1000 );
当列表加载数据时,给动画设置一个时长duration,然后按Z轴旋转,即垂直方向旋转rotateZ,每次旋转360度,周期是1000毫秒。
列表的布局跟上次的小豆瓣图书的结构差不多,用到了循环结构wx:for和判断语句wx:if、 wx:else来控制不同的展示方向。
&view class=&common-list&&
&block wx:for=&{{pageData}}&&
&view class=&list-item {{item.images[0] ? &#39;has-img&#39;: &#39;&#39;}}& wx:if=&{{item.type != 3}}& data-id=&{{item.id}}& bindtap=&toDetailPage&&
&view class=&content&&
&text&{{item.title}}&/text&
&image wx:if=&{{item.images[0]}}& src=&{{item.images[0]}}& class=&cover&&&/image&
&view class=&list-spliter& wx:else&
&text&{{item.title}}&/text&
class=&list-spliter&这块是用来显示日期,列表中的日报只要不是同一天的记录,就在中间插入一条日期显示块。在列表项中有一个三元运算判断输出具体的class{{item.images[0] ? 'has-img': ''}},是因为列表中可能没有图片,因此需要判定当前有没有图片,没有图片就不添加class为has-img来控制带有图片列表项的布局。
因为小程序中没有侧栏组件,无法做到侧滑手势显示侧栏(本人发现touchstart事件和tap事件有冲突,无法实现出手势侧滑判断,所以没有用侧滑手势,可能是本人理解太浅了,没有发现解决方法,嘿嘿...),浮动按钮的样式参照了中的FloatAction经典按钮。可以浮动在界面上,还可以滑动到任意位置,背景为稍微透明。
&view class=&float-action& bindtap=&ballClickEvent& style=&opacity: {{ballOpacity}};bottom:{{ballBottom}}right:{{ballRight}}& bindtouchmove=&ballMoveEvent&&
.float-action {
bottom: 20
height: 50
border-radius: 50%;
box-shadow: 2px 2px 10px #AAA;
background: #1891D4;
z-index: 100;
按钮的样式随便弄了一下,宽高用了px是因为后面的移动判断需要获取屏幕的宽高信息,这些信息的单位是px。wxml绑定了点击事件和移动事件,点击事件是控制侧栏弹出,滑动事件是按钮移动。
//浮动球移动事件
ballMoveEvent: function( e ) {
var touchs = e.touches[ 0 ];
var pageX = touchs.pageX;
var pageY = touchs.pageY;
if( pageX & 25 )
if( pageX & this.data.screenWidth - 25 )
if( this.data.screenHeight - pageY &= 25 )
if( pageY &= 25 )
var x = this.data.screenWidth - pageX - 25;
var y = this.data.screenHeight - pageY - 25;
this.setData( {
ballBottom: y,
ballRight: x
touchmove事件中的会传递一个event参数,通过这个参数可以获取到当前手势滑动到的具体坐标信息e.touches[ 0 ]
侧滑菜单是一个经典APP布局方案,小程序中没有提供这个组件,甚是遗憾。不过实现起来也不是很难,但是总感觉有点别扭...
侧滑菜单的样式采用了固定定位的布局position: fixed,默认隐藏与左侧,当点击浮动按钮时弹出,点击遮罩或者侧栏上边的关闭按钮时收回。侧栏的弹出和收回动画采用小程序提供的动画API。
&view class=&slide-mask& style=&display:{{maskDisplay}}& bindtap=&slideCloseEvent&&&/view&
&view class=&slide-menu& style=&right: {{slideRight}}width: {{slideWidth}}height:{{slideHeight}}& animation=&{{slideAnimation}}&&
&icon type=&cancel& size=&30& class=&close-btn& color=&#FFF& bindtap=&slideCloseEvent& /&
&scroll-view scroll-y=&true& style=&height:100%;width:100%&&
&view class=&header&&
&view class=&userinfo&&
&image src=&../../images/avatar.png& class=&avatar&&&/image&
&text&Oopsguy&/text&
&view class=&toolbar&&
&view class=&item&&
&image src=&../../images/fav.png&&&/image&
&text&收藏&/text&
&view class=&item& bindtap=&toSettingPage&&
&image src=&../../images/setting.png&&&/image&
&text&设置&/text&
&view class=&menu-item home&&
&text&首页&/text&
&view class=&slide-inner&&
&block wx:for=&{{themeData}}&&
&view class=&menu-item& data-id=&{{item.id}}& bindtap=&toThemePage&&
&text&{{item.name}}&/text&
&image src=&../../images/plus.png&&&/image&
&/scroll-view&
/*slide-menu*/
.slide-mask {
width: 100%;
bottom: 0;
background: rgba(0, 0, 0, .3);
z-index: 800;
.slide-menu {
background: #FFF;
z-index: 900;
/*.slide-menu .slide-inner {
padding: 40
.slide-menu .header {
background: #019DD6;
height: 200
color: #FFF;
padding: 20rpx 40rpx 0 40
.userinfo {
height: 80
line-height: 80
.userinfo .avatar {
height: 80
border-radius: 50%;
margin-right: 40
.userinfo text {
font-size: 35
.toolbar {
height: 100
padding-top: 25
line-height: 75
.toolbar .item {
width: 50%;
display: inline-
text-align: center
.toolbar .item text {
display: inline-
font-size: 30rpx
.toolbar .item image {
display: inline-
margin-right: 10
height: 50
.slide-menu .menu-item {
height: 100
line-height: 100
padding: 0 40
font-size: 35
.slide-menu .menu-item:active {
background: #FAFAFA;
.slide-menu .menu-item image {
height: 50
.slide-menu .home {
color: #019DD6
.slide-menu .close-btn {
z-index: 1000
以上是侧栏的一个简单的布局和样式,包含了侧栏中的用户信息块和主题日报列表。当然这些信息是需要通过js的中网络请求来获取的。侧栏结构上边有一个class为slide-mask的view,这是一个遮罩元素,当侧栏弹出的时候,侧栏后边就有一层轻微透明的黑色遮罩。侧栏的高度和宽度初始是不定的,需要在进入页面的时候,马上获取设备信息来获取屏幕的高度宽度调整侧栏样式。
//获取设备信息,屏幕的高度宽度
onLoad: function() {
var _this =
wx.getSystemInfo( {
success: function( res ) {
_this.setData( {
screenHeight: res.windowHeight,
screenWidth: res.windowWidth,
slideHeight: res.windowHeight,
slideRight: res.windowWidth,
slideWidth: res.windowWidth * 0.7
宽度我取了屏幕宽度的70%,高度一致。侧栏的弹出收回动画使用内置动画API
//侧栏展开
function slideUp() {
var animation = wx.createAnimation( {
duration: 600
this.setData( { maskDisplay: &#39;block&#39; });
animation.translateX( &#39;100%&#39; ).step();
this.setData( {
slideAnimation: animation.export()
//侧栏关闭
function slideDown() {
var animation = wx.createAnimation( {
duration: 800
animation.translateX( &#39;-100%&#39; ).step();
this.setData( {
slideAnimation: animation.export()
this.setData( { maskDisplay: &#39;none&#39; });
侧栏弹出的时候,遮罩的css属性display设置为block显示,侧栏通过css动画transform来想右侧移动了100%的宽度translateX(100%),侧栏收回时,动画恰好与弹出的相反,其实这些动画最后都会翻译为css3动画属性,这些API只是css3动画的封装。为了点击遮罩收回侧栏,遮罩的tap事件也要绑定slideCloseEvent
//浮动球点击 侧栏展开
ballClickEvent: function() {
slideUp.call( this );
//遮罩点击
slideCloseEvent: function() {
slideDown.call( this );
日报详情页
日报的内容也是最难做的,因为接口返回的内容是html...,天呀,是html!小程序肯本就不支持,解析html的过程非常痛苦,因为本人的正则表达式只是几乎为0,解析方案的寻找过程很虐心,经典的jQuery是用不了了,又没有dom,无法用传统的方式解析html。尝试了正则学习,但是也是无法在短时间内掌握,寻找了很多解析库,大多是依赖api。不过,上天是不会忽视有心人的,哈哈,还是被我找到了解决方案。幸运的我发现了一个用正则编写的和类似与语法分析方法的xml解析库。这个库是一个very good的网友封装的html解析库。详情点击 用解析html。
由于日报详情内容的html部分结构太大,这里只列出了简要的结构,这个结构是通用的(不过不保证知乎会变动结构,要是变动了,之前的解析可能就没用了...心累)
&div class=&question&&
&h2 class=&question-title&&日本的六大财阀现在怎么样了?&/h2&
&div class=&answer&&
&div class=&meta&&
![](http://upload-images.jianshu.io/upload_images/caed2ef752b34.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
&span class=&author&&leon,&/span&&span class=&bio&&data analyst&/span&
&div class=&content&&
&p&“财阀”在战后统称为 Group(集团),是以银行和传统工业企业为核心的松散集合体,由于历史渊源而有相互持股。&/p&
&p&Group 对于当今日本企业的意义在于:&/p&
&p&&strong&MUFG:三菱集团、三和集团(みどり会)&/strong&&/p&
&p&&img class=&content-image& src=&/70/90c319ac7a7bde954f45bc_b.jpg&
&div class=&view-more&&&a href=&/question/&&查看知乎讨论&span class=&js-question-holder&&&/span&&/a&&/div&
外层的.question是日报中问题答案的显示单位,可能有多个,因此需要循环显示。.question-title是问题的标题,.meta中是作者的信息,img.avatar是用户的头像,span.author是用户的名称,span.bio可能使用户的签名吧。最难解析的是.content中的内容,比较多。但是有个规律就是都是以&p&标签包裹着,获取了.content中的所有p就可以得到所有的段落。之后再解析出段落中的图片。
以下是详情页的内容展示模版
&view style=&padding-bottom: 150&&
&block wx:for=&{{news.body}}&&
&view class=&article&&
&view class=&title& wx:if=&{{item.title && item.title != &#39;&#39;}}&&
&text&{{item.title}}&/text&
&view class=&author-info& wx:if=&{{(item.avatar && item.avatar != &#39;&#39;) || (item.author && item.author != &#39;&#39;) || (item.bio && item.bio != &#39;&#39;)}}&&
&image wx:if=&{{item.avatar && item.avatar != &#39;&#39;}}& class=&avatar& src=&{{item.avatar}}&&&/image&
&text wx:if=&{{item.author && item.author != &#39;&#39;}}& class=&author-name&&{{item.author}}&/text&
&text wx:if=&{{item.bio && item.bio != &#39;&#39;}}& class=&author-mark&&,{{item.bio}}&/text&
&view class=&content& wx:if=&{{item.content && item.content.length & 0}}&&
&block wx:for=&{{item.content}}& wx:for-item=&it&&
&block wx:if=&{{it.type == &#39;p&#39;}}&&
&text&{{it.value}}&/text&
&block wx:elif=&{{it.type == &#39;img&#39;}}&&
&image mode=&aspectFill& src=&{{it.value}}& data-src=&{{it.value}}& bindtap=&previewImgEvent& /&
&block wx:elif=&{{it.type == &#39;pstrong&#39;}}&&
&text class=&strong&&{{it.value}}&/text&
&block wx:elif=&{{it.type == &#39;pem&#39;}}&&
&text class=&em&&{{it.value}}&/text&
&block wx:elif=&{{it.type == &#39;blockquote&#39;}}&&
&text class=&qoute&&{{it.value}}&/text&
&block wx:else&
&text&{{it.value}}&/text&
class=&discuss& wx:if=&{{item.more && item.more != &#39;&#39;}}&&
&navigator url=&{{item.more}}&&查看知乎讨论&/navigator&
可以看出模版中的内容展示部分用了蛮多的block加判断语句wx:if
wx:else。这些都是为了需要根据解析后的内容类型来判断需要展示什么标签和样式。解析后的内容大概格式是这样的:
title: &#39;标题&#39;,
author: &#39;作者&#39;,
bio: &#39;签名&#39;,
avatar: &#39;头像&#39;,
more: &#39;更多地址&#39;,
content: [
type: &#39;p&#39;,
value: &#39;普通段落内容&#39;
type: &#39;img&#39;,
value: &#39;http://xxx.xx.xx/1.jpg&#39;
type: &#39;pem&#39;,
value: &#39;...&#39;
需要注意的一点是主题日报有时候返回的html内容是经过unicode编码的不能直接显示,里边全是类似&#的字符,这需要单独为主题日报的日报详情解析编码。
再点击主题日报中的列表项是,传递一个标记是主题日报的参数theme
//跳转到日报详情页
toDetailPage: function( e ) {
var id = e.currentTarget.dataset.
wx.navigateTo( {
url: &#39;../detail/detail?theme=1&id=&#39; + id
然后在Detail.js的onLoad事件中接受参数
//获取列表残过来的参数 id:日报id, theme:是否是主题日报内容(因为主题日报的内容有些需要单独解析)
onLoad: function( options ) {
var id = options.
var isTheme = options[ &#39;theme&#39; ];
this.setData( { id: id, isTheme: isTheme });
之后开始请求接口获取日报详情,并根据是否是主题日报进行个性化解析
//加载页面相关数据
function loadData() {
var _this =
var id = this.data.
var isTheme = this.data.isT
//获取日报详情内容
_this.setData( { loading: true });
requests.getNewsDetail( id, ( data ) =& {
data.body = utils.parseStory( data.body, isTheme );
_this.setData( { news: data, pageShow: &#39;block&#39; });
wx.setNavigationBarTitle( { title: data.title }); //设置标题
}, null, () =& {
_this.setData( { loading: false });
以上传入一个isTheme参数进入解析方法,解析方法根据此参数判断是否需要进行单独的编码解析。
内容解析的库代码比较多,就不贴出了,可以到git上查看。这里给出解析的封装。
var HtmlParser = require( &#39;htmlParseUtil.js&#39; );
String.prototype.trim = function() {
return this.replace( /(^\s*)|(\s*$)/g, &#39;&#39; );
String.prototype.isEmpty = function() {
return this.trim() == &#39;&#39;;
* 快捷方法 获取HtmlParser对象
* @param {string} html html文本
* @return {object} HtmlParser
function $( html ) {
return new HtmlParser( html );
* 解析story对象的body部分
* @param {string} html body的html文本
* @param {boolean} isDecode 是否需要unicode解析
* @return {object} 解析后的对象
function parseStory( html, isDecode ) {
var questionArr = $( html ).tag( &#39;div&#39; ).attr( &#39;class&#39;, &#39;question&#39; ).match();
var stories = [];
if( questionArr ) {
for( var i = 0, len = questionArr.i &i++ ) {
$story = $( questionArr[ i ] );
stories.push( {
title: getArrayContent( $story.tag( &#39;h2&#39; ).attr( &#39;class&#39;, &#39;question-title&#39; ).match() ),
avatar: getArrayContent( getArrayContent( $story.tag( &#39;div&#39; ).attr( &#39;class&#39;, &#39;meta&#39; ).match() ).jhe_ma( &#39;img&#39;, &#39;src&#39; ) ),
author: getArrayContent( $story.tag( &#39;span&#39; ).attr( &#39;class&#39;, &#39;author&#39; ).match() ),
bio: getArrayContent( $story.tag( &#39;span&#39; ).attr( &#39;class&#39;, &#39;bio&#39; ).match() ),
content: parseStoryContent( $story, isDecode ),
more: getArrayContent( getArrayContent( $( html ).tag( &#39;div&#39; ).attr( &#39;class&#39;, &#39;view-more&#39; ).match() ).jhe_ma( &#39;a&#39;, &#39;href&#39; ) )
* 解析文章内容
* @param {string} $story htmlparser对象
* @param {boolean} isDecode 是否需要unicode解析
* @returb {object} 文章内容对象
function parseStoryContent( $story, isDecode ) {
var content = [];
var ps = $story.tag( &#39;p&#39; ).match();
var p, strong, img, blockquote,
if( ps ) {
for( var i = 0, len = ps.i &i++ ) {
p = ps[ i ]; //获取&p&的内容
if( !p || p.isEmpty() )
img = getArrayContent(( p.jhe_ma( &#39;img&#39;, &#39;src&#39; ) ) );
strong = getArrayContent( p.jhe_om( &#39;strong&#39; ) );
em = getArrayContent( p.jhe_om( &#39;em&#39; ) );
blockquote = getArrayContent( p.jhe_om( &#39;blockquote&#39; ) );
if( !img.isEmpty() ) { //获取图片
content.push( { type: &#39;img&#39;, value: img });
else if( isOnly( p, strong ) ) { //获取加粗段落&p&&strong&...&/strong&&/p&
strong = decodeHtml( strong, isDecode );
if( !strong.isEmpty() )
content.push( { type: &#39;pstrong&#39;, value: strong });
else if( isOnly( p, em ) ) { //获取强调段落 &p&&em&...&/em&&/p&
em = decodeHtml( em, isDecode );
if( !em.isEmpty() )
content.push( { type: &#39;pem&#39;, value: em });
else if( isOnly( p, blockquote ) ) { //获取引用块 &p&&blockquote&...&/blockquote&&/p&
blockquote = decodeHtml( blockquote, isDecode );
if( !blockquote.isEmpty() )
content.push( { type: &#39;blockquote&#39;, value: blockquote });
else { //其他类型 归类为普通段落 ....太累了 不想解析了T_T
p = decodeHtml( p, isDecode );
if( !p.isEmpty() )
content.push( { type: &#39;p&#39;, value: p });
* 取出多余或者难以解析的html并且替换转义符号
function decodeHtml( value, isDecode ) {
if( !value ) return &#39;&#39;;
value = value.replace( /&[^&]+&/g, &#39;&#39; )
.replace( / /g, &#39; &#39; )
.replace( /“/g, &#39;&&#39; )
.replace( /”/g, &#39;&&#39; ).replace( /·/g, &#39;·&#39; );
if( isDecode )
return decodeUnicode( value.replace( /&#/g, &#39;\\u&#39; ) );
* 解析段落的unicode字符,主题日报中的内容又很多是编码过的
function decodeUnicode( str ) {
var ret = &#39;&#39;;
var splits = str.split( &#39;;&#39; );
for( let i = 0;i & splits.i++ ) {
ret += spliteDecode( splits[ i ] );
* 解析单个unidecode字符
function spliteDecode( value ) {
var target = value.match( /\\u\d+/g );
if( target && target.length & 0 ) { //解析类似
&7.1 \u20998& 参杂其他字符
target = target[ 0 ];
var temp = value.replace( target, &#39;{{@}}&#39; );
target = target.replace( &#39;\\u&#39;, &#39;&#39; );
target = String.fromCharCode( parseInt( target ) );
return temp.replace( &{{@}}&, target );
// value = value.replace( &#39;\\u&#39;, &#39;&#39; );
// return String.fromCharCode( parseInt( value, &#39;10&#39; ) )
* 获取数组中的内容(一般为第一个元素)
* @param {array} arr 内容数组
* @return {string} 内容
function getArrayContent( arr ) {
if( !arr || arr.length == 0 ) return &#39;&#39;;
return arr[ 0 ];
function isOnly( src, target ) {
return src.trim() ==
module.exports = {
parseStory: parseStory
代码的解析过程比较繁杂,大家可以根据返回的html结构和参照解析库的作者写的文章来解读。
底部工具栏
一般资讯APP的详情页都有一个底部的工具栏用于操作分享、收藏、评论和点赞等等。为了更好地锻炼动手能力,自己也做了一个底部工具栏,虽然官方的APP并没有这个东西。前面介绍到的获取额外信息API在这里就被使用了。本来自己是想把推荐人数和评论数显示在底部的图片右上角,但是由于本人的设计问题,底部的字号已经是很小了,显示数量的地方的字号又不能再小了,这样看起来数字显示的地方和图标的大小几乎一样,很是别扭,所以就不现实数字了。这块还是有很多待完善的功能的,比较收藏功能和是否有评论提示功能等。
&view class=&toolbar&&
&view class=&inner&&
&view class=&item& bindtap=&showModalEvent&&&image src=&../../images/share.png& /&&/view&
&view class=&item& bindtap=&reloadEvent&&&image src=&../../images/refresh.png& /&&/view&
&view class=&item&&&image src=&../../images/favorite.png& /&&/view&
&view class=&item& data-id=&{{id}}& bindtap=&toCommentPage&&&image src=&../../images/insert_comment.png& /&
&view class=&tip&&&/view&
&view class=&item&&
&image src=&../../images/thumb_up_active.png& /&
底部有分享、收藏、评论和点赞按钮,分享肯定是做不了啦,哈哈,但是效果还是需要有的,就一个modal弹窗,显示各类社交应用的图标就行啦。
&modal class=&modal& confirm-text=&取消& no-cancel hidden=&{{modalHidden}}& bindconfirm=&hideModalEvent&&
&view class=&share-list&&
&view class=&item&&&image src=&../../images/share_qq.png& /&&/view&
&view class=&item&&&image src=&../../images/share_pengyouquan.png& /&&/view&
&view class=&item&&&image src=&../../images/share_qzone.png& /&&/view&
&view class=&share-list& style=&margin-top: 20rpx&&
&view class=&item&&&image src=&../../images/share_weibo.png& /&&/view&
&view class=&item&&&image src=&../../images/share_alipay.png& /&&/view&
&view class=&item&&&image src=&../../images/share_plus.png& /&&/view&
model的隐藏和显示都是通过hidden属性来控制。
底部工具栏中还有一个按钮是刷新,其实就是一个重新调用接口请求数据的过程而已。
//重新加载数据
reloadEvent: function() {
loadData.call( this );
评论页面蛮简单的,就是展示评论列表,但是要展示两部分,一部分是长评,另一部分是短评。长评跟短评的布局都是通用的。进入到评论页面时,如果长评有数据,则先加载长评,短评需要用户点击短评标题才加载,否则就直接加载短评。这需要上一个详情页面中传递日报的额外信息过来(即长评数量和短评数量)。
之前已经在日报详情页面中,顺便加载了额外的信息
//请求日报额外信息(主要是评论数和推荐人数)
requests.getStoryExtraInfo( id, ( data ) =& {
_this.setData( { extraInfo: data });
在跳转到评论页面的时候顺便传递评论数量,这样我们就不用在评论页面在请求一次额外信息了。
//跳转到评论页面
toCommentPage: function( e ) {
var storyId = e.currentTarget.dataset.
var longCommentCount = this.data.extraInfo ? this.data.extraInfo.long_comments : 0; //长评数目
var shortCommentCount = this.data.extraInfo ? this.data.extraInfo.short_comments : 0; //短评数目
//跳转到评论页面,并传递评论数目信息
wx.navigateTo( {
url: &#39;../comment/comment?lcount=&#39; + longCommentCount + &#39;&scount=&#39; + shortCommentCount + &#39;&id=&#39; + storyId
评论页面接受参数
//获取传递过来的日报id 和 评论数目
onLoad: function( options ) {
var storyId = options[ &#39;id&#39; ];
var longCommentCount = parseInt( options[ &#39;lcount&#39; ] );
var shortCommentCount = parseInt( options[ &#39;scount&#39; ] );
this.setData( { storyId: storyId, longCommentCount: longCommentCount, shortCommentCount: shortCommentCount });
进入页面立刻加载数据
//加载长评列表
onReady: function() {
var storyId = this.data.storyId;
var _this =
this.setData( { loading: true, toastHidden: true });
//如果长评数量大于0,则加载长评,否则加载短评
if( this.data.longCommentCount & 0 ) {
requests.getStoryLongComments( storyId, ( data ) =& {
console.log( data );
_this.setData( { longCommentData: ments });
}, () =& {
_this.setData( { toastHidden: false, toastMsg: &#39;请求失败&#39; });
}, () =& {
_this.setData( { loading: false });
loadShortComments.call( this );
* 加载短评列表
function loadShortComments() {
var storyId = this.data.storyId;
var _this =
this.setData( { loading: true, toastHidden: true });
requests.getStoryShortComments( storyId, ( data ) =& {
_this.setData( { shortCommentData: ments });
}, () =& {
_this.setData( { toastHidden: false, toastMsg: &#39;请求失败&#39; });
}, () =& {
_this.setData( { loading: false });
评论页面的展示也是非常的简单,一下给出长评模版,短评也是一样的,里面的点赞按钮功能木有实现哦。
&view class=&headline&&
&text&{{longCommentCount}}条长评&/text&
&view class=&common-list&&
&block wx:for=&{{longCommentData}}&&
&view class=&list-item has-img& data-id=&{{item.id}}&&
&view class=&content&&
&view class=&header&&
&text class=&title&&{{item.author}}&/text&
&image class=&vote& src=&../../images/thumb_up.png& /&
&text class=&body&&{{item.content}}&/text&
&text class=&bottom&&{{item.time}}&/text&
&image src=&{{item.avatar}}& class=&cover& /&
主题日报的样式跟首页几乎一模一样,我是拷贝过来修改了一点点(懒)。却别在多了一行主编区域。不过这个主编区域没有实现什么功能,本来是点击主编的头像跳转到主编的个人首页简介,没有时间安排就不做了,这也是需要解析html的(累)。
主题日报列表需要接受一个具体的主题日报id,根据这个id来请求接口获取主题日报的日报列表。
//接受主页传递过来的主题日报id
onLoad: function( options ) {
this.setData( { id: options.themeId });
主题日报的请求列表方式和主页的列表方式差不多,由于没有发现分页参数,主题日报的日报列表这部分也没有分页请求。主题日报的日报详情还是跳转到日报详情页面的。
本来想做设置页面里列出的功能,但是工作比较忙,还是归入到后边的完善计划吧,现阶段只做了简单的页面布局。
但是还是讲一下自己的思路
夜间模式就是改变应用的显示样式,利用到了css,我们可以在page中放置一个顶层的view来包括起所有的wxml元素,当切换主题时给页面顶层元素一个主题控制类。
&view class=&light&&
&view class=&night&&
那怎么实现换肤立即生效呢?一个页面刚启动是会经过onLoad、onShow等,当第二次进来的时候页面的onLoad事件就不会在次触发,而是触发onShow事件,我们可以通过onShow事件来获取存在全局缓存中的主题设置。
onShow: function() {
var app = getApp();
this.setData({theme: app.globalData.theme});
&view class=&{{theme}}&&
清除缓存功能,当然是把临时文件和localStorage中的数据清空。
clearDataEvent: function() {
wx.clearStorage(); //清除应用数据
应用的无图浏览模式跟主题的思路差不多,就是判断应用缓存中的设置是否是无图模式,如果是就在内容显示的时候加一个判断,根据这个值来判断是否显示图片类型的内容。
onLoad: function() {
var app = getApp();
this.setData({imageMode: app.getImageMode()});
&image wx:if=&{{imageMode}}& src=&...& /&
&!--或者--&
&block wx:if=&{{imageMode}}&&
&image src=&...& /&
蛮多图片显示不出来,不知到是为什么,src路径正常,以前的小豆瓣图书也是有图片列表,但是没有出现这种情况。
代码结构比较烂,很多地方都没有优化处理,复用率较低,待重构。
页面布局有些不合理,尺寸控制的不够好。
部分wxml没有用模版功能代替重复的渲染工作,达不到复用效果。
本次编写的小程序用到了蛮多知识点,虽然花费了不少时间,但是一切都是非常的值得。编写的过程中遇到最大的困难就是解析html内容,可以说是绞尽脑汁,哈哈,智商不足啦。很期待能有网友能奉献出更好的解决方法。这个小例子做的比较简陋,很多功能没有完全实现,跟别人的Android和React仿客户端相比,小巫见大巫啦。还得抽空完成后续的更多功能。
到目前为止,小程序已经更新了几次,支持了ES5/ES6转换、下拉刷新事件、上传文件等功能,不过还有很多API还不能在模拟环境下显示效果。自己觉得一直做类似于豆瓣图书和知乎日报等除了网络请求之外没什么特别的地方的应用也不好,需要尝试新的API来扩展自己的视野,后续打算往未使用到的API进行案例制作。不知不觉已经踏出校园准备有4个月了,很怀念以前的学习日子,做过很多案例,但是都没有写日志和保存的习惯。这次写的字数蛮多的,可累死我了。很幸运自己初入工作圈就能碰上小程序风暴,期待它正式公测!
现阶段比较完整的效果动态图
本次示例的源码地址:
/oopsguy/WechatSmallApps
http://git.oschina.net/oopsguy/WechatSmallApps
如果大家喜欢,给个start激励一下我,以后会有更好的作品与大家分享:)以上就是这篇文章的全部内容了,希望大家能够喜欢。
这些内容可能对你也有帮助
更多可查看Javascript教程列表页。
猜您也会喜欢这些文章}

我要回帖

更多关于 微信小程序开发样式库 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信