;

微信小程序天气预报有详细流程和源码

2023-10-12 阅读:1140
;
您当前位置:首页 | 新闻列表 | 新闻详情

这是一个完整的已经线上运行的天气应用小程序。

新版首页(可根据不同的天气改变背景)

其他效果图:

数据来源

地理编码、天气数据均来自百度地图开放平台。个人开发完全免费,有对应的小程序定制 sdk,加入即可,但是返回的天气数据较少。

运行前准备

  • 注册微信小程序,获取 appid
  • 气象数据和风天气,需要注册账号获取key;免费版只能获取三天的天气数据,若要获取七天的气象数据,可以申请个人开发者认证;
  • 在 app.js 中替换 globalData 中的key为自己的key
  • Run

天气数据获取

因为只是一个个人版DEMO(完整版),开发前就决定选择免费的天气数据(个人开发免费),懒得去寻找其他的天气数据,懒得去注册账号,就直接选择了百度地图开放平台的天气数据,正好也提供了小程序对应的 sdk,但是可能相比于其他的天气 API,百度返回的数据偏少:当天 pm2.5、当天和未来三天数据、当天生活指数,其他的就没有了。但是对于一款简单的天气应用小程序来说也够了。

地理编码

获取天气数据默认返回当前城市的天气数据,如果要获取其他的城市的天气数据,需要传入经纬度。为了获取其他城市的经纬度,这里使用的地图的地理编码接口,输入城市名,输出经纬度,然后调用获取天气数据 API 即可。

具体实现

该应用只有五个个页面:首页、设置页、关于页。如下:

首页

首页最终的显示效果是这个样子:

从上到下依次是:其他城市天气搜索、当前城市数据展示、当天和未来三天天气数据展示、当天生活指数展示、footer。下拉刷新会刷新当前地区的天气数据。其中,顶部城市天气搜索和生活指数可以在设置中隐藏。屏幕右下角是一个悬浮球菜单,点击后会弹出设置、关于页面的入口。背景色默认是#40a7e7纯色,未来三天天气预报和生活指数分别添加了透明的黑色背景。设计稿?没有的,纯肉眼调试,直到自己看着舒服。

主页面

先定义一个方法获取当前地区的天气数据:

	
csharp
复制代码
init(params) { let that = this let BMap = new bmap.BMapWX({ ak: globalData.ak, }) BMap.weather({ location: params.location, fail: that.fail, success: that.success, }) },

ak请替换为自己的ak,因为需要获取用户的地理位置,所以在fail的回调中需要处理用户拒绝获取地理位置的逻辑,这里处理为:提示打开地理位置授权,3000ms后wx.openSetting()跳转到小程序设置页,如下:

	
less
复制代码
fail (res) { wx.stopPullDownRefresh() let errMsg = res.errMsg || '' // 拒绝授权地理位置权限 if (errMsg.indexOf('deny') !== -1 || errMsg.indexOf('denied') !== -1) { wx.showToast({ title: '需要开启地理位置权限', icon: 'none', duration: 3000, success (res) { let timer = setTimeout(() => { clearTimeout(timer) wx.openSetting({}) }, 3000) }, }) } else { wx.showToast({ title: '网络不给力,请稍后再试', icon: 'none', }) } },

获取到用户的地理位置后,执行success:

	
kotlin
复制代码
success (data) { wx.stopPullDownRefresh() let now = new Date() // 存下来源数据 data.updateTime = now.getTime() data.updateTimeFormat = utils.formatDate(now, "MM-dd hh:mm") let results = data.originalData.results[0] || {} data.pm = this.calcPM(results['pm25']) // 当天实时温度 data.temperature = `${results.weather_data[0].date.match(/\d+/g)[2]}` wx.setStorage({ key: 'cityDatas', data: data, }) this.setData({ cityDatas: data, }) },

看一下返回的天气数据格式:

	
json
复制代码
{ "error": 0, "status": "success", "date": "2018-06-29", "results": [ { "currentCity": "北京市", "pm25": "55", "index": [ { "des": "天气炎热,建议着短衫、短裙、短裤、薄型T恤衫等清凉夏季服装。", "zs": "炎热", "tipt": "穿衣指数", "title": "穿衣" }, { "des": "较适宜洗车,未来一天无雨,风力较小,擦洗一新的汽车至少能保持一天。", "zs": "较适宜", "tipt": "洗车指数", "title": "洗车" }, { "des": "各项气象条件适宜,发生感冒机率较低。但请避免长期处于空调房间中,以防感冒。", "zs": "少发", "tipt": "感冒指数", "title": "感冒" }, { "des": "天气较好,无雨水困扰,但考虑气温很高,请注意适当减少运动时间并降低运动强度,运动后及时补充水分。", "zs": "较不宜", "tipt": "运动指数", "title": "运动" }, { "des": "属中等强度紫外线辐射天气,外出时建议涂擦SPF高于15、PA+的防晒护肤品,戴帽子、太阳镜。", "zs": "中等", "tipt": "紫外线强度指数", "title": "紫外线强度" } ], "weather_data": [ { "date": "周五 06月29日 (实时:34℃)", "dayPictureUrl": "http://api.map.baidu.com/images/weather/day/duoyun.png", "nightPictureUrl": "http://api.map.baidu.com/images/weather/night/qing.png", "weather": "多云转晴", "wind": "东南风微风", "temperature": "38 ~ 25℃" }, { "date": "周六", "dayPictureUrl": "http://api.map.baidu.com/images/weather/day/duoyun.png", "nightPictureUrl": "http://api.map.baidu.com/images/weather/night/duoyun.png", "weather": "多云", "wind": "东南风微风", "temperature": "36 ~ 23℃" }, { "date": "周日", "dayPictureUrl": "http://api.map.baidu.com/images/weather/day/qing.png", "nightPictureUrl": "http://api.map.baidu.com/images/weather/night/qing.png", "weather": "晴", "wind": "东南风微风", "temperature": "35 ~ 23℃" }, { "date": "周一", "dayPictureUrl": "http://api.map.baidu.com/images/weather/day/qing.png", "nightPictureUrl": "http://api.map.baidu.com/images/weather/night/duoyun.png", "weather": "晴转多云", "wind": "南风微风", "temperature": "35 ~ 25℃" } ] } ] }

success里缓存了最新一次获取的天气数据+更新的时间cityDatas,小程序的模板里无法使用方法,所以数据需要在js里面先格式化。calcPM用来计算当前 pm2.5 的质量,返回“优良差”类似字样,范围标准可自行搜索。当天的实时温度并没有给出独立的字段,而是混在了wearther_data[0]的data字段里:"date": "周五 06月29日 (实时:34℃)",需要自行提取。返回的天气 icon 和色调不搭,就没有使用。其他的数据按照按照我们要显示的格式直接填充即可。

城市天气搜索

获取天气数据传参为经纬度,所以搜索城市天气时,需先将城市转换为对应的经纬度,然后调用获取天气数据 API 即可。获取经纬度的 API 为:

https://api.map.baidu.com/geocoder/v2/?address=${address}&output=json&ak=${yourak}

返回的数据格式为:

	
json
复制代码
{ "status":0, "result":{ "location":{ "lng":117.21081309155257, "lat":39.143929903310074 }, "precise":0, "confidence":12, "level":"城市" } }

然后直接调用获取天气 API 即可。具体代码如下:

	
kotlin
复制代码
geocoder (address, success) { let that = this wx.request({ url: getApp().setGeocoderUrl(address), success (res) { let data = res.data || {} if (!data.status) { let location = (data.result || {}).location || {} // location = {lng, lat} success && success(location) } else { wx.showToast({ title: data.msg || '网络不给力,请稍后再试', icon: 'none', }) } }, fail (res) { wx.showToast({ title: res.errMsg || '网络不给力,请稍后再试', icon: 'none', }) }, complete () { that.setData({ searchText: '', }) }, }) }, search (val) { // 动画 if (val === '520' || val === '521') { this.setData({ searchText: '', }) this.dance() return } wx.pageScrollTo({ scrollTop: 0, duration: 300, }) if (val) { let that = this this.geocoder(val, (loc) => { that.init({ location: `${loc.lng},${loc.lat}` }) }) } },

搜索动画彩蛋

在搜索框里搜索520或521,会出现从顶部下小心心的动画,如下:

这里实现比较简单。

创建了一个heartbeat的组件。wxml结构是遍历数组,创建多个大小、位置随机的图片:

	
arduino
复制代码
<image wx:for='{{arr}}' wx:key='{{index}}' animation='{{animations[index]}}' class='heart' style='left:{{lefts[index]}}px;top:{{tops[index]}}px;width:{{widths[index]}}rpx;height:{{widths[index]}}rpx;' src='/img/heartbeat.png'></image>

然后使用的是小程序提供的wx.createAnimation,动画的使用比较简单,创建动画,然后赋予animation属性即可,比较简单,但是也有局限性,比如,没有直接的动画结束后的回调,但是可以使用setTimeout来实现等。这里会用到可用窗口宽高,因为多处用到了该参数,所以在app.js里面异步获取了先。

动画代码如下:

	
scss
复制代码
dance (callback) { let windowWidth = this.data.windowWidth let windowHeight = this.data.windowHeight let duration = this.data.duration let animations = [] let lefts = [] let tops = [] let widths = [] let obj = {} for (let i = 0; i < this.data.arr.length; i++) { lefts.push(Math.random() * windowWidth) tops.push(-140) widths.push(Math.random() * 50 + 40) let animation = wx.createAnimation({ duration: Math.random() * (duration - 1000) + 1000 }) animation.top(windowHeight).left(Math.random() * windowWidth).rotate(Math.random() * 960).step() animations.push(animation.export()) } this.setData({ lefts, tops, widths, }) let that = this let timer = setTimeout(() => { that.setData({ animations, }) clearTimeout(timer) }, 200) let end = setTimeout(() => { callback && callback() clearTimeout(end) }, duration) }, },

首页搜索特定关键词后,调用组件dance方法即触发小心心动画。

悬浮球菜单

屏幕右下角的悬浮球提供了三个页面的入口:城市选择页、设置页、关于页。菜单弹出、收回会有动画。

这里的动画分为弹出和收起,两者写起来基本上一样的,只是动画的参数不一样。这里贴出弹出的动画:

	
css
复制代码
// wxml <!-- 悬浮菜单 --> <view class='menus'> <image src="/img/location.png" animation="{{animationOne}}" class="menu" bindtap="menuOne" style='top:{{pos.top}}px;left:{{pos.left}}px;'></image> <image src="/img/setting.png" animation="{{animationTwo}}" class="menu" bindtap="menuTwo" style='top:{{pos.top}}px;left:{{pos.left}}px;'></image> <image src="/img/info.png" animation="{{animationThree}}" class="menu" bindtap="menuThree" style='top:{{pos.top}}px;left:{{pos.left}}px;'></image> <image src="/img/menu.png" animation="{{animationMain}}" class="menu main" bindtap="menuMain" catchtouchmove='menuMainMove' style='top:{{pos.top}}px;left:{{pos.left}}px;'></image> </view> // js popp() { let animationMain = wx.createAnimation({ duration: 200, timingFunction: 'ease-out' }) let animationOne = wx.createAnimation({ duration: 200, timingFunction: 'ease-out' }) let animationTwo = wx.createAnimation({ duration: 200, timingFunction: 'ease-out' }) let animationThree = wx.createAnimation({ duration: 200, timingFunction: 'ease-out' }) animationMain.rotateZ(180).step() animationOne.translate(-50, -60).rotateZ(360).opacity(1).step() animationTwo.translate(-90, 0).rotateZ(360).opacity(1).step() animationThree.translate(-50, 60).rotateZ(360).opacity(1).step() this.setData({ animationMain: animationMain.export(), animationOne: animationOne.export(), animationTwo: animationTwo.export(), animationThree: animationThree.export(), }) },

悬浮菜单是可以在屏幕上随意滑动的,方法也很简单,监听touchmove事件即可,因为菜单展开方向是在左边,所以悬浮菜单能往左边移动的最远距离要有一段间隔,否则展开的菜单就进入左边屏幕了,移动到上方同样逻辑(后期可以改成菜单展开方向随移动而改变,而不是一味在左边展开)。

代码如下:

	
ini
复制代码
menuMainMove (e) { // 如果已经弹出来了,需要先收回去,否则会受 top、left 会影响 if (this.data.hasPopped) { this.takeback() this.setData({ hasPopped: false, }) } let windowWidth = SYSTEMINFO.windowWidth let windowHeight = SYSTEMINFO.windowHeight let touches = e.touches[0] let clientX = touches.clientX let clientY = touches.clientY // 边界判断 if (clientX > windowWidth - 40) { clientX = windowWidth - 40 } if (clientX <= 90) { clientX = 90 } if (clientY > windowHeight - 40 - 60) { clientY = windowHeight - 40 - 60 } if (clientY <= 60) { clientY = 60 } let pos = { left: clientX, top: clientY, } this.setData({ pos, }) },

至于一些样式、逻辑上的细节,这里不再赘述,具体可查看源码。

关于页

关于页是一个展示页,没有多少交互,使用到的 API 只有复制到剪切板wx.setClipboardData。“微信快速联系”使用的是小程序提供的联系客服的方式<button open-type="contact" class='btn'></button>,将button绝对定位隐藏到点击区域的下方即可。有精力的话,可以自己搭建服务,将小程序的消息 push 到自己的服务上去。

设置页

设置页的功能看着有点多,其实并不多,只是一堆 API 的调用。这个页面分了自定义、检查更新、小工具、清除数据三个部分。各个设置参数保存在storage中。一个一个来说。

1. 自定义

  • 自定义首页背景

自定义背景是将选取的图片(wx.chooseImage)保存(wx.saveFile)到本地,然后首页获取(wx.getSavedFileList)保存的图片,在首页展示出来即可。长按删除,则是获取(wx.getSavedFileList)保存的图片,然后wx.removeSavedFile掉即可。现在设置的是本地只保存一张图片,所以重新设置其他背景时,会删除上一张背景图,然后重新保存新背景图。

实现如下:

	
javascript
复制代码
defaultBcg () { this.removeBcg(() => { wx.showToast({ title: '恢复默认背景', duration: 1500, }) }) }, removeBcg (callback) { wx.getSavedFileList({ success: function (res) { let fileList = res.fileList let len = fileList.length if (len > 0) { for (let i = 0; i < len; i++) (function (path) { wx.removeSavedFile({ filePath: path, complete: function (res) { if (i === len - 1) { callback && callback() } } }) })(fileList[i].filePath) } else { callback && callback() } }, fail: function () { wx.showToast({ title: '出错了,请稍后再试', icon: 'none', }) }, }) }, customBcg () { let that = this wx.chooseImage({ success: function (res) { that.removeBcg(() => { wx.saveFile({ tempFilePath: res.tempFilePaths[0], success: function (res) { wx.navigateBack({}) }, }) }) }, fail: function (res) { let errMsg = res.errMsg // 如果是取消操作,不提示 if (errMsg.indexOf('cancel') === -1) { wx.showToast({ title: '发生错误,请稍后再试', icon: 'none', }) } }, }) },

  • 打开顶部城市天气快捷搜索

该操作只是将首页的顶部搜索wx:if掉而已。switch组件的样式可以通过修改默认的类来修改,调一个自己满意的即可:

	
css
复制代码
.wx-switch-input{width:84rpx !important;height:43rpx !important;} .wx-switch-input::before{width:82rpx !important;height: 38rpx !important;} .wx-switch-input::after{width: 38rpx !important;height: 38rpx !important;}

  • 显示生活指数信息

同样wx:if掉。

  • 检查更新

检查更新默认关闭。小程序的更新是在冷启动时去检查,如果有新版本会异步下载,再次冷启动时会加载新版本。这里使用wx.getUpdateManager,因为该 API 基础库支持最低版本是 1.9.90,基础库版本低的会提示不支持,显示的文案也会相应修改。

  • 小工具

1)NFC

使用wx.getHCEState。

2)屏幕亮度

获取屏幕亮度、设置屏幕亮度、保持常亮使用的 API 分别是wx.getScreenBrightness、wx.setScreenBrightness、wx.setKeepScreenOn。完整实现可查看源码。

3)系统信息

系统信息会跳转到新页面。


上一篇:公司企业微信自建小程序应用踩坑实践 下一篇:小程序开发费用由哪些组成?

推荐信息

更多+

    联系我们

  • 电话 :134-8274-2882
  • QQ:1059681242
  • 邮箱:haoljq@163.com
  • 地址:上海市松江区米易路166号交科医疗健康产业园(2楼)

在线预约

  • 您的姓名
  • 联系电话

友情链接:

小程序开发 小程序定制开发 小程序商店 微信小程序开发文档 分销商城小程序 电商小程序开发 百家号 商城小程序 微信小程序开发API 小程序定制 生鲜小程序 全平台开发 网站建设 外包开发 自主研发产品 sitemap robots

热门地区:

松江开发公司 青浦开发公司 崇明开发公司 杨浦开发公司 宝山开发公司 奉贤开发公司 虹口开发公司 闵行开发公司 长宁开发公司 静安开发公司 黄浦开发公司 嘉定开发公司 徐汇开发公司 金山开发公司 上海开发公司 南昌开发公司 杭州开发公司 上饶开发公司

COPYRIGHT 2009-2016 www.guanzhiweb.com ALL RIGHTS RESERVED

版权所有 上海观智网络科技有限公司

在线
客服

在线客服服务时间:9:00-21:00

选择下列咨讯方式马上在线沟通:

客服
热线

134-8274-2882
小程序定制开发已服务600+

特价
商城

商城小程序助力每个商家获客盈收

来电咨讯获取商城方案,报价功能清单

小程
序码

扫码加微信咨询
顶部