微信小程序开发日记——高仿知乎日报(上)

本人对知乎日报是情有独钟,看个人博客和github就知道了,写了几个不一样技术类型的知乎日报APPjavascript

要作微信小程序首先要对htmlcssjs有必定的基础,还有对微信小程序的API也要很是熟悉css

我将该教程分为如下三篇html

  1. 微信小程序开发日记——高仿知乎日报(上)
  2. 微信小程序开发日记——高仿知乎日报(中)
  3. 微信小程序开发日记——高仿知乎日报(下)

三篇分别讲不一样的组件和功能块html5

这篇要讲java

  • API分析
  • 启动页
  • 轮播图
  • 日报列表
  • 浮动按钮
  • 侧滑菜单

API分析

如下是使用到的具体API,更加详细参数和返回结构可参照网上网友分享的 知乎日报-API-分析 ,在此就不作再次分析了。css3

启动界面图片

http://news-at.zhihu.com/api/4/start-image/{size}git

参数 说明
size 图片尺寸,格式:宽*高。例如: 768*1024

获取刚进入应用时的显示封面,能够根据传递的尺寸参数来获取适配用户屏幕的封面。github

获取最新日报

http://news-at.zhihu.com/api/4/news/latest小程序

返回的数据用于日报的首页列表,首页的结构有上下部分,上部分是图片滑动模块,用于展现热门日报,下部分是首页日报列表,以上接口返回的数据有热门日报和首页日报微信小程序

获取日报详细

http://news-at.zhihu.com/api/4/news/{id}

参数 说明
id 日报id

在点击日报列表也的日报项时,须要跳转到日报详情页展现日报的具体信息,这个接口用来获取日报的展现封面和具体内容。

历史日报

http://news.at.zhihu.com/api/4/news/before/{date}

参数 说明
date 年月日格式时间yyyyMMdd,例如:2015090三、20161202

这个接口也是用与首页列表的日报展现,可是不一样的是此接口须要传一个日期参数,如20150804格式。获取最新日报接口只能获取当天的日报列表,若是须要获取前天或者更久以前的日报,则须要这个接口单独获取。

日报额外信息

http://news-at.zhihu.com/api/4/story-extra/{id}

参数 说明
id 日报id

在日报详情页面中,不只要展现日报的内容,好须要额外获取此日报的评论数目和推荐人数等额外信息。

日报长评

http://news-at.zhihu.com/api/4/story/{id}/long-comments

参数 说明
id 日报id

日报的评论页面展现长评用到的接口(没有找到分页参数,分页没有作)

日报短评

http://news-at.zhihu.com/api/4/story/{id}/short-comments

参数 说明
id 日报id

日报的评论页面展现段评用到的接口(没有找到分页参数,分页没有作)

主题日报栏目列表

http://news-at.zhihu.com/api/4/themes

主页的侧边栏显示有主题日报的列表,须要经过这个接口获取主题日报栏目列表

主题日报具体内容列表

http://news-at.zhihu.com/api/4/theme/{themeId}

参数 说明
themeId 主题日报栏目id

在主页侧栏点击主题日报进入主题日报的内容页,须要展现此主题日报下的日报列表。

启动页

做为一个仿制知乎日报的伪APP,高大上的启动封面是必须的,哈哈。启动页面很简单,请求一个应用启动封面接口,获取封面路径和版权信息。当进入页面,在onLoad事件中获取屏幕的宽和高来请求适合尺寸的图片,在onReady中请求加载图片,在请求成果以后,延迟2s进入首页,防止页面一闪而过。

onLoad: function( options ) { var _this = this; wx.getSystemInfo( { success: function( res ) { _this.setData( { screenHeight: res.windowHeight, screenWidth: res.windowWidth, }); } }); }, onReady: function() { var _this = this; var size = this.data.screenWidth + '*' + this.data.screenHeight; requests.getSplashCover( size, ( data ) => { _this.setData( { splash: data }); }, null, () => { toIndexPage.call(_this); }); } /** * 跳转到首页 */ function toIndexPage() { setTimeout( function() { wx.redirectTo( { url: '../index/index' }); }, 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> </block> </swiper>

全部的内容都必需要在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> </view>

代码中有一个显眼的animation属性,这个属性就是用来控制动画的。

/** * 旋转上拉加载图标 */ function updateRefreshIcon() { var deg = 360; var _this = 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] ? 'has-img': ''}}" wx:if="{{item.type != 3}}" data-id="{{item.id}}" bindtap="toDetailPage"> <view class="content"> <text>{{item.title}}</text> </view> <image wx:if="{{item.images[0]}}" src="{{item.images[0]}}" class="cover"></image> </view> <view class="list-spliter" wx:else> <text>{{item.title}}</text> </view> </block> </view>

class="list-spliter"这块是用来显示日期,列表中的日报只要不是同一天的记录,就在中间插入一条日期显示块。在列表项中有一个三元运算判断输出具体的class{{item.images[0] ? 'has-img': ''}},是由于列表中可能没有图片,所以须要断定当前有没有图片,没有图片就不添加class为has-img来控制带有图片列表项的布局。

浮动按钮

由于小程序中没有侧栏组件,没法作到侧滑手势显示侧栏(本人发现touchstart事件和tap事件有冲突,没法实现出手势侧滑判断,因此没有用侧滑手势,多是本人理解太浅了,没有发现解决方法,嘿嘿…),浮动按钮的样式参照了Android中的FloatAction经典按钮。能够浮动在界面上,还能够滑动到任意位置,背景为稍微透明。

<view class="float-action" bindtap="ballClickEvent" style="opacity: {{ballOpacity}};bottom:{{ballBottom}}px;right:{{ballRight}}px;" bindtouchmove="ballMoveEvent"> </view>
.float-action { position: absolute; bottom: 20px; right: 30px; width: 50px; height: 50px; 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 ) return; if( pageX > this.data.screenWidth - 25 ) return; if( this.data.screenHeight - pageY <= 25 ) return; if( pageY <= 25 ) return; 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}}px;width: {{slideWidth}}px;height:{{slideHeight}}px;" 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> <view class="toolbar"> <view class="item"> <image src="../../images/fav.png"></image> <text>收藏</text> </view> <view class="item" bindtap="toSettingPage"> <image src="../../images/setting.png"></image> <text>设置</text> </view> </view> </view> <view class="menu-item home"> <text>首页</text> </view> <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> </view> </block> </view> </scroll-view> </view>
/*slide-menu*/ .slide-mask { position: fixed; width: 100%; top: 0; left: 0; bottom: 0; background: rgba(0, 0, 0, .3); z-index: 800; } .slide-menu { position: fixed; top: 0; background: #FFF; z-index: 900; } /*.slide-menu .slide-inner { padding: 40rpx; }*/ .slide-menu .header { background: #019DD6; height: 200rpx; color: #FFF; padding: 20rpx 40rpx 0 40rpx; } .userinfo { height: 80rpx; line-height: 80rpx; overflow: hidden; } .userinfo .avatar { width: 80rpx; height: 80rpx; border-radius: 50%; margin-right: 40rpx; float: left; } .userinfo text { float: left; font-size: 35rpx; } .toolbar { height: 100rpx; padding-top: 25rpx; line-height: 75rpx; } .toolbar .item { width: 50%; display: inline-block; overflow: hidden; text-align: center } .toolbar .item text { display: inline-block; font-size: 30rpx } .toolbar .item image { display: inline-block; position: relative; top: 10rpx; margin-right: 10rpx; height: 50rpx; width: 50rpx; } .slide-menu .menu-item { position: relative; height: 100rpx; line-height: 100rpx; padding: 0 40rpx; font-size: 35rpx; } .slide-menu .menu-item:active { background: #FAFAFA; } .slide-menu .menu-item image { position: absolute; top: 25rpx; right: 40rpx; width: 50rpx; height: 50rpx; } .slide-menu .home { color: #019DD6 } .slide-menu .close-btn { position: absolute; top: 20rpx; right: 40rpx; z-index: 1000 }

以上是侧栏的一个简单的布局和样式,包含了侧栏中的用户信息块和主题日报列表。固然这些信息是须要经过js的中网络请求来获取的。侧栏结构上边有一个class为slide-mask的view,这是一个遮罩元素,当侧栏弹出的时候,侧栏后边就有一层轻微透明的黑色遮罩。侧栏的高度和宽度初始是不定的,须要在进入页面的时候,立刻获取设备信息来获取屏幕的高度宽度调整侧栏样式。

//获取设备信息,屏幕的高度宽度 onLoad: function() { var _this = 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: 'block' }); animation.translateX( '100%' ).step(); this.setData( { slideAnimation: animation.export() }); } //侧栏关闭 function slideDown() { var animation = wx.createAnimation( { duration: 800 }); animation.translateX( '-100%' ).step(); this.setData( { slideAnimation: animation.export() }); this.setData( { maskDisplay: 'none' }); }

侧栏弹出的时候,遮罩的css属性display设置为block显示,侧栏经过css动画transform来想右侧移动了100%的宽度translateX(100%),侧栏收回时,动画刚好与弹出的相反,其实这些动画最后都会翻译为css3动画属性,这些API只是css3动画的封装。为了点击遮罩收回侧栏,遮罩的tap事件也要绑定slideCloseEvent

//浮动球点击 侧栏展开 ballClickEvent: function() { slideUp.call( this ); }, //遮罩点击 侧栏关闭 slideCloseEvent: function() { slideDown.call( this ); }

效果图

相关文章
相关标签/搜索