“微天氣”微信小程式實戰開發過程
本文節選自高洪濤撰寫的《從零開始學微信小程式開發》一書,由電子工業出版社出版。
責編:陳秋歌,關注微信開發等領域,尋求報道或者投稿請發郵件至chenqg#csdn.net。
在智慧手機軟體的裝機量中,天氣預報類的APP排在比較靠前的位置。說明使用者對天氣的關注度很高。因為人們無論是工作還是度假旅遊等各種活動都需要根據自然天氣來安排。本文將帶大家開發一個“微天氣”小程式,方便微信網友隨時檢視天氣。
天氣預報API
要開發天氣預報類APP,首先要考慮的問題就是天氣預報資料的來源。有了天氣預報的資料來源,才能按需要在微信小程式中顯示出來。其實,微信小程式就是一個顯示天氣資訊的前端系統,而天氣預報API就是後端系統。由於天氣預報API可以在網上免費獲取,因此,本案例中開發者不需要開發後端系統,只需要根據API的要求進行訪問即可。
目前,網際網路上提供的天氣預報API比較多,筆者將幾個主要的API列舉出來,讀者可根據需要使用(當然,本案例只使用其中一個即可)。
中國天氣網天氣預報介面
要查詢天氣預報,當然是以中央氣象臺的資料為權威。中央氣象臺通過“中國天氣”網站對外發布全國各地天氣預報,國內各大入口網站的天氣預報資料都是從這個網站獲取的。如圖1所示就是中國氣象局的天氣預報網站——中國天氣網。
圖1 中國天氣網
在圖1所示的中國天氣網中可檢視全國乃至世界各地的天氣預報資訊,在上方查詢輸入框中輸入一個城市名稱進行查詢,就可檢視到詳細天氣預報資料。例如,輸入“上海”單擊右端的查詢按鈕,就可看到如圖2所示的上海當天的詳細預報。
從圖2所示瀏覽器的位址列可看到其地址為:
這個URL地址中的101020100是上海的一個編碼,如果換成其他城市(如101270101——成都的編碼),則可看到其他城市的天氣預報資訊。
圖2 查詢上海的天氣預報
對於城市編碼這個資料,可以從網站上收集到,將其儲存到一個文字檔案中,查詢時從檔案中讀入即可。例如,將收集到的城市編碼按以下格式儲存到city.txt檔案中。
北京,101010100|北京海淀,101010200|北京朝陽,101010300|北京順義,101010400|北京懷柔,101010500|北京通州,101010600|北京昌平,101010700|北京延慶,101010800|北京豐臺,101010900|北京石景山,101011000|北京大興,101011100|北京房山,101011200|北京密雲,101011300|北京門頭溝,101011400|北京平谷,101011500|上海,101020100|上海閔行,101020200|上海寶山,101020300|上海嘉定,101020500|……
在上面的資料格式中,每一個區域名稱和編碼之間用逗號分隔,而區域之間用豎線分隔。這樣做的好處是可用Python中的split函式分隔資料,具體方法詳見後面的程式碼。
知道城市編碼後,就可通過城市編碼去訪問對應的網頁,得到該城市的天氣預報資料。首先想到的方法當然是用wx.request方法開啟對應的網頁,獲取HTML資料,然後進行分析。不過,這裡對HTML進行分析的過程非常麻煩,且效率不高。
不過,中國天氣網提供了專用的資料介面,通過訪問這些資料介面API,可獲得JSON資料。這樣,就不會有其他雜亂的HTML程式碼來干擾。而微信小程式對JSON資料是可以直接解析的,因此,使用這些API介面是最方便的。
1. 天氣實況資訊
要獲取天氣實況資訊,可通過以下介面:
其中,數字部分是城市編碼,101010100是北京的編碼,因此,上面的介面查詢到的是北京的天氣實況資訊(如果換成101020100,則返回的是上海的天氣實況資訊)。
在瀏覽器中輸入以上URL地址,可得到如圖3所示的結果。
圖3 查詢北京的天氣預報
圖3所示返回的是JSON資料,不過,這裡作為文字顯示,不太容易看得清,整理一下格式,得到的JSON資料如下所示:
{
"weatherinfo": {
"city": "北京",
"cityid": "101010100",
"temp": "18",
"WD": "東南風",
"WS": "1級",
"SD": "17%",
"WSE": "1",
"time": "17:05",
"isRadar": "1",
"Radar": "JC_RADAR_AZ9010_JB",
"njd": "暫無實況",
"qy": "1011",
"rain": "0"
}
}
可看出,返回的JSON物件中有一個weatherinfo物件,其中的各屬性分別表示了天氣預報中的一項資訊,如city是城市名稱,temp是當前溫度,WD風向,WS是風速……
2. 全天天氣預報
要獲取全天天氣預報的資訊,可通過以下介面:
其中,數字部分是城市編碼,101010100是北京的編碼,因此,上面的介面查詢到的是北京的天氣資訊。訪問該介面返回的JSON資料如下所示:
{
"weatherinfo": {
"city": "北京",
"cityid": "101010100",
"temp1": "-2℃",
"temp2": "16℃",
"weather": "晴",
"img1": "n0.gif",
"img2": "d0.gif",
"ptime": "18:00"
}
}
3.天氣詳情
使用以下介面可獲取最詳盡的天氣預報資訊。
以上介面返回的JSON資料格式如下:
{
"weatherinfo": {
"city": "北京",
"city_en": "beijing",
"date_y": "2016年11月16日",
"date": "",
"week": "星期四",
"fchh": "11",
"cityid": "101010100",
"temp1": "2℃~-7℃",
"temp2": "1℃~-7℃",
"temp3": "4℃~-7℃",
"temp4": "7℃~-5℃",
"temp5": "5℃~-3℃",
"temp6": "5℃~-2℃",
"tempF1": "35.6℉~19.4℉",
"tempF2": "33.8℉~19.4℉",
"tempF3": "39.2℉~19.4℉",
"tempF4": "44.6℉~23℉",
"tempF5": "41℉~26.6℉",
"tempF6": "41℉~28.4℉",
"weather1": "晴",
"weather2": "晴",
"weather3": "晴",
"weather4": "晴轉多雲",
"weather5": "多雲",
"weather6": "多雲轉陰",
"img1": "0",
"img2": "99",
"img3": "0",
"img4": "99",
"img5": "0",
"img6": "99",
"img7": "0",
"img8": "1",
"img9": "1",
"img10": "99",
"img11": "1",
"img12": "2",
"img_single": "0",
"img_title1": "晴",
"img_title2": "晴",
"img_title3": "晴",
"img_title4": "晴",
"img_title5": "晴",
"img_title6": "晴",
"img_title7": "晴",
"img_title8": "多雲",
"img_title9": "多雲",
"img_title10": "多雲",
"img_title11": "多雲",
"img_title12": "陰",
"img_title_single": "晴",
"wind1": "北風3-4級轉微風",
"wind2": "微風",
"wind3": "微風",
"wind4": "微風",
"wind5": "微風",
"wind6": "微風",
"fx1": "北風",
"fx2": "微風",
"fl1": "3-4級轉小於3級",
"fl2": "小於3級",
"fl3": "小於3級",
"fl4": "小於3級",
"fl5": "小於3級",
"fl6": "小於3級",
"index": "冷",
"index_d": "天氣冷,建議著棉衣、皮夾克加羊毛衫等冬季服裝。年老體弱者宜著厚棉衣或冬大衣。",
"index48": "冷",
"index48_d": "天氣冷,建議著棉衣、皮夾克加羊毛衫等冬季服裝。年老體弱者宜著厚棉衣或冬大衣。",
"index_uv": "弱",
"index48_uv": "弱",
"index_xc": "適宜",
"index_tr": "一般",
"index_co": "較不舒適",
"st1": "1",
"st2": "-8",
"st3": "2",
"st4": "-4",
"st5": "5",
"st6": "-5",
"index_cl": "較不宜",
"index_ls": "基本適宜",
"index_ag": "極不易發"
}
}
不過,現在中國天氣網已不能通過這個介面獲取資料了。
中華萬年曆的天氣預報介面
中華萬年曆的天氣預報介面地址如下:
該介面很簡單,只需要給出城市的名稱即可,不像中囯天氣網的介面還需要根據城市名稱去查詢城市編碼,然後再去訪問介面。介面返回的資料也是JSON格式,具體形式如下所示:
{
"desc": "OK",
"status": 1000,
"data": {
"wendu": "15",
"ganmao": "晝夜溫差較大,較易發生感冒,請適當增減衣服。體質較弱的朋友請注意防護。",
"forecast": [{
"fengxiang": "北風",
"fengli": "3-4級",
"high": "高溫 14℃",
"type": "晴",
"low": "低溫 3℃",
"date": "19日星期六"
},
{
"fengxiang": "無持續風向",
"fengli": "微風級",
"high": "高溫 4℃",
"type": "雨夾雪",
"low": "低溫 -1℃",
"date": "20日星期天"
},
{
"fengxiang": "北風",
"fengli": "3-4級",
"high": "高溫 0℃",
"type": "小雪",
"low": "低溫 -7℃",
"date": "21日星期一"
},
{
"fengxiang": "北風",
"fengli": "3-4級",
"high": "高溫 -3℃",
"type": "晴",
"low": "低溫 -9℃",
"date": "22日星期二"
},
{
"fengxiang": "無持續風向",
"fengli": "微風級",
"high": "高溫 -3℃",
"type": "多雲",
"low": "低溫 -10℃",
"date": "23日星期三"
}],
"yesterday": {
"fl": "微風",
"fx": "無持續風向",
"high": "高溫 10℃",
"type": "霾",
"low": "低溫 6℃",
"date": "18日星期五"
},
"aqi": "40",
"city": "北京"
}
}
可以看出,以上返回的JSON資料很多,有當天的溫度wendu、感冒描述ganmao,還有forecast陣列中儲存的最近5天的天氣資料,以及yesterday中儲存的昨日天氣資料。
只需要一個介面就可獲得詳細的天氣資訊,因此,本案例選擇使用該API介面。
介面設計
本案例要求介面簡單,儘量在一個頁面中顯示當前天氣、最近五天的天氣,同時,還要提供按城市名稱查詢的功能,可顯示出所查詢城市的天氣預報資訊。UI設計如圖4所示。
圖4 UI設計
在圖4所示UI中,上方顯示所查詢城市的名稱,右側顯示當前日期。接著以較大字號顯示查詢城市的溫度和感冒描述。下方排著5個小卡片顯示最近5天的天氣資訊,最下方接收使用者輸入要查詢的城市名稱,單擊“查詢”按鈕即可查詢指定城市的天氣預報資訊。
當剛開啟該小程式時,由於使用者還沒有輸入查詢城市名稱,需要設定一個預設城市名稱,以方便顯示初始的天氣預報資訊。
編寫介面程式碼
選擇好使用的API並設計好UI介面的佈局之後,就可以建立微信小程式專案,並編寫介面程式碼和邏輯層的JavaScript程式碼了。
建立專案
首先按以下步驟創建出專案。
(1)建立名為ch11的專案目錄。
(2)啟動微信小程式開發工具,在啟動介面中單擊“新增專案”按鈕,開啟如圖5所示的對話方塊。
(3)在圖5所示對話方塊中填寫好相應的專案名稱,並選擇儲存專案的目錄,單擊“新增專案”按鈕即可建立好一個專案的框架。
圖5 新增專案
本專案只有一個頁面,因此也就不需要再新增其他頁面,將index頁面中已有的內容刪除,然後再在index頁面中編寫WXML和JS程式碼即可。
(4)修改顯示標題,開啟app.json檔案,修改成以下內容:
{
"pages":[
"pages/index/index",
"pages/logs/logs"
],
"window":{
"backgroundTextStyle":"light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "微天氣",
"navigationBarTextStyle":"black"
}
}
編寫介面程式碼
根據圖4所示的UI設計,開啟index.wxml檔案,刪除該檔案原有內容,輸入以下wxml程式碼。
<view class="content">
<!--顯示當天的天氣資訊-->
<view class="info">
<!--城市名稱 當前日期-->
<view class="city">{{city}} ({{today}})</view>
<!--當天溫度-->
<view class="temp">{{weather.wendu}}℃</view>
<!--感冒描述-->
<view class="weather">{{weather.ganmao}}</view>
</view>
<!--昨天的天氣資訊-->
<view class="yesterday">
<view class="detail"><text class="yesterday-title">昨天</text>
{{weather.yesterday.date}}</view>
<view class="detail"> {{weather.yesterday.type}} <!--天氣型別,如陰、晴-->
{{weather.yesterday.fx}} <!--風向-->
{{weather.yesterday.fl}} <!--風力-->
{{weather.yesterday.low}} <!--最低溫度-->
{{weather.yesterday.high}} <!--最高溫度-->
</view>
</view>
<!--最近五天天氣資訊-->
<view class="forecast" >
<view class="next-day" wx:key="{{index}}" wx:for="{{weather.forecast}}" >
<!--日期-->
<view class="detail date">{{item.date}}</view>
<!--天氣型別-->
<view class="detail">{{item.type}}</view>
<!--最高溫度-->
<view class="detail">{{item.high}}</view>
<!--最低溫度-->
<view class="detail">{{item.low}}</view>
<!--風向-->
<view class="detail">{{item.fengxiang}}</view>
<!--風力-->
<view class="detail">{{item.fengli}}</view>
</view>
</view>
<!--搜尋-->
<view class="search-area">
<input bindinput="inputing" placeholder="請輸入城市名稱"
value="{{inputCity}}" />
<button type="primary" size="mini" bindtap="bindSearch">查詢</button>
</view>
</view>
以上wxml程式碼添加了註釋,每一部分的作用都在註釋中進行了描述。
編寫介面樣式程式碼
儲存以上wxml程式碼之後,在開發工具左側的預覽區中並沒有看到如圖4中的UI效果。為了達到設計的佈局效果,需要編寫樣式程式碼對wxml元件進行控制。其實,在上面的wxml程式碼中,已經為各元件設定了class屬性,接下來只需要在index.wxss中針對每一個class編寫相應的樣式程式碼即可,具體程式碼如下:
.content{
height: 100%;
width:100%;
display:flex;
flex-direction:column;
font-family: 微軟雅黑, 宋體;
box-sizing:border-box;
padding:20rpx 10rpx;
color: #252525;
font-size:16px;
background-color:#F2F2F8;
}
/*當天天氣資訊*/
.info{
margin-top:50rpx;
width:100%;
height:160px;
}
/*城市名稱*/
.city{
margin: 20rpx;
border-bottom:1px solid #043567;
}
/*當天溫度*/
.temp{
font-size: 120rpx;
line-height: 130rpx;
text-align: center;
padding-top:20rpx;
color:#043567;
}
/*感冒描述*/
.weather{
line-height: 22px;
margin: 10px 0;
padding: 0 10px;
}
/*昨天天氣資訊*/
.yesterday{
width:93%;
padding:20rpx;
margin-top:50rpx;
border-radius:10rpx;
border:1px solid #043567;
}
/*昨天的*/
.yesterday-title{
color:red;
}
/*最近五天天氣資訊*/
.forecast{
width: 100%;
display:flex;
margin-top:50rpx;
align-self:flex-end;
}
/*每一天的天氣資訊*/
.next-day{
width:20%;
height:450rpx;
text-align:center;
line-height:30px;
font-size:14px;
margin: 0 3rpx;
border:1px solid #043567;
border-radius:10rpx;
}
/*日期*/
.date{
margin-bottom:20rpx;
border-bottom:1px solid #043567;
color:#F29F39;
}
/*搜尋區域*/
.search-area{
display:flex;
background: #f4f4f4;
padding: 1rem 0.5rem;
}
/*搜尋區域的輸入框*/
.search-area input{
width:70%;
height: 38px;
line-height: 38px;
border: 1px solid #ccc;
box-shadow: inset 0 0 10px #ccc;
color: #000;
background-color:#fff;
border-radius: 5px;
}
/*搜尋區的按鈕*/
.search-area button{
width: 30%;
height: 40px;
line-height: 40px;
margin-left: 5px;
}
在上面的wxss程式碼中,每一個class設定前都有相應的註釋,可與wxml程式碼對應起來。
儲存好index.wxss檔案之後,開發工具左側預覽區可看到如圖6所示的介面效果。
圖6 介面效果
編寫邏輯層程式碼
由於在index.js中還沒有設定初始化資料,所以在圖6所示介面中看不到具體的資料,從而也導致介面的效果沒達到設定的要求。
接下來就編寫邏輯層程式碼index.js,為了檢查介面設計效果,首先編寫初始資料,然後再逐步深入地編寫其他相關業務邏輯程式碼。
編寫資料初始化程式碼
在index.wxml中編寫了很多資料,因此需要在index.js中先把這些資料進行初始化,然後在開發工具的模擬器中就可預覽結果。
開啟index.js檔案,刪除原來的內容,重新編寫以下程式碼:
Page({
data: {
weather:{
wendu:18,
ganmao:'晝夜溫差較大,較易發生感冒,請適當增減衣服。體質較弱的朋友請注意防護。',
yesterday:{
date:'17日星期四',
type:'陰',
fx:'南風',
fl:'微風級',
low:'低溫 8℃',
high:'高溫 16℃'
},
forecast:[
{
date:'18日星期五',
type:'陰',
high:'高溫 16℃',
low:'低溫 8℃',
fengxiang:'南風',
fengli:'微風級'
},{
date:'18日星期五',
type:'陰',
high:'高溫 16℃',
low:'低溫 8℃',
fengxiang:'南風',
fengli:'微風級'
},{
date:'18日星期五',
type:'陰',
high:'高溫 16℃',
low:'低溫 8℃',
fengxiang:'南風',
fengli:'微風級'
},{
date:'18日星期五',
type:'陰',
high:'高溫 16℃',
low:'低溫 8℃',
fengxiang:'南風',
fengli:'微風級'
},{
date:'18日星期五',
type:'陰',
high:'高溫 16℃',
low:'低溫 8℃',
fengxiang:'南風',
fengli:'微風級'
}
]
},
today:'2016-11-18',
city:'北京', //城市名稱
inputCity:'', //輸入查詢的城市名稱
}
})
編寫好以上初始化資料之後,儲存index.js,在開發工具左側預覽區域可看到如圖7所示的介面效果。
圖7 介面效果
以上程式碼很長,主要是由於模擬了5天的天氣資料,實際上,在小程式執行時,應該在開啟小程式之後就馬上通過API獲取天氣資料,因此上面的初始化資料程式碼中,只需要用以下語句將weather初始化為一個空物件即可,而上面新增在weather中的屬性資料都可以刪除。
weather:{}
獲取當前位置的城市名稱
根據本案例的要求,當用戶開啟本案例之後,首先要獲取使用者當前所在城市的天氣資訊,這就需要獲取使用者當前所在城市的名稱。要完成這個功能,需要經過幾個轉折。
首先,可以使用微信小程式的獲取當前地理位置經緯度的API(就是wx. getLocation),通過該API即可獲取使用者所在位置的經緯度。
有了使用者所在的經緯度,還需要查詢該經緯度對應的城市名稱。這可以使用百度地圖的介面來實現,百度地圖Geocoding API服務地址如下:
呼叫該介面需要傳遞以下幾個引數。
- output:設定介面返回的資料格式為json或者xml。
- ak:這是必須設定的一個引數,是使用者在百度申請註冊的key,自v2開始引數修改為“ak”,之前版本引數為“key”。
- sn:若使用者所用ak的校驗方式為sn校驗時該引數必須啟用。
- callback:一個回撥函式,將json格式的返回值通過callback函式返回以實現jsonp功能。
例如,在瀏覽器中輸入以下地址:
返回的JSON格式如下所示:
{
"status": 0,
"result": {
"location": {
"lng": 104.06654099999996,
"lat": 30.572268897395259
},
"formatted_address": "四川省成都市武侯區G4201(成都繞城高速)",
"business": "",
"addressComponent": {
"country": "中國",
"country_code": 0,
"province": "四川省",
"city": "成都市",
"district": "武侯區",
"adcode": "510107",
"street": "G4201(成都繞城高速)",
"street_number": "",
"direction": "",
"distance": ""
},
"pois": [],
"poiRegions": [],
"sematic_description": "環球中心w6區西南108米",
"cityCode": 75
}
}
在以上JSON資料中,通過result.addressComponent.city可獲取傳入經緯度對應的城市名稱。因此,在本案例中可通過這種方式獲取使用者當前所在城市的名稱。
根據以上分析,在index.js的onLoad事件處理函式中編寫如下所示程式碼:
var util = require('../../utils/util.js');
Page({
data: {
……
},
onLoad: function (options) {
this.setData({
today:util.formatTime(new Date()).split(' ')[0] //更新當前日期
});
var self = this;
wx.getLocation({
type: 'wgs84',
success: function (res) {
wx.request({
url:'http://api.map.baidu.com/geocoder/v2/' +
'?ak=ASAT5N3tnHIa4APW0SNPeXN5&location='+
res.latitude+',' + res.longitude + '&output=json&pois=0',
data: {},
header: {
'Content-Type': 'application/json'
},
success: function (res) {
var city = res.data.result.addressComponent.city.replace('市','');//城市名稱
self.searchWeather(city); //查詢指定城市的天氣資訊
}
})
}
})
},
})
以上程式碼中,第1行使用require匯入工具方法,用來格式化日期。
根據城市名稱獲取天氣預報
獲取了城市名稱,接下來就可使用以下介面獲取指定城市名稱的天氣預報資訊:
在上面的介面中,城市名稱中不包含“市”這個字,如“成都市”只需要傳入“成都”。
在本節前面介紹該介面時,只查看了介面執行成功後返回的JSON資料,如果傳入的城市名稱有誤,則返回如下所示JSON資料:
{
"desc": "invilad-citykey",
"status": 1002
}
在程式中可通過status判斷資料查詢是否成功。
由於根據城市名稱查詢天氣預報資訊的程式碼需要重複呼叫,因此,單獨編寫成一個函式,方便在查詢時呼叫。
//根據城市名稱查詢天氣預報資訊
searchWeather:function(cityName){
var self = this;
wx.request({
//天氣預報查詢介面
url: 'http://wthrcdn.etouch.cn/weather_mini?city='+cityName,
data: {},
header: {
'Content-Type': 'application/json'
},
success: function (res) {
if(res.data.status == 1002) //無此城市
{
//顯示錯誤資訊
wx.showModal({
title: '提示',
content: '輸入的城市名稱有誤,請重新輸入!',
showCancel:false,
success: function(res) {
self.setData({inputCity:''});
}
})
}else{
var weather = res.data.data; //獲取天氣資料
for(var i=0;i<weather.forecast.length;i++)
{
var d = weather.forecast[i].date;
//處理日期資訊,新增空格
weather.forecast[i].date = ' ' + d.replace('星期',' 星期');
}
self.setData({
city:cityName, //更新顯示城市名稱
weather:weather, //更新天氣資訊
inputCity:'' //清空查詢輸入框
})
}
}
})
}
在上面程式碼中,獲取的date中儲存的是“19日星期六”這種格式的字串,為了使日期和星期分別顯示在兩行中,這裡使用了一種小技巧,就是在日期字串中添加了2個全形狀態的空格,這樣在顯示這個字串時自動斷行。
編寫好以上這些程式碼之後,儲存,在開發工具左側可看到已經獲取當前的天氣資料,而不是前面初始化的資料了,如圖8所示。
圖8 使用者所在地天氣預報
這樣,本案例的主要程式碼就算編寫完成了。不過,還只能顯示使用者當前所在地的天氣資訊,如果要檢視其他城市的天氣,還需要繼續編寫相應的查詢程式碼。
查詢天氣預報
查詢程式碼的編寫很簡單,只需要獲取使用者輸入的城市名稱,然後傳入searchWeather函式即可。具體的程式碼如下:
//輸入事件
inputing:function(e){
this.setData({inputCity:e.detail.value});
},
//搜尋按鈕
bindSearch:function(){
this.searchWeather(this.data.inputCity);
}
儲存以上程式碼之後,在開發工具左側模擬器中輸入查詢的城市名稱,如輸入“三亞”,單擊“查詢”按鈕,介面中即可顯示“三亞”的天氣資訊,如圖9所示。
圖9 查詢城市天氣
如果在下方輸入框輸入一個不存在的城市名稱,將顯示如圖11-10所示的提示資訊。
圖10 城市名稱錯誤的提示