如何从零开始两天撸一个微信小程序?!(内含源码)

640?wx_fmt=gif

 

640?wx_fmt=jpeg

作者 | myvin

责编 | 伍杏玲

【程序人生 编者按】新人如何入门小程序?不妨尝试做一个实用性强的天气应用小程序吧。下面是作者只用了两天时间写的已上线天气小程序,一起来学学如何做的。

2019程序员转型学什么?

https://edu.csdn.net/topic/ai30?utm_source=cxrs_bw

640?wx_fmt=png

前期准备

 

天气数据

因为这是个人的DEMO,开发前就决定选择免费的天气数据(个人开发免费),我选择了百度地图开放平台的天气数据,正好也提供了小程序对应的 SDK,可能相比于其他的天气 API,百度返回的数据偏少:例如当天 PM2.5、当天和未来三天数据、当天生活指数,但是对于一款简单的天气应用小程序来说也够了。

地理编码

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

运行前准备

 

  1. 注册微信小程序,获取 AppID

  2. 注册百度地图开放平台开发者,创建应用,获取 ak(其他配置自行查看)

  3. 在 app.js 中替换 globalData 中的 ak 为自己的 ak

  4. Run

 

640?wx_fmt=png

具体实现

 

该应用有五个页面:首页、城市选择页、设置页、关于页、系统信息页(展示页)。分别是这样实现的:

首页

从上到下依次是:其他城市天气搜索、当前城市数据展示、当天和未来三天天气数据展示、当天生活指数展示、footer。下拉刷新会刷新当前地区的天气数据。其中,顶部城市天气搜索和生活指数可以在设置中隐藏。屏幕右下角是一个可以移动的悬浮球菜单,点击后会弹出城市选择、设置、关于页面的入口。背景色默认是 #40a7e7 纯色,可在设置中更换背景图,未来三天天气预报和生活指数分别添加了透明的黑色背景。最终实现图如下:

640?wx_fmt=png

主页面

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

 

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() 跳转到小程序设置页,如下:

 
 

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

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

 
 

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',
    datadata,
  })
  this.setData({
    cityDatas: data,
  })
},

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

 
 

{
    "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 即可。

返回的数据格式为:

 
 

{
    "status":0,
    "result":{
        "location":{
            "lng":117.21081309155257,
            "lat":39.143929903310074
        },
        "precise":0,
        "confidence":12,
        "level":"城市"
    }
}

然后直接调用获取天气 API 即可。具体代码可参考文末。

搜索动画彩蛋

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

640?wx_fmt=jpeg

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

 
 

<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 里面异步获取了先。

动画代码如下:

 
 

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({
        durationMath.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 方法即触发小心心动画。

悬浮球菜单

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

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

 
 

// 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(-900).rotateZ(360).opacity(1).step()
  animationThree.translate(-5060).rotateZ(360).opacity(1).step()
  this.setData({
    animationMain: animationMain.export(),
    animationOne: animationOne.export(),
    animationTwo: animationTwo.export(),
    animationThree: animationThree.export(),
  })
},

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

城市选择页

城市选择页面就是一个城市列表,如下图:

640?wx_fmt=jpeg

点击相应的城市,跳转到首页获取所选城市的天气数据。这里的城市数据是这样的格式无序的列表:

 

"letter""B""name""北京市" }

因为需要按照字母排列进行排序,所以需要先排序再遍历,代码如下:

 
 

// 按照字母顺序生成需要的数据格式
getSortedAreaObj(areas) {
  // let areas = staticData.areas
  areas = areas.sort((a, b) => {
    if (a.letter > b.letter) {
      return 1
    }
    if (a.letter < b.letter) {
      return -1
    }
    return 0
  })
  let obj = {}
  for (let i = 0, len = areas.length; i < len; i++) {
    let item = areas[i]
    delete item.districts
    let letter = item.letter
    if (!obj[letter]) {
      obj[letter] = []
    }
    obj[letter].push(item)
  }
  // 返回一个对象,直接用 wx:for 来遍历对象,index 为 key,item 为 value,item 是一个数组
  return obj
},

点击城市后,需要通知首页“我已经切换城市了,麻烦获取下这个城市的数据谢谢”,这里使用的是使用 getCurrentPages 获取页面堆栈,修改首页数据的方式。

关于页

关于页是一个展示页,没有多少交互,使用到的 API 只有复制到剪切板 wx.setClipboardData。“微信快速联系”使用的是小程序提供的联系客服的方式。有精力的话,可以自己搭建服务,将小程序的消息 Push 到自己的服务上去。效果图:

640?wx_fmt=other

设置页

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

1. 自定义

  • 自定义首页背景

自定义背景是将选取的图片(wx.chooseImage)保存(wx.saveFile)到本地,然后首页获取(wx.getSavedFileList)保存的图片,在首页展示出来即可。

长按删除,则是获取(wx.getSavedFileList)保存的图片,然后 wx.removeSavedFile 掉即可。现在设置的是本地只保存一张图片,所以重新设置其他背景时,会删除上一张背景图,然后重新保存新背景图。

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

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

 

.wx-switch-input{width:84rpx !important;height:43rpx !important;}
.wx-switch-input::before{width:82rpx !important;height38rpx !important;}
.wx-switch-input::after{width38rpx !important;height38rpx !important;}

  • 显示生活指数信息

同样 wx:if 。

  • 检查更新

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

  • 小工具

1)NFC

使用 wx.getHCEState。

2)屏幕亮度

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

3)系统信息

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

  • 清除数据

1)首页悬浮球复位

首页悬浮球的位置信息是保存本地的变量 pos,复位位置,清除 pos 即可。

2)恢复初始化设置

设置信息是保存本地的变量 setting,复位位置,清除 setting 即可。

3)清除所有本地数据

wx.clearStorage 即可。

效果图:

640?wx_fmt=other

源码地址:https://github.com/myvin/quietweather

原文链接:https://juejin.im/post/5b39bbcc5188252ce018c745

声明:本文经作者授权转载,如需转载请联系原作者。

 2019程序员转型学什么?

https://edu.csdn.net/topic/ai30?utm_source=cxrs_bw

 

零基础学习小程序?

开发小程序遇到问题不知道问谁?

找不到小程序的组织?

欢迎加入我们的小程序开发者群!

偷偷告诉你,本文作者也在哦!

640?wx_fmt=jpeg

640?wx_fmt=png

640?wx_fmt=gif

 热 文 推 荐 

为什么程序员下班后只关显示器从不关电脑?

“软件外包城”下的马鞍山 | 程序员有话说

程序员求职新思路:互联网巨鳄瓜分 ToB 资源全公开

神操作!程序员如何拿下硅谷顶级公司 200 万年薪?!

可怕!9岁男孩为买任天堂游戏机,竟然...

云评测 | OpenStack智能运维解决方案 @文末有福利!

拿下中科大的计算机课程全靠它了!

月入5万,程序员夫人们过上"贵妇"生活了吗?

 

print_r('点个好看吧!');
var_dump('点个好看吧!');
NSLog(@"点个好看吧!");
System.out.println("点个好看吧!");
console.log("点个好看吧!");
print("点个好看吧!");
printf("点个好看吧!\n");
cout << "点个好看吧!" << endl;
Console.WriteLine("点个好看吧!");
fmt.Println("点个好看吧!");
Response.Write("点个好看吧!");
alert("点个好看吧!")
echo "点个好看吧!"

640?wx_fmt=gif点击“阅读原文”,输入关键词,即可搜索您想要的文章!

640?wx_fmt=png喜欢就点击“好看”吧

THE END
< <上一篇
下一篇>>