HarmonyOS NEXT实战:高仿墨迹天气开发手记(附源码)

news/2025/3/16 7:15:42/文章来源:https://www.cnblogs.com/youlanjihua/p/18774508

老余说3月份的神秘产品是为纯血鸿蒙而生的一款全新形态的手机,别人想象不到的手机产品,这次的保密工作真是非常到位,让人十分期待。

闲言少叙,今天为大家分享新年的第一个实战项目,高仿墨迹天气

这个项目中有一些复杂的动效和曲线,对于新手友友来说可能会有一点难,不过没关系,下面幽蓝君把项目中的难点一一详解:

▍ 下拉动画

下拉动画主要在于页面的上半部分,看似简单其实暗藏玄机,导航栏和整个页面融为一体,下拉刷新时,图片前景部分跟随页面向下移动,而图片的背景部分则像是一种拉伸的效果。向上滑动时,整个图片又保持不动,和页面脱离开来。

 

动图封面
 

 

所以导航栏部分直接自定义一个透明的,这里相对简单。

到了图片部分,我的实现方案是将背景颜色和前景图片分开来添加,先添加一个渐变色的背景:

 

 

它的代码是这样的:

Row(){
}
.width('100%')
.height(450 + this.refreshOffset)
.linearGradient({  direction: GradientDirection.Bottom, // 渐变方向  repeating: false, // 渐变颜色是否重复  colors: [[0x9ab7dd, 0] ,[0xb2c5cd, 0.4] ,[0xcbd2bc,1]] // 数组末尾元素占比小于1时满足重复着色效果})

 

在下拉刷新时背景有一个跟随拉伸的效果,所以他的高度里设置了一个动态变量refreshOffset,在下拉刷新组件Refresh的回调方法onOffsetChange中获取偏移量赋值就行了。

注意,上面这个背景颜色部分只是一个拉伸效果,它并不跟随下拉刷新向下移动,所以这个页面中它是唯一不在Refresh组件中的组件。

我想说的是,现在开始我们要添加一个Refresh组件,将其他元素都写在其中,包括刚刚说到的前景图片。

Refresh({ refreshing: $$this.isRefreshing }){       Row(){      Image($r('app.media.weather_back'))        .width('100%')        .height(210)        .objectFit(ImageFit.Fill)    }    .alignItems(VerticalAlign.Bottom)    .width('100%')    .height(450)。。。
}

 

这样的话基本实现了下拉刷新时背景拉伸,前景向下移动的效果。

接下来,在向上滑动页面时这个图片整体又会固定在页面上方,不跟随内容移动。内容部分很显然是一个List组件,所以图片部分不在List组件中,并且和List是层叠的布局。

所以在Refresh组件中添加Stack组件,将前景图片和List放在其中,这个复杂的动画基本就完成了。给大家放个代码结构图:

 

 

▍ 24小时温度曲线

我相信有相当一部分友友想到要用原生实现这样的曲线和动效的瞬间是崩溃的,因为我们习惯了使用Echarts实现各种图表。

 

动图封面
 

 

 

没关系,万丈高楼平地起,我们一点一点来。

首先添加一个画布组件:

Canvas(this.context)  .width('100%')  .height(100)  .backgroundColor(Color.Green)  .onReady(() => {  })

 

 

接下来我们要在画布的onReady方法下作画了。先随便画一个坐标点,以坐标(10,10)为例:

this.context.lineWidth = 4
this.context.arc(10, 10, 2, 0, 360)
this.context.stroke()

 

 

接下来上点难度,画一条贝塞尔曲线,我们这里使用三次贝塞尔曲线路径,也就是通过三个坐标点:

this.context.moveTo(10, 10)
this.context.bezierCurveTo(20, 100, 200, 100, 200, 20)
this.context.stroke()

 

 

现在是不是有一点思路了?我们如果把24个小时温度转换成坐标点连接起来是不是就能实现这条曲线?

现在要想办法把24个小时的温度均匀的分布在画布上,x轴直接24等分,y轴要计算出最大温差,然后再等分就可以了。转换坐标点的具体代码如下:

let points:PointClass[] = []
for(let i = 0;i < temps.length ;i++){let y_pointValue = temps[i]let y_point = 100 - (y_pointValue - lowestTemp) *tempUnitlet x_point = i*xUnit + xUnit/2let point:PointClass = {x:x_point,y:y_point}points.push(point)
}

 

不出意外我们将得到24个均匀分布的坐标,把它们连接起来是不是就能实现项目中的曲线?还不行,为了得到一条平滑的曲线,我们需要更多更多的坐标,所以把它们进行一些处理:

const cp: Point[] = [];
const bezierPoints: Point[] = [];
bezierPoints.push({x:points[0].x,y:points[0].y});
for (let i = 1; (i + 2) < points.length; i++) {const bezierItem = points[i];cp[0] = {x:bezierItem.x, y:bezierItem.y};cp[1] = {x:bezierItem.x + (points[i + 1].x - points[i - 1].x) / 6, y:bezierItem.y + ( points[i + 1].y -  points[i - 1].y) / 6};cp[2] = {x:points[i + 1].x + (points[i].x -  points[i + 2].x) / 6, y:points[i + 1].y + ( points[i].y -  points[i + 2].y) / 6};cp[3] = {x:points[i + 1].x, y:points[i + 1].y};bezierPoints.push(cp[1], cp[2], cp[3]);
}

 

使用三次贝塞尔曲线将这些坐标连接:

//填充颜色
this.timerContext.fillStyle = BasicTool.LINE_COLOR
//线条颜色
this.timerContext.strokeStyle = BasicTool.LINE_COLOR
//线条宽度
this.timerContext.lineWidth = 2
this.timerContext.moveTo(time_total[0].x,time_total[0].y);
for(let i = 1;i < time_total.length - 2;i+=3){this.timerContext.bezierCurveTo(time_total[i].x,time_total[i].y,time_total[i+1].x,time_total[i+1].y,time_total[i+2].x,time_total[i+2].y,);
}
this.timerContext.stroke();

 

一条平滑的24小时温度曲线就完成了。

 

 

接下来,曲线上还有一个浮标,跟随曲线滑动。这个不难,我们只要让浮标圆点的位置在曲线坐标上就行了嘛,坐标我们已经有了,需要做的事情就是在滑动页面的时候移动浮标就可以了。

scroll组件滑动的时候会返回一个偏移量,我们根据偏移量计算相应的贝塞尔曲线坐标,然后修改浮标位置:

let time_unit2 = BasicTool.SCREEN_WIDTH*2/this.timeLinePoints.length
let currentPosition2 = this.timeScroller.currentOffset().xOffset/time_unit2
this.currentScrollIndex =  Math.ceil(currentPosition2*(BasicTool.SCREEN_WIDTH*2/425))
if(this.currentScrollIndex >= this.timeLinePoints.length){this.currentScrollIndex = this.timeLinePoints.length - 1
}

 

现在这个复杂的曲线动效就基本实现了。

▍ 15天预报曲线

 

 

15天天气预报部分比较难的也是曲线部分,但是曲线我们已经会画了,不一样的就是这里多了圆点,我们在曲线上再添加一些点就行了,简单:

for (let i = 0;i < hightPoints.length; i++) {if(!(i ==0 || i == hightPoints.length -1)){this.context.beginPath() // 开始绘制路径this.context.lineWidth = 4 // 线条宽度this.context.strokeStyle = BasicTool.LINE_COLOR // 描边颜色this.context.fillStyle = BasicTool.LINE_COLOR // 描边颜色this.context.font = `12px sans-serif`this.context.arc(hightPoints[i].x, hightPoints[i].y, 2, 0, 360)this.context.stroke() }
}

 

以上就是本项目的难点解析,欢迎您关注本站,获取更多鸿蒙实战技术分享。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/899625.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

VSCode + CMake + MinGW 在 Windows 下的简易调试指南

VSCode + CMake + MinGW 在 Windows 下的简易调试指南 目录VSCode + CMake + MinGW 在 Windows 下的简易调试指南准备工作下载VSCode下载CMake下载MinGW待编译源码VSCode调试task.json 配置launch.json 配置开始调试鉴于网络上关于VSCode的调试的教程不多,并且掺杂着大量的随机…

指令集并行与开发进阶算法

进阶算法 基础算法无法解决中断恢复的问题,即假如有两个写寄存器的操作,指令1,指令2,可能乱序执行时指令2的结果已经将写回了寄存器,但是指令1还未执行,此时发生中断后,从指令1重新开始执行,就会重新进行两次写入,将会发生错误。 只要保证后面指令修改机器状态时, 前面…

指令集并行与开发Tomasulo算法

指令集并行与开发Tomasulo算法 1. 概念 Tomasulo 方法是一种用于在超标量处理器中执行指令并处理数据相关(数据相关性)的方法。它主要通过对指令进行乱序执行和动态调度来提高指令级并行性。 可以通过寄存器重命名消除 WAR 和 WAW 相关(通过保留站号间接实现重命名) 也可以…

芯片存储器层次结构概述

存储器层次结构概述 1. Cache的作用 Cache结构与作用,如图2-5所示。图2-5 Cache结构与作用 介绍一下Cache具有特征。Cache没有程序上的意义,只是为了降低访存延迟;处理器访问Cache和访问存储器使用相同的地址。 Tag存储cache块在主存中的首地址(cache每个字节都给一个地址太…

推荐1《AI芯片开发核心技术详解》、2《智能汽车传感器:原理设计应用》、3《TVM编译器原理与实践》、4《LLVM编译器原理与实践》书,谢谢

4本书推荐《AI芯片开发核心技术详解》、《智能汽车传感器:原理设计应用》、《TVM编译器原理与实践》、《LLVM编译器原理与实践》由清华大学出版社资深编辑赵佳霓老师策划编辑的新书《AI芯片开发核心技术详解》已经出版,京东、淘宝天猫、当当等网上,相应陆陆续续可以购买。该…

MYSQL-DDL操作

点击查看代码 ```plaintext create table tb(id int comment ID,username varchar(20) comment 用户名,name varchar(10) comment 姓名,age int comment 年龄,gender char(1) comment 性别 )comment user测试表</details> ![](https://img2024.cnblogs.com/blog/3619156…

JetBrains IDEA破解后一直跳出激活弹窗

正文 一直跳弹窗是因为选了区域中国,你可以断网,然后到打开设置,搜索区域,选择亚洲。保险起见,保存后先关闭idea,再连接网络,启动IDEA。

三分钟教学:手把手教你实现Arduino发布第三方库

Arduino 发布第三方库的流程包括:构建库的基本框架后将其打包并上传至 GitHub,在 GitHub 上创建 Tag 和 Release 后,提交到 Arduino 库管理器,最后在Arduino IDE进行验证。三分钟教学:手把手教你实现Arduino发布第三方库原文链接: 手把手教你实现Arduino发布第三方库 摘要…

2025-315晚会总结

🔖简介 2025年315晚会曝光了多个行业的消费乱象和违法侵权行为。 主题:“共铸诚信 提振消费”,聚焦食品安全、公共安全、金融安全、数字经济等领域。 核心诉求:打击消费陷阱,推动构建公平、诚信的消费环境。 📢曝光现象 🔒数据安全与隐私侵权非法窃取个人信息涉事企业…

再破难关(BFS)

问题 F: 再破难关 题目描述 OIBH组织派出的黄金十二人+青铜五小强还没有到, 他们只能指望原先的机关能够阻拦住柯南的脚步。柯南打开大门之后发现里面还有一个门, 门上还有一个神奇的锁(-,-) 这是一个4*4的锁, 上面有8个凸起的格子和8个被按下的格子,当且仅当两个格子有公共边…

LLM大模型:OpenManus原理

继deepseek之后,武汉一个开发monica的团队又开发了manus,号称是全球第一个通用的agent!各路自媒体企图复刻下一个deepseek,疯狂报道!然而manus发布后不久,metaGPT团队5个工程师号称耗时3小时就搞定了一个demo版本的manus,取名openManus,才几天时间就收获了34.4K的start…

Day14_TCP三次握手

每日一题 TCP三次握手详解 三次握手(Three-Way Handshake) 是TCP协议建立可靠连接的核心过程,确保通信双方能够正常收发数据并同步初始序列号。以下是详细步骤和原理:1. 第一次握手:SYN(客户端 → 服务器)动作:客户端发送一个TCP报文,设置SYN=1(同步标志位),并生成…