Update:

I. The meteorological data is changed from baidu Map open platform to mild weather, so you need to register an account to obtain the key;

II. D0e51c8 is the cloud development version of the small program. If the cloud development function is not enabled, you can reset the version number to git reset D0e51C8 –hard, or note out the code related to cloud development, so as not to affect the normal operation of the small program. Details can be found here.

Introduction to the

This is a complete already running online weather application small procedures, click to view the source code, can optionally star. You can also scan the small program code below to experience directly. Incidentally recommend another open source small program nuggets third party version, source code here.

New homepage (optional built-in background)

Effect:

The data source

Geocoding and weather data are all from baidu Map open platform. Personal development is completely free, there is a corresponding small program SDK, you can join, but the return of less weather data.

Pre-operation preparation

  • Register wechat mini program to get appID
  • Register baidu Map open platform developers, create applications, obtain AK (other configurations to check by yourself)
  • Replace the globalData AK with your own AK in app.js
  • Run

stakeholders

There is no

Weather data acquisition

Because only a personal DEMO version (full version), development before they decided to choose free weather data (personal development) for free, too lazy to find other weather data, too lazy to register account, choose the baidu map directly open platform of weather data, just also corresponding SDK provides a small program, but the weather may be compared to other API, Baidu returns less data: pm2.5 for the day, data for the day and the next three days, daily life index, and nothing else. But it’s enough for a simple weather app.

geocoding

By default, the weather data of the current city is returned. If you want to obtain the weather data of other cities, you need to pass in the longitude and latitude. To get the latitude and longitude of other cities, use the geocoding interface of the map here, input the city name, output latitude and longitude, and then call the API to get the weather data.

The specific implementation

The app has five separate pages: home, City selection, Settings, About, and System info (show). As follows:

Home page

The home page should look something like this:

From top to bottom: weather search for other cities, current city data display, weather data display for the current day and the next three days, daily life index display, footer. The refresh drop-down refreshes the weather data for the current region. Among them, the top city weather search and living index can be hidden in Settings. In the bottom right corner of the screen is a floating ball that moves. Menu, after clicking will pop up the city selection, Settings, about page entrance. The default background color is # 40a7E7 solid color, which can be changed in Settings. The next three days weather forecast and life index add a transparent black background respectively. The design draft? No, just test with your eyes until you feel comfortable.

The main page

Define a method to get weather data for the current region:

init(params) {
  let that = this
  let BMap = new bmap.BMapWX({
    ak: globalData.ak,
  })
  BMap.weather({
    location: params.location,
    fail: that.fail,
    success: that.success,
  })
},
Copy the code

Please replace AK with your own AK, because the user’s geographical location needs to be obtained, so the logic of user refusing to obtain geographical location needs to be processed in the callback of FAIL, which is as follows: prompt to open geographical location authorization, and after 3000ms, wx.opensetting () will jump to the small program setting page, as follows:

fail (res) {
  wx.stopPullDownRefresh()
  let errMsg = res.errMsg || ' '
  // Refuse to grant location permissions
  if (errMsg.indexOf('deny')! = =- 1 || errMsg.indexOf('denied')! = =- 1) {
    wx.showToast({
      title: 'Need to enable geolocation permissions'.icon: 'none'.duration: 3000,
      success (res) {
        let timer = setTimeout((a)= > {
          clearTimeout(timer)
          wx.openSetting({})
        }, 3000)}})}else {
    wx.showToast({
      title: 'Network is not working, please try again later'.icon: 'none',}}}),Copy the code

After obtaining the user’s location, execute Success:

success (data) {
  wx.stopPullDownRefresh()
  let now = new Date(a)// Save source data
  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'])
  // Real-time temperature of the day
  data.temperature = `${results.weather_data[0].date.match(/\d+/g) [2]}`
  wx.setStorage({
    key: 'cityDatas'.data: data,
  })
  this.setData({
    cityDatas: data,
  })
},
Copy the code

Take a look at the returned weather data format:

{
    "error": 0."status": "success"."date": "2018-06-29"."results": [{"currentCity": "Beijing"."pm25": "55"."index": [{"des": "In hot weather, we recommend cool summer clothes such as short shirts, short skirts, shorts and thin T-shirts."."zs": "Hot"."tipt": "Dressing index"."title": "Dress"
                }, 
                {
                    "des": "It is more suitable for car washing, there is no rain in the future, the wind is small, and the new car can be cleaned for at least one day."."zs": "More suitable"."tipt": Car Wash index."title": "Car"
                }, 
                {
                    "des": "The weather conditions are favorable and the incidence of cold is low. But please avoid being in an air-conditioned room for a long time to avoid catching a cold."."zs": "Less"."tipt": "Cold index"."title": "Cold"
                }, 
                {
                    "des": "The weather is good, no rain trouble, but considering the high temperature, please pay attention to appropriately reduce the time and intensity of exercise, exercise in time to replenish water."."zs": "Less suitable"."tipt": "Activity index"."title": "Movement"
                }, 
                {
                    "des": "It is a moderate uv radiation day. It is recommended to wear sunscreen skin care products with SPF higher than 15 and PA+, and wear hats and sunglasses when going out."."zs": "Medium"."tipt": "Uv Intensity Index"."title": "Uv intensity"}]."weather_data": [{"date": "Friday June 29th (real-time: 34℃)"."dayPictureUrl": "http://api.map.baidu.com/images/weather/day/duoyun.png"."nightPictureUrl": "http://api.map.baidu.com/images/weather/night/qing.png"."weather": "Cloudy to clear"."wind": "Southeast breeze"."temperature": "38 ~ 25 ℃"
                }, 
                {
                    "date": "Saturday"."dayPictureUrl": "http://api.map.baidu.com/images/weather/day/duoyun.png"."nightPictureUrl": "http://api.map.baidu.com/images/weather/night/duoyun.png"."weather": "Cloudy"."wind": "Southeast breeze"."temperature": "36 ~ 23 ℃"
                }, 
                {
                    "date": "Sunday"."dayPictureUrl": "http://api.map.baidu.com/images/weather/day/qing.png"."nightPictureUrl": "http://api.map.baidu.com/images/weather/night/qing.png"."weather": "Fine"."wind": "Southeast breeze"."temperature": "35 ~ 23 ℃"
                }, 
                {
                    "date": "On Monday"."dayPictureUrl": "http://api.map.baidu.com/images/weather/day/qing.png"."nightPictureUrl": "http://api.map.baidu.com/images/weather/night/duoyun.png"."weather": "Cloudy and sunny"."wind": "South Breeze"."temperature": "35 ~ 25 ℃"}]}]}Copy the code

Success caches the latest acquired weather data + updated cityDatas. Methods cannot be used in the template of the small program, so the data needs to be formatted in JS first. CalcPM is used to calculate the quality of the current PM2.5, and the words “good and bad” are returned. The range standard can be searched by itself. “Date “:” Friday June 29th (real time: 34℃)” is mixed with the data field of Wearther_data [0]. The returned weather icon does not match the color, so it is not used. Other data can be directly filled in according to the format we want to display.

Urban weather search

The parameter to obtain weather data is longitude and latitude, so when searching city weather, you need to convert the city to the corresponding longitude and latitude first, and then call the API to obtain weather data. The API for obtaining latitude and longitude is:

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

The data returned is in the following format:

{
    "status": 0."result": {"location": {"lng": 117.21081309155257."lat":39.143929903310074
        },
        "precise": 0."confidence": 12."level":"City"}}Copy the code

Then call the get weather API directly. The specific code is as follows:

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 || 'Network is not working, please try again later'.icon: 'none',
        })
      }
    },
    fail (res) {
      wx.showToast({
        title: res.errMsg || 'Network is not working, please try again later'.icon: 'none',
      })
    },
    complete () {
      that.setData({
        searchText: ' ',
      })
    },
  })
},
search (val) {
  / / animation
  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}`}}}})),Copy the code

Search for animated eggs

If you search for 520 or 521 in the search box, you will see an animation of the heart from the top down, as follows:

This is a simpler implementation.

Create a heartbeat component. The WXML structure iterates through a group of numbers to create multiple images of random size and position:

<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>
Copy the code

Create the animation and then assign the animation properties. This is relatively simple, but it has some limitations. For example, there is no direct callback after the animation ends, but setTimeout can be used to achieve this. The available window width is used here, and because this parameter is used in many places, it is retrieved asynchronously in app.js.

The animation code is as follows:

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((a)= > {
      that.setData({
        animations,
      })
      clearTimeout(timer)
    }, 200)
    let end = setTimeout((a)= > {
      callback && callback()
      clearTimeout(end)
    }, duration)
  },
},
Copy the code

After searching for a specific keyword on the home page, the component dance method is called to trigger the careful heart animation.

Hover ball menu

The floating ball in the bottom right corner of the screen provides access to three pages: the City Selection page, the Settings page, and the About page. Menu pop-ups and retractions will have animation.

Here the animation is divided into pop and fold, both written basically the same, but the animation parameters are not the same. A pop-up animation is posted here:

// wxml<! -- Hover menu --><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(),
  })
},
Copy the code

The hover menu can be slid on the screen at will. The method is also very simple. Just listen for the TouchMove event. The same logic applies to moving to the top of the menu.

The code is as follows:

menuMainMove (e) {
  // If it is already out, it needs to be removed first, otherwise it will be affected by top and 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
  // boundary judgment
  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,
  })
},
Copy the code

As for some style, logic details, here is no longer redundant, specific can view the source.

City Selection page

The city selection page is a list of cities as follows:

Click the corresponding city to jump to the home page to get the weather data of the selected city. The city data here is an unordered list in this format:

{"letter": "B", "name": "Beijing"}

Because it needs to be sorted in alphabetical order, it needs to be sorted first and then traversed (the city data is the data used before, so it is directly pasted over without sorting). The code is as follows:

// Generate the required data format alphabetically
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 is the key, item is the value, and item is an array
  return obj
},
Copy the code

After clicking on a city, you need to notify the home page “I have switched cities, please get the data of this city thank you”. Here you use getCurrentPages to get the page stack and modify the home page data. The code is as follows:

choose(e) {
  let item = e.currentTarget.dataset.item
  let name = item.name
  let pages = getCurrentPages()
  let len = pages.length
  let indexPage = pages[len - 2]
  indexPage.setData({
    // Whether to switch cities
    cityChanged: true.// The city to be queried
    searchCity: name,
  })
  wx.navigateBack({})
},
Copy the code

On the page

About the page being a presentation page, there is not much interaction and the API used is just copied to the wx.setclipboardData. , the absolute position of the button hidden under the click area can be. If you have the energy, you can build your own service and push messages from applets to your own service.

Settings page

The Settings page may seem like a lot of functionality, but it’s not much, just a bunch of API calls. This page is divided into three parts: customize, check for updates, gadgets, and clear data. Each setting parameter is saved in storage. One at a time.

1. The custom

  • Customize the home page background

The custom background is to save the selected image (wx.ChooseImage) (wx.savefile) to the local, and then get the home page (wx.getSavedFilelist) saved image, display it on the home page. Wx.getsavedfilelist – Remove the list of images saved by wx.getSavedFilelist – remove the list of images saved by wx.removesavedFile It is now set to save only one image locally, so resetting other backgrounds will delete the previous one and then save the new one.

The implementation is as follows:

defaultBcg () {
  this.removeBcg((a)= > {
    wx.showToast({
      title: 'Restore default background'.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: 'Error, please try again later'.icon: 'none',
      })
    },
  })
},
customBcg () {
  let that = this
  wx.chooseImage({
    success: function (res) {
      that.removeBcg((a)= > {
        wx.saveFile({
          tempFilePath: res.tempFilePaths[0].success: function (res) {
            wx.navigateBack({})
          },
        })
      })
    },
    fail: function (res) {
      let errMsg = res.errMsg
      // If the operation is cancelled, no prompt will be given
      if (errMsg.indexOf('cancel') = = =- 1) {
        wx.showToast({
          title: 'Error occurred, please try again later'.icon: 'none',})}},})},Copy the code
  • Open top city weather quick search

All this does is remove the search wx:if from the top of the home page. The style of the switch component can be changed by modifying the default class:

.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; }Copy the code
  • Displays life index information

Likewise wx:if dropped.

  • Check the update

Check for updates is off by default. Updates to applets are checked during cold startup. If a new version is downloaded asynchronously, it will be loaded upon cold startup again. Wx. getUpdateManager is used here, because the lowest version of the API base library support is 1.9.90, the base library version is low will prompt not support, display copy will be modified accordingly.

  • Small tools

1) the NFC

The use of wx. GetHCEState.

2) Screen brightness

To get the brightness of the screen, to set the brightness of the screen, to keep the brightness of the screen on, the APIS are wx.getScreenBrightness, wx.setScreenBrightness, and wx. setScreenScreenOn respectively. Complete implementation can be viewed source code.

3) System information

The system information page is displayed.

  • Clear data

1) The suspension ball on the home page is reset

The position information of the floating ball on the home page is to save the local variable POS, which can be reset to clear POS.

2) Restore the initialization Settings

Setting information is to save the local variable setting, reset the position, clear the setting.

3) Clear all local data

Wx. ClearStorage can.

Tip: Restore initialization Settings, clear all local data and not delete Settings background (if any), this can be added later.

other

Other code details, no further details, specific can view the source code.

Another open source applets recommended

Nuggets small program: article here: two weeks of a nuggets micro channel small program, source here.