微信小程序基于高德地图API实现天气组件(动态效果)
在社区翻腾了许久,没有找到合适的天气插件。迫不得已,只好借鉴互联网上的web
项目,手动迁移到小程序中使用。现在分享到互联网社区中,帮助后续有需要的开发者。
1.组件介绍
1.1 组件效果预览图
小程序组件继承了外部样式colorui
的色彩,但实际动画会根据父节点的color
属性自动填充颜色,即使不引入colorui
这个样式库,也可以在该组件引用外定义一个有color
属性的块包裹该组件,同样可以达到如图的效果。
1.2 构造形式
1.3 支持的动画效果
简单介绍下,动画由3个部分组成
一个是主体块
,这几个动画中的大云朵就是;第二个是背景块
,如第一个中的太阳和第三个中的多层云;第三个就是状态块
,如第一个中的雨水和第二个中的雷。每个块有且仅能展示一个。可以根据自己的需要,自行组合这几个块,来满足对应的天气需求。
注:如想要实现雷雨交加的效果,需要定义两个动画,一个是雷一个是雨,然后通过定时器进行动画的来回切换,如果有完成的可以在评论里留下代码,我懒得实现了,哈哈。
2.组件的使用
组件的使用,需要授权获取位置信息,在app.json
中配置授权。
"permission": { "scope.userLocation": { "desc": "你的位置信息将用于定位效果和天气信息展示" } }
组件配置完成后,在全局app.json
中进行引入。
"usingComponents": { "uweather":"animation/uweather/weather" }
组件有两种模式:
用户自定义模式默认模式(引入amap-wx.js
,申请高德地图key
,具体步骤参见参考文档第一个)
- 用户自定义模式下,所有的信息包括动画和信息展示,都由用户传入的信息来控制。
- 默认模式下,即用户未传入任何信息,这时候组件就会基于位置信息,请求高德地图对应接口来获取地理位置及其天气信息。
组件在被创建的时候会检测是否有对应值的传入,如果有值传入,那么就是用户自定义模式,如果没有值传入,那么就是默认模式。
lifetimes:{ attached(){ if(this.properties.winfo == null){ this.setData({ amapPlugin: new amap.AMapWX({ key: this.data.key }) },()=>{ //获取天气信息 this.getWeather() }) } } },
2.1 自定义模式
自定义模式下,传入的数据要按照规定的的格式(也可以自己修改组件的属性值)
例如在page
中配置的属性如下
weather:'雷', winfo:{ province:'自定义省份', city:'自定义城市', temperature:'自定义温度', weather:'自定义天气', winddirection:'自定义风向', windpower:'自定义风力' }
wxml
页面中的组件使用如下
<uweather weather="{{weather}}" winfo="{{winfo}}" > </uweather>
那么对应的组件展示效果就是这样子的
2.2 默认模式
默认模式需要获得用户的地理位置信息授权,确认在app.json
中进行了授权配置和使用组件前完成了授权信息的校验。
组件生命周期会在每一次组件被装如页面树时,监听是否有对应数据的传入,如果没有,就会请求对应的接口,获取地图信息。使用默认方法,还需要配置 https://restapi.amap.com 为合法的request
域名和申请对应的key
用于开发,申请步骤参见参考文档。
默认模式下不需要传入任何参数,直接引入组件即可。
<uweather> </uweather>
3.组件使用注意事项
默认方法的天气返回值具有很多种,具体使用还需要自己修改组件,完成不同天气到对应动画的映射,例如小雨、中雨、大雨都可以映射到雨
这个动画状态。下图是高德地图天气API的部分信息,全部请参见参考文档。
4.组件代码
详细的组件在项目中的使用结构 请看[开源项目](miniprogram/animation/uweather · Kindear/校园小程序 - 码云 - 开源中国 (gitee.com)),记得给个⭐,感谢。
uweather.js
// animation/uweather/rain.js const amap = require('../../lib/amap-wx.js'); Component({ options: { addGlobalClass: true, multipleSlots: true }, /** * 组件的属性列表 */ properties: { weather:{ type:String, value:'', observer:function(n,o){ //天气变化 } }, winfo:{ type:Object, value:null, observer:function(n,o){ //如果有自定义的值就使用自定义的值 this.setData({ obj:n }) } } }, /** * 组件的初始数据 */ data: { amapPlugin: null, key: "6799b5f6f88d3d9fb52ac244855a8759", obj:{}, }, lifetimes:{ attached(){ if(this.properties.winfo == null){ this.setData({ amapPlugin: new amap.AMapWX({ key: this.data.key }) },()=>{ this.getWeather() }) } } }, /** * 组件的方法列表 */ methods: { //获取天气数据 getWeather:function(){ wx.showLoading({ title: '请稍候...' }) // type:天气的类型。默认是live(实时天气),可设置成forecast(预报天气)。 // city:城市对应的adcode,非必填。为空时,基于当前位置所在区域。 如:440300,返回深圳市天气 // success(data) :调用成功的回调函数。 // fail(info) :调用失败的回调函数。 this.data.amapPlugin.getWeather({ success: (data) =>{ //成功回调 console.log(data) wx.hideLoading() this.setData({ obj:data.liveData, }) if(this.properties.weather == ''){ this.setData({ weather:data.liveData.weather }) } }, fail: function (info) { //失败回调 console.log(info) } }) }, } })
uweather.wxml
<view class="padding-sm"> <view class="bg-gradual-blue padding radius shadow-blur" style="display: flex;flex-direction: row;"> <view style="width:50%;height:100%;color:#1A94E6;"> <view class="icon sun-shower " wx:if="{{weather == '太阳雨'}}"> <view class="cloud"></view> <view class="sun"><view class="rays"></view></view> <view class="rain"></view> </view> <view class="icon sun-shower " wx:if="{{weather == '多云'}}"> <view class="cloud"></view> <view class="sun"><view class="rays"></view></view> </view> <view class="icon thunder-storm" wx:if="{{weather == '雷'}}"> <view class="cloud"></view> <view class="lightning"> <view class="bolt"></view> <view class="bolt"></view> </view> </view> <view class="icon cloudy" wx:if="{{weather == '阴'}}"> <view class="cloud"></view> <view class="cloud"></view> </view> <view class="icon flurries" wx:if="{{weather == '雪'}}"> <view class="cloud"></view> <view class="snow"> <view class="flake"></view> <view class="flake"></view> </view> </view> <view class="icon sunny" wx:if="{{weather == '晴'}}"> <view class="sun"><view class="rays"></view></view> </view> <view class="icon rainy" wx:if="{{weather == '雨'}}"><view class="cloud"></view></view> </view> <!--文字部分--> <view style="width:50%;height:100%;"> <view class="title"> <view class="text-cut" style="margin-top:20rpx;">{{obj.province}}-{{obj.city}}</view> <!--view class="text-cut">湿度:{{obj.humidity.data}}</view--> <view class="text-cut" style="margin-top:20rpx;">温度:{{obj.temperature}}℃</view> <view class="text-cut" style="margin-top:20rpx;">天气:{{obj.weather}}</view> <view class="text-cut" style="margin-top:20rpx;">{{obj.winddirection}}风{{obj.windpower}}级</view> </view> </view> </view> </view>
uweather.wxss
body { max-width: 42em; padding: 2em; margin: 0 auto; color: #161616; font-family: 'Roboto', sans-serif; text-align: center; background-color: currentColor; } h1 { margin-bottom: 1.375em; color: #fff; font-weight: 100; font-size: 2em; text-transform: uppercase; } p, a { color: rgba(255,255,255,0.3); font-size: small; } p { margin: 1.375rem 0; } .icon { position: relative; display: inline-block; width: 12em; height: 10em; font-size: 1em; /* control icon size here */ } .cloud { position: absolute; z-index: 1; top: 50%; left: 50%; width: 3.6875em; height: 3.6875em; margin: -1.84375em; background: currentColor; border-radius: 50%; box-shadow: -2.1875em 0.6875em 0 -0.6875em, 2.0625em 0.9375em 0 -0.9375em, 0 0 0 0.375em #fff, -2.1875em 0.6875em 0 -0.3125em #fff, 2.0625em 0.9375em 0 -0.5625em #fff; } .cloud:after { content: ''; position: absolute; bottom: 0; left: -0.5em; display: block; width: 4.5625em; height: 1em; background: currentColor; box-shadow: 0 0.4375em 0 -0.0625em #fff; } .cloud:nth-child(2) { z-index: 0; background: #fff; box-shadow: -2.1875em 0.6875em 0 -0.6875em #fff, 2.0625em 0.9375em 0 -0.9375em #fff, 0 0 0 0.375em #fff, -2.1875em 0.6875em 0 -0.3125em #fff, 2.0625em 0.9375em 0 -0.5625em #fff; opacity: 0.3; transform: scale(0.5) translate(6em, -3em); animation: cloud 4s linear infinite; } .cloud:nth-child(2):after { background: #fff; } .sun { position: absolute; top: 50%; left: 50%; width: 2.5em; height: 2.5em; margin: -1.25em; background: currentColor; border-radius: 50%; box-shadow: 0 0 0 0.375em #fff; animation: spin 12s infinite linear; } .rays { position: absolute; top: -2em; left: 50%; display: block; width: 0.375em; height: 1.125em; margin-left: -0.1875em; background: #fff; border-radius: 0.25em; box-shadow: 0 5.375em #fff; } .rays:before, .rays:after { content: ''; position: absolute; top: 0em; left: 0em; display: block; width: 0.375em; height: 1.125em; transform: rotate(60deg); transform-origin: 50% 3.25em; background: #fff; border-radius: 0.25em; box-shadow: 0 5.375em #fff; } .rays:before { transform: rotate(120deg); } .cloud + .sun { margin: -2em 1em; } .rain, .lightning, .snow { position: absolute; z-index: 2; top: 50%; left: 50%; width: 3.75em; height: 3.75em; margin: 0.375em 0 0 -2em; background: currentColor; } .rain:after { content: ''; position: absolute; z-index: 2; top: 50%; left: 50%; width: 1.125em; height: 1.125em; margin: -1em 0 0 -0.25em; background: #0cf; border-radius: 100% 0 60% 50% / 60% 0 100% 50%; box-shadow: 0.625em 0.875em 0 -0.125em rgba(255,255,255,0.2), -0.875em 1.125em 0 -0.125em rgba(255,255,255,0.2), -1.375em -0.125em 0 rgba(255,255,255,0.2); transform: rotate(-28deg); animation: rain 3s linear infinite; } .bolt { position: absolute; top: 50%; left: 50%; margin: -0.25em 0 0 -0.125em; color: #fff; opacity: 0.3; animation: lightning 2s linear infinite; } .bolt:nth-child(2) { width: 0.5em; height: 0.25em; margin: -1.75em 0 0 -1.875em; transform: translate(2.5em, 2.25em); opacity: 0.2; animation: lightning 1.5s linear infinite; } .bolt:before, .bolt:after { content: ''; position: absolute; z-index: 2; top: 50%; left: 50%; margin: -1.625em 0 0 -1.0125em; border-top: 1.25em solid transparent; border-right: 0.75em solid; border-bottom: 0.75em solid; border-left: 0.5em solid transparent; transform: skewX(-10deg); } .bolt:after { margin: -0.25em 0 0 -0.25em; border-top: 0.75em solid; border-right: 0.5em solid transparent; border-bottom: 1.25em solid transparent; border-left: 0.75em solid; transform: skewX(-10deg); } .bolt:nth-child(2):before { margin: -0.75em 0 0 -0.5em; border-top: 0.625em solid transparent; border-right: 0.375em solid; border-bottom: 0.375em solid; border-left: 0.25em solid transparent; } .bolt:nth-child(2):after { margin: -0.125em 0 0 -0.125em; border-top: 0.375em solid; border-right: 0.25em solid transparent; border-bottom: 0.625em solid transparent; border-left: 0.375em solid; } .flake:before, .flake:after { content: '\2744'; position: absolute; top: 50%; left: 50%; margin: -1.025em 0 0 -1.0125em; color: #fff; opacity: 0.2; animation: spin 8s linear infinite reverse; } .flake:after { margin: 0.125em 0 0 -1em; font-size: 1.5em; opacity: 0.4; animation: spin 14s linear infinite; } .flake:nth-child(2):before { margin: -0.5em 0 0 0.25em; font-size: 1.25em; opacity: 0.2; animation: spin 10s linear infinite; } .flake:nth-child(2):after { margin: 0.375em 0 0 0.125em; font-size: 2em; opacity: 0.4; animation: spin 16s linear infinite reverse; } /* Animations */ @keyframes spin { 100% { transform: rotate(360deg); } } @keyframes cloud { 0% { opacity: 0; } 50% { opacity: 0.3; } 100% { opacity: 0; transform: scale(0.5) translate(-200%, -3em); } } @keyframes rain { 0% { background: #0cf; box-shadow: 0.625em 0.875em 0 -0.125em rgba(255,255,255,0.2), -0.875em 1.125em 0 -0.125em rgba(255,255,255,0.2), -1.375em -0.125em 0 #0cf; } 25% { box-shadow: 0.625em 0.875em 0 -0.125em rgba(255,255,255,0.2), -0.875em 1.125em 0 -0.125em #0cf, -1.375em -0.125em 0 rgba(255,255,255,0.2); } 50% { background: rgba(255,255,255,0.3); box-shadow: 0.625em 0.875em 0 -0.125em #0cf, -0.875em 1.125em 0 -0.125em rgba(255,255,255,0.2), -1.375em -0.125em 0 rgba(255,255,255,0.2); } 100% { box-shadow: 0.625em 0.875em 0 -0.125em rgba(255,255,255,0.2), -0.875em 1.125em 0 -0.125em rgba(255,255,255,0.2), -1.375em -0.125em 0 #0cf; } } @keyframes lightning { 45% { color: #fff; background: #fff; opacity: 0.2; } 50% { color: #0cf; background: #0cf; opacity: 1; } 55% { color: #fff; background: #fff; opacity: 0.2; } }
参考文档
入门指南-微信小程序SDK | 高德地图API (amap.com)
天气查询-API文档-开发指南-Web服务 API | 高德地图API (amap.com)
校园小程序: 基于强智教务系统的校园服务类小程序--多校版本(默认 山科)使用云开发 (gitee.com)
到此这篇关于微信小程序基于高德地图API实现天气组件(动态效果)的文章就介绍到这了,更多相关微信小程序实现天气组件内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!