微信小程序開發日記——高仿知乎日報(上)

本人對知乎日報是情有獨鍾,看個人博客和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 ); }

效果圖

相關文章
相關標籤/搜索