引言
最近在学习鸿蒙开发,发现系统的Tabs不支持左右切换时标签文字的字体大小和颜色渐变,于是通过查阅相关资料及文章自己自定义了一个。先看下效果图:
实现
自定义指示器滑动和字体大小渐变是参考【OpenHarmony动效开发】实现Tabs翻页效果(二)这篇文章实现的,这里主要说下颜色渐变的实现。
在鸿蒙中颜色是一个混合类型ResourceColor,支持Color、number、string、Resource,要实现两个颜色的渐变我们需要将传入的ResourceColor转成对应的rgb颜色,然后通过滑动的进度生成新的rgb颜色,再设置给文字标签。
首先我们定义rgb颜色的接口RgbaColor
interface RgbaColor {
red: number,
green: number,
blue: number,
alpha: number
}
接下来我们把传入的ResourceColor先转成string类型,经测试系统的Color也是string类型
// 根据resourceColor获取hex颜色值
static getHex(color: ResourceColor): string {
let colorStr = '';
if (typeof color === 'number') { // number类型
colorStr = `#${(color as number).toString(16).padStart(6, '0')}`;
} else if (typeof color === 'string') { // string类型或Color类型
if ((color as string).startsWith('#')) {
colorStr = color
} else {
colorStr = Utils.rgbaToHex(color)
}
} else { // Resource类型
let newColor = color as Resource;
colorStr = `#${getContext(Utils).resourceManager.getColorSync(newColor).toString(16).padStart(6, '')}`;
}
return colorStr
}
在鸿蒙中string类型的颜色支持两种格式#开头和rgb开头的,比如黑色可以用#000、#000000、#ff000000表示,或者用rgb(0, 0, 0)、rgba(0, 0, 0, 1)表示,我们在判断string类型时如果是rgb表示的要先转成hex
// rgba转hex
static rgbaToHex(rgbaStr: string): string {
// 匹配逗号分割的数字,join('.')是为了处理小数
let rgbaArr = rgbaStr.split(',').map(item => item.match(/\d+/g)!.join('.'));
// 最终的hex字符串
let hexStr = '#';
// 判断是否是正确的格式
if (rgbaArr.length === 3 || rgbaArr.length === 4) {
hexStr += rgbaArr.map((item, index) => {
// 如果r、g、b任何一个是0,转成16进制后把0重复两遍
let str: string = index < 3 ? Math.floor(Number(item)).toString(16) : Math.floor(Number(item) * 255).toString(16);
return str.length === 1 ? str.repeat(2) : str;
})
.join('')
return hexStr
} else {
// 格式不正确
return ''
}
}
接下来我们根据hex颜色获取对应的r、g、b、a值
// hex转rgbaColor
static hexToRgbaColor(hex: string): RgbaColor {
// 初始化color
let rgbaColor: RgbaColor = { red: 0, green: 0, blue: 0, alpha: 1 }
// 去除#号
let excludePrefix = hex.replace('#', '');
// hex值的长度
let hexLength = excludePrefix.length;
// hex的格式 #bfa, #bbffaa,#ffbbffaa(经测试鸿蒙的8位alpha在前面,即ff)
if (hex.startsWith('#') && (hexLength === 3 || hexLength === 6 || hexLength === 8)) {
let hexArr: string[] = [];
// 针对不同格式分别处理
switch (hexLength) {
case 3: // 3位
excludePrefix.split('').forEach((item, index) => {
let number = parseInt(item.repeat(2), 16);
if (index === 0) {
rgbaColor.red = number;
} else if (index === 1) {
rgbaColor.green = number;
} else if (index === 2) {
rgbaColor.blue = number;
}
})
rgbaColor.alpha = 1;
break;
case 6: // 6位
for (let i = 0; i < 3; i++) {
hexArr.push(excludePrefix.slice(i * 2, i * 2 + 2));
}
hexArr.forEach((item, index) => {
let number = parseInt(item, 16);
if (index === 0) {
rgbaColor.red = number;
} else if (index === 1) {
rgbaColor.green = number;
} else if (index === 2) {
rgbaColor.blue = number;
}
})
rgbaColor.alpha = 1;
break;
case 8: // 8位
for (let i = 0; i < 4; i++) {
hexArr.push(excludePrefix.slice(i * 2, i * 2 + 2));
}
hexArr.forEach((item, index) => {
let number = parseInt(item, 16);
if (index === 0) {
// alpha取值[0,1]所以做下处理
rgbaColor.alpha = Number((number / 255).toFixed(2))
} else if (index === 1) {
rgbaColor.red = number
} else if (index === 2) {
rgbaColor.green = number
} else if (index === 3) {
rgbaColor.blue = number
}
})
break;
}
}
return rgbaColor
}
然后把传入的ResourceColor转成rgbaColor
// 根据resourceColor获取rgbaColor
static getRgbaColor(color: ResourceColor) {
let hex = Utils.getHex(color);
return Utils.hexToRgbaColor(hex);
}
接着我们通过传入的两个颜色和进度生成新的颜色
// 生成颜色
static interpolationColor(from: ResourceColor, to: ResourceColor, percent: number): string {
let fromColor = Utils.getRgbaColor(from);
let toColor = Utils.getRgbaColor(to);
let red = Math.round(Utils.interpolation(fromColor.red, toColor.red, percent));
let green = Math.round(Utils.interpolation(fromColor.green, toColor.green, percent));
let blue = Math.round(Utils.interpolation(fromColor.blue, toColor.blue, percent));
let alpha = Utils.interpolation(fromColor.alpha, toColor.alpha, percent).toFixed(2);
return `rgba(${red}, ${green}, ${blue}, ${alpha})`
}
// 计算插值
static interpolation(from: number, to: number, percent: number): number {
percent = Math.max(0, Math.min(1, percent));
return from + (to - from) * percent;
}
然后定义方法,生成颜色并设置到对应的Text
// 根据状态是否选中,确定标签状态颜色
private getTextFontColor(index: number): ResourceColor {
let normalColor = this.config.normalColor;
let selectColor = this.config.selectColor;
if (this.swiperState === SwiperState.DRAG) {
if (this.swiperIndex === index) {
return Utils.interpolationColor(normalColor, selectColor, 1 - this.swiperRatio);
}
if (this.nextIndex === index) {
return Utils.interpolationColor(normalColor, selectColor, this.swiperRatio);
}
}
return this.currentIndex === index ? selectColor : normalColor;
}
Text(item.title)
.fontSize(this.getTextFontSize(index))
.fontColor(this.getTextFontColor(index))
.fontWeight(this.getTextFontWeight(index))
至此,自定义标签栏颜色渐变就完成了。
总结
本篇主要是通过学习【OpenHarmony动效开发】实现Tabs翻页效果(二)的自定义标签栏,在它的基础上实现了标签栏文字颜色渐变过渡效果。