写点什么

基于 OpenHarmony 开发的玻璃拟态天气应用 (3) 构建天气组件并实现毛玻璃效果

作者:路北路陈
  • 2023-06-28
    北京
  • 本文字数:5753 字

    阅读完需:约 19 分钟

基于OpenHarmony开发的玻璃拟态天气应用(3)构建天气组件并实现毛玻璃效果

基于 OpenHarmony 开发的玻璃拟态天气应用(3)构建天气组件并实现毛玻璃效果

项目介绍

项目描述

本项目使用了 API9 作为开发版本,该版本是一个较新的版本,在这个项目中我会展示该项目的主要功能构成,主要是展示玻璃拟态的具体开发代码和开发思路,本项目偏向前端开发,后端使用的是和风天气的免费天气 API.

开源地址

整个项目也会同步开源到 Gitee 和 GItHub 仓库中


Gitee:GlassWerther (gitee.com)


GItHub:GlassWeather (github.com)

项目展示

项目设计思路

本项目的设计风格主要学习了 IOS 自带的天气软件,IOS 自带天气的风格就是这种玻璃拟态的风格,个人认为这种风格还是非常好看的,所以本项目大部分代码都是为了实现与其相近的效果

项目主要组件

当前天气组件 NowTemp

该组件显示的内容有以下几部分:当前城市,当前天气,天气描述,今日最高温和最低温

今日空气质量组件 AirQuality

该组件展示了今日的空气质量,主要以展示 AQI 的方式来展示空气质量

卡片组件

该组件包含了数个卡片,卡片中的信息是今日天气指数的一些相关信息,如钓鱼指数等,目的是直观的展示今日天气适合做什么.

搭建项目

构建空气质量组件

创建空气质量组件的文件

在目录E:\Works\DevoEcoProjects\GlassWeather\entry\src\main\ets\weatherComponent下创建新的 ArkTS File,命名为 AirQuality,如图:



按照之前写当前天气组件的方式来构建这个组件


先添加修饰符@Component并在下面创建对应的结构体组件,代码如下


@Componentexport struct AirQuality {  build() {    Column() {
} }}
复制代码


添加修饰符@Preview即可在预览窗口中查看,但不建议这样做,还是建议直接预览主页面而不是预览自定义组件

为主页添加 Scroll 容器

将该组件导入主页面中即可预览


因为在当前天气的下方是一个可以滑动的模块,所以我们需要添加一个 Scroll 容器来放之后的组件,关于这个容器的具体用法,可以参考官方的文档


首先,我们需要创建一个滚动控制器,在主界面添加一个变量


  scroller: Scroller = new Scroller(); // 滚动器
复制代码


然后再添加 Scroll 容器才能生效,并修改样式


主页面代码修改为:


import { AirQuality } from '../weatherComponent/AirQuality'import { CurrentWeather } from '../weatherComponent/CurrentWeather'@Entry@Componentstruct Index {  @State message: string = 'Hello World'
scroller: Scroller = new Scroller(); // 滚动器 build() { Column() { CurrentWeather() Scroll(this.scroller) { AirQuality() } .padding({ left: 20, right: 20, bottom: 300 }) .scrollable(ScrollDirection.Vertical) // 滚动方向纵向 .scrollBar(BarState.Off) // 滚动条常驻显示 .edgeEffect(EdgeEffect.Spring) // 滚动到边沿后回弹 Text('卡片组件') .fontSize(50) .fontWeight(FontWeight.Bold) } .width('100%') .justifyContent(FlexAlign.Start) }}
复制代码


添加了之后主页面并没有改变,因为我们现在还没有在自定义组件中写东西,接下来我们继续写自定义组件

添加颜色

在这之前,先添加一下之后会用到的颜色到color.json


修改后的代码如下


{  "color": [    {      "name": "start_window_background",      "value": "#FFFFFF"    },    {      "name": "baseFontColor",      "value": "#000000"    },    {      "name": "weatherCardTitle",      "value": "#60FFFFFF"    },    {      "name": "weatherCardBack",      "value": "#205392c8"    },    {      "name": "weatherCardShadow",      "value": "#30010b30"    }  ]}
复制代码

构建空气质量组件(重点)

按照图中所示添加对应的文字,并修改格式


代码修改后如下:


@Componentexport struct AirQuality {
@State aqi: string = 'aqi' @State category: string = 'category'
aboutToAppear() {
} build() { Column() { Text('空气质量').fontColor($r('app.color.weatherCardTitle')).fontSize(11) .margin({ bottom: 10 })
Column() { } .border({ width: { left: 0, right: 0, top: 0, bottom: 0.4 }, color: $r('app.color.weatherCardTitle'), }) .width('100%')
Text(this.aqi + ' - ' + this.category) .fontColor($r('app.color.baseFontColor')) .fontSize(20) .fontWeight(700) .margin({ top: 10, }) Text('当前AQI(CN)为' + this.aqi) .fontColor($r('app.color.baseFontColor')).fontSize(16).fontWeight(300) .margin({ top: 10, bottom: 5 }) } .border({ width: 0.4, color: $r('app.color.weatherCardTitle'), }) .backgroundColor($r('app.color.weatherCardBack')) .alignItems(HorizontalAlign.Start) .width('100%') .padding(15) .borderRadius(10) .margin({ top: 20 }) .shadow({ radius:20, color:$r('app.color.weatherCardShadow') })
.backdropBlur(70) .animation({ duration: 120, curve: Curve.EaseInOut, }) }}
复制代码


这个地方可以重点解析一下前端的代码,这一部分就是实现毛玻璃效果的关键

分隔线

首先,组件中间有一个分隔线,我是用的是以下代码来实现的


      Column() {      }      .border({ width: { left: 0, right: 0, top: 0, bottom: 0.4 }, color: $r('app.color.weatherCardTitle'), })      .width('100%')
复制代码


这部分代码就是一个 column 容器,但是里面不放任何东西,但是在样式上给一个底部的边框,这样的话就可以实现基本的分隔线效果了


分隔线主要是起到视觉上的平衡效果,所以对应的颜色不用太深,可以使用带有透明度的颜色,比如这里app.color.weatherCardTitle对应的的#60FFFFFF这样效果既不会太喧宾夺主又非常好看

毛玻璃效果
  .border({ width: 0.4, color: $r('app.color.weatherCardTitle'), })  .backgroundColor($r('app.color.weatherCardBack'))    .borderRadius(10)    .shadow({      radius:20,      color:$r('app.color.weatherCardShadow')    })    .backdropBlur(70)
复制代码


毛玻璃效果的关键就是以上的样式


最关键的是.backdropBlur(70)这一行,这行代码控制着背景的模糊量,数值越高,模糊的程度越大


背景颜色需要使用半透明的颜色,不然是没有效果的,比如这里的app.color.weatherCardBack就对应的是#205392c8这个颜色


shadow这个属性我认为也是必不可少的一部分,添加淡淡的阴影可以让整个组件显得更加突出


border这个属性为整个组件添加了一圈淡淡的描边,这里我使用的颜色是和分隔线一样的半透明的颜色,这样可以让描边更好地融入整个组件之中


目前还没有添加网络请求,但是大概的样子已经可以看出来了


效果:


添加背景

因为没有背景图片,所以毛玻璃的效果不是很好,我们照一张图片来充当背景图,比如下面这张图



将这张图放在E:\Works\DevoEcoProjects\GlassWeather\entry\src\main\resources\base\media目录下,


通过


    .backgroundImage($r('app.media.img_1'), ImageRepeat.NoRepeat)    .backgroundImageSize(ImageSize.Cover)
复制代码


就可以修改背景了


修改之后主界面的完整代码如下:


import { AirQuality } from '../weatherComponent/AirQuality'import { CurrentWeather } from '../weatherComponent/CurrentWeather'@Entry@Componentstruct Index {  @State message: string = 'Hello World'
scroller: Scroller = new Scroller(); // 滚动器 build() { Column() { CurrentWeather() Scroll(this.scroller) { AirQuality() } .padding({ left: 20, right: 20, bottom: 300 }) .scrollable(ScrollDirection.Vertical) // 滚动方向纵向 .scrollBar(BarState.Off) // 滚动条常驻显示 .edgeEffect(EdgeEffect.Spring) // 滚动到边沿后回弹
Text('卡片组件') .fontSize(50) .fontWeight(FontWeight.Bold) } .height('100%') .width('100%') .justifyContent(FlexAlign.Start) .backgroundImage($r('app.media.img_1'), ImageRepeat.NoRepeat) .backgroundImageSize(ImageSize.Cover) }}
复制代码


再修改一下字体的颜色


修改后的 color 文件如下


{  "color": [    {      "name": "start_window_background",      "value": "#FFFFFF"    },    {      "name": "baseFontColor",      "value": "#FFFFFF"    },    {      "name": "weatherCardTitle",      "value": "#60FFFFFF"    },    {      "name": "weatherCardBack",      "value": "#205392c8"    },    {      "name": "weatherCardShadow",      "value": "#30010b30"    }  ]}
复制代码


效果如下:


添加空气质量请求

构建对应的模型

E:\Works\DevoEcoProjects\GlassWeather\entry\src\main\ets\model\WeatherModel.ets中添加对应的模型,以便将获得的 json 文本转换为对象


export class AirQualityData{  aqi:string  category:string}
复制代码


完整代码如下:


export class CurrentWeatherModel {  obsTime?: string // 数据观测时间  temp?: string // 温度,默认单位:摄氏度  feelsLike?: string // 体感温度,默认单位:摄氏度  icon?: string // 天气状况和图标的代码,图标可通过天气状况和图标下载  text?: string // 天气状况的文字描述,包括阴晴雨雪等天气状态的描述  wind360?: string // 风向360角度  windDir?: string // 风向  windScale?: string // 风力等级  temperature?: string // 温度  direct?: string // 风向  windSpeed?: string // 风速,公里/小时  humidity?: string // 相对湿度,百分比数值  precip?: string // 当前小时累计降水量,默认单位:毫米  pressure?: string //大气压强,默认单位:百帕  vis?: string // 能见度,默认单位:公里  cloud?: string //   云量,百分比数值。可能为空  dew?: string // 露点温度。可能为空}
export class AirQualityData{ aqi:string category:string}
复制代码

构建 axios 请求

构建 axios 请求


  GetAirQuality() {    // 构建天气URL    let weatherUrl = 'https://devapi.qweather.com/v7/air/now?location=116,40&key=9243123252294a3e93ae6ccab5ec5da7'    // 发送GET请求获取天气数据    axios.get(weatherUrl)      .then(res => {        // 将返回的天气数据转换为字符串        let data = JSON.stringify(res.data.now);
// 将字符串转换为JSON对象并赋值给airWeather变量 this.airQualityData = JSON.parse(data.toString());
this.aqi = this.airQualityData.aqi this.category = this.airQualityData.category }); }
复制代码


将构建好的函数放入aboutToAppear()中,这样组件加载时就会调用这个函数来获取信息


完整的代码如下:


import axios from '@ohos/axios'import { AirQualityData } from '../model/WeatherModel'
@Componentexport struct AirQuality { @State aqi: string = 'aqi' @State category: string = 'category' @State airQualityData: AirQualityData = new AirQualityData()
aboutToAppear() { this.GetAirQuality() }
GetAirQuality() { // 构建天气URL let weatherUrl = 'https://devapi.qweather.com/v7/air/now?location=116,40&key=9243123252294a3e93ae6ccab5ec5da7' // 发送GET请求获取天气数据 axios.get(weatherUrl) .then(res => { // 将返回的天气数据转换为字符串 let data = JSON.stringify(res.data.now);
// 将字符串转换为JSON对象并赋值给airWeather变量 this.airQualityData = JSON.parse(data.toString());
this.aqi = this.airQualityData.aqi this.category = this.airQualityData.category }); }
build() { Column() { Text('空气质量').fontColor($r('app.color.weatherCardTitle')).fontSize(11) .margin({ bottom: 10 })
Column() { } .border({ width: { left: 0, right: 0, top: 0, bottom: 0.4 }, color: $r('app.color.weatherCardTitle'), }) .width('100%')
Text(this.aqi + ' - ' + this.category) .fontColor($r('app.color.baseFontColor')) .fontSize(20) .fontWeight(700) .margin({ top: 10, }) Text('当前AQI(CN)为' + this.aqi) .fontColor($r('app.color.baseFontColor')).fontSize(16).fontWeight(300) .margin({ top: 10, bottom: 5 }) } .border({ width: 0.4, color: $r('app.color.weatherCardTitle'), }) .backgroundColor($r('app.color.weatherCardBack')) .alignItems(HorizontalAlign.Start) .width('100%') .padding(15) .borderRadius(10) .margin({ top: 20 }) .shadow({ radius: 20, color: $r('app.color.weatherCardShadow') })
.backdropBlur(70) .animation({ duration: 120, curve: Curve.EaseInOut, }) }}
复制代码


这样就能获取实时的天气质量信息了

总结

在搭建项目的过程中,我们首先创建了空气质量组件的文件,并添加了修饰符和构建函数。然后,在主页中添加了一个 Scroll 容器来放置空气质量组件,并对 Scroll 容器进行了样式修改。接下来,我们为空气质量组件添加了对应的文字和样式,包括分隔线和毛玻璃效果。然后,我们添加了背景图片,并修改了字体颜色。最后,我们构建了空气质量请求,通过 axios 发送 GET 请求获取天气数据,并将返回的数据转换为字符串并赋值给组件的状态变量。通过这样的操作,我们成功地搭建了一个可以获取实时空气质量信息的组件

发布于: 刚刚阅读数: 2
用户头像

路北路陈

关注

还未添加个人签名 2023-06-10 加入

还未添加个人简介

评论

发布
暂无评论
基于OpenHarmony开发的玻璃拟态天气应用(3)构建天气组件并实现毛玻璃效果_前端_路北路陈_InfoQ写作社区