HarmonyOS Next实战教程:实现中间凹陷的异形tabbar
今天要和大家分享的实战案例是实现中间凹陷的tabar

前些天在做墨迹天气的时候看到了这种异形的tabbar,看起来比较有挑战性,因为鸿蒙版的墨迹天气app还没有这个东西,我决定尝试做一下。
系统的Tabs肯定是不行了,我们需要自定义。
难度直接拉满,直接做最难的部分,就是这个中间有凹陷的矩形,这怎么整呢?

我们一点一点来,先不管矩形,先尝试画一条上边的曲线,这里需要使用三次贝塞尔曲线路径方法bezierCurveTo。
要画贝塞尔曲线,就要知道线上的坐标,尤其是曲线部分,坐标越多越好。
那么问题来了,直线部分的坐标很容易知道,曲线部分的坐标是多少呢?
我们需要做一道几何题,看一下这个曲线,它像是一个半圆又不像是一个半圆。我把它分解了一下,可以看到它是由三个同样大小的圆的三个部分拼凑起来,这三个圆的排列也比较有规律。

我们根据笛卡尔坐标系,计算出圆上的坐标点,越多越好。
我把这段曲线分为相等的4段,每一段都获取24个坐标点:
//精确度
let accuracy = 24
let finalP:Point[] = []
for (let index = 0; index <= j1_x; index+=(j1_x/accuracy)) {
let pp = this.calculateYFromX(circle1.x,circle1.y,circleR,start_left_x + index,true)
finalP.push(pp)
}
for (let index = j1_x; index <= middleSize/2 + j1_x; index+=(j1_x/(accuracy*2))) {
let pp = this.calculateYFromX(circle2.x,circle2.y,circleR,start_left_x + index,false)
finalP.push(pp)
}
for (let index = middleSize/2 + j1_x; index <= middleSize; index+=(j1_x/accuracy)) {
let pp = this.calculateYFromX(circle3.x,circle3.y,circleR,start_left_x + index,true)
finalP.push(pp)
}
这样曲线部分的坐标就有了。
现在我们开始画线,先画左边的直线:
@State padding_top:number = 6
this.context.lineTo(start_left_x,this.padding_top)
这里说一下y坐标为什么不是0,因为这个tabbar上方还有一些阴影,要把阴影这一部分留出来。
再画曲线部分:
for(let i = 1;i < finalP.length - 2;i+=3){
this.context.bezierCurveTo(
finalP[i].x,finalP[i].y,
finalP[i+1].x,finalP[i+1].y,
finalP[i+2].x,finalP[i+2].y,
);
}
再画右边的直线:
this.context.lineTo(this.screen_width,this.padding_top)
同样的把其他三个边也都画线形成闭环。
接下来我们就可以进行填充:
this.context.fillStyle = '#ffffff'
this.context.stroke();
this.context.fill()
刚才说了tabbar有阴影,很多同学不知道贝塞尔曲线可以设置阴影,下面给大家示范一下:
this.context.shadowOffsetY = 0
this.context.shadowColor = '#949494'
this.context.shadowBlur = 12
this.context.stroke();
这样就实现了沿着曲线的阴影,非常完美。
现在我们要在画布上添加切换页面的按钮,很明显要使用层叠布局,而且中间的按钮要给它特殊处理一下
@State tabList:TabItem[] = [
{image:$r('app.media.tb00'),selectImage:$r('app.media.tb01'),title:'首页'},
{image:$r('app.media.tb10'),selectImage:$r('app.media.tb11'),title:'发现'}
]
ForEach(this.tabItems,(item:TabItem,index)=>{
if(index == this.tabItems.length/2){
Image($r('app.media.middle'))
.width(60)
.height(60)
.borderRadius(30)
.offset({y:-30})
.borderWidth(4)
.borderColor(Color.White)
.borderStyle(BorderStyle.Solid)
.shadow({
radius: 20,
color: Color.Gray,
offsetX: 0,
offsetY: 0
})
.onClick(()=>{
this.currentIndex = index
this.tabClick(-1)
})
}
Column({space:4}){
Image(this.currentIndex == index? item.selectImage:item.image)
.width(22)
.height(22)
.objectFit(ImageFit.Contain)
Text(item.title)
.fontSize(13)
.fontColor( this.currentIndex == index? this.selectedFontColor:this.fontColor)
}
.alignItems(HorizontalAlign.Center)
.onClick(()=>{
this.currentIndex = index
this.tabClick(index)
})
// .backgroundColor(Color.Black)
})
现在一个完整的tabbar样式就完成了,接下来要用它替换系统的tabbar并且实现页面的切换,虽然说是替换掉系统tabbar,但是还是要使用它。你会发现在系统的tabbar中不设置tabBar属性的话页面底部就会是一片空白:

正好可以将我们的tabbar放在那,你还可以使用barHeight属性调整高度:
Stack({alignContent:Alignment.Bottom}){
Tabs({ barPosition: BarPosition.End, controller: this.tabsController }) {
TabContent() {
Page1()
}
TabContent() {
Page2()
}
TabContent() {
Page3()
}
TabContent() {
Page3()
}
}
.backgroundColor(Color.White)
.barHeight(64)
.onChange((index) => {
this.currentIndex = index
})
.animationDuration(1)
.scrollable(false)
BottomBar({tabItems:this.tabList,currentIndex:this.currentIndex,tabClick:(index)=>{
if(index == -1){
router.pushUrl({
url:'pages/Page3'
})
}else {
this.tabsController.changeIndex(index)
}
}})
}
.height('100%')
.width('100%')
这样我们就成功实现了异形的自定义tabbar,非常完美。
HarmonyOS Next实战教程:实现中间凹陷的异形tabbar的更多相关文章
- 【ASP.NET实战教程】ASP.NET实战教程大集合,各种项目实战集合
[ASP.NET实战教程]ASP.NET实战教程大集合,各种项目实战集合,希望大家可以好好学习教程中,有的比较老了,但是一直很经典!!!!论坛中很多小伙伴说.net没有实战教程学习,所以小编连夜搜集整 ...
- 【转】mybatis实战教程(mybatis in action),mybatis入门到精通
MyBatis 目录(?)[-] mybatis实战教程mybatis in action之一开发环境搭建 mybatis实战教程mybatis in action之二以接口的方式编程 mybatis ...
- NDK-JNI实战教程(二) JNI官方中文资料
声明 设计概述 JNI接口函数和指针 加载和链接本地方法 解析本地方法名 本地方法的参数 引用Java对象 全局和局部引用 实现局部引用 访问Java对象 访问基本类型数组 访问域和方法 报告编程错误 ...
- mybatis实战教程(mybatis in action),mybatis入门到精通
转自:http://www.yihaomen.com/article/java/302.htm (读者注:其实这个应该叫做很基础的入门一下下,如果你看过hibernate了那这个就非常的简单) (再加 ...
- ActiveReports 9实战教程(3): 图文并茂的报表形式
基于上面2节内容,我们搭建了AR9的开发环境,配置好了数据源.在本节,我们以官方提供的3个中文图文并茂的报表来展示AR9的功能,并通过实战的方式一一分享. 以往做报表相关的工作时,最害怕的是报表的UI ...
- 《软件性能测试与LoadRunner实战教程》新书上市
作者前三本书<软件性能测试与LoadRunner实战>.<精通软件性能测试与LoadRunner实战>和<精通软件性能测试与LoadRunner最佳实战>面市后,受 ...
- BI之SSAS完整实战教程7 -- 设计维度、细化维度中 :浏览维度,细化维度
上篇文章我们已经将Dim Geography维度设计好. 若要查看维度的成员, AS需要接收该维度的详细信息(包括已创建的特性.成员属性以及多级层次结构), 通过XMLA与AS的实例进行通信. 今天我 ...
- BI之SSAS完整实战教程6 -- 设计维度、细化维度上:创建维度定义特性关系
前面我们使用过数据源向导.数据源视图向导.Cube向导来创建相应的对象. 本篇我们将学习使用维度向导来创建维度. 通过前面几个向导的学习,我们归纳一下共同点,主要分成两步 1. 使用某种对象类型的向导 ...
- BI之SSAS完整实战教程5 -- 详解多维数据集结构
之前简单介绍过多维数据集(Cube)的结构. 原来计划将Cube结构这部分内容打散,在实验中穿插讲解, 考虑到结构之间不同的部分都有联系,如果打散了将反而不好理解,还是直接一次性全部讲完. 本篇我们将 ...
- BI之SSAS完整实战教程4 -- 部署至SSAS进行简单分析
上一篇已经创建了多维数据集的结构. 接下来我们将多维数据集的架构定义发送到Analysis Services实例,部署到AS上去. 文章提纲 部署和浏览多维数据集 SSMS使用简介 总结 一.部署和浏 ...
随机推荐
- flutter-原生路由传自定义对象
自定义对象 1 class Dk { 2 int id; 3 String title; 4 String address; 5 String ww; 6 String account; 7 } 传值 ...
- [AHOI2013] 差异 题解
后缀自动机维护子串公共后缀方便一点,所以直接倒序插入字符串即可. 我们给所有前缀打上标记,然后跑树形 \(dp\),设 \(sum_i\) 表示第 \(i\) 个点的子树内有多少个前缀,\(ans\) ...
- 探秘Transformer系列之(5)--- 训练&推理
探秘Transformer系列之(5)--- 训练&推理 0x00 概述 Transformer训练的目的是通过对输入源序列和模型输出序列的学习,来拟合真正的目标序列.推理的目的则是仅通过输入 ...
- SpringBoot实现HandlerInterceptor拦截器的接口没有需要重写的方法也不报错是怎么回事
以前实现HandlerInterceptor接口,总会提示需要实现3个方法(preHandle.postHandle.afterCompletion).现在没有出现提示.原因:这是Java8的新特性- ...
- bin格式转safetensors
技术背景 本文主要介绍在Hugging Face上把bin格式的模型文件转为safetensors格式的模型文件,并下载到本地的方法. bin转safetensors 首先安装safetensors: ...
- ES - 概述
前言 Q1:ElasticSearch 是什么? 为什么要学习? ElasticSearch 是一个分布式.可扩展.实时的搜索和分析引擎,基于 Lucene 构建.它可以用于全文搜索.结构化搜索.分析 ...
- 记一次Microsoft.Toolkit.Mvvm(MVVM Toolkit)的兼容性问题
今天在目标框架为framework4.6.1的wpf项目中使用Microsoft.Toolkit.Mvvm7.1.1出现了一个比较怪异的编译时错误,前提是打开了 工具>选项>环境>预 ...
- 什么是nginx的强缓存和协商缓存
一.强缓存(Strong Cache) 1. 定义 • 强缓存直接告诉浏览器:在缓存过期前,无需与服务器通信,直接使用本地缓存. • 由服务器通过响应头 Cache-Control 和 Expires ...
- Supac 如何修改地址界限高层点
编辑->图层->运算 2.选择z ->填写高度 如-180阶段 3.保存
- rust学习笔记(2)
类型 有符号整数(signed integers):i8.i16.i32.i64.i128 和 isize(指针宽度) 无符号整数(unsigned integers): u8.u16.u32.u64 ...