
跟着IBest-UI学HarmonyOS NEXT组件封装:Watermark 水印 原创
一、引言:一起来啃源码,解锁HarmonyOS NEXT的“组件密码”!
嘿,小伙伴们!今天想和大家聊一个超实用的开源项目——IBest-UI,一个专为鸿蒙生态打造的轻量级UI组件库。如果你正在开发HarmonyOS NEXT应用,一定遇到过这些痛点:重复造轮子、适配多端界面费时费力、深浅模式切换麻烦……别急,IBest-UI就是来“救场”的!
它有多香?
- 轻量到飞起:核心代码精简,引入即用,绝不给你添负担。
- 主题随心换:深色模式?浅色模式?一行代码切换,适配鸿蒙元服务毫无压力。
- 功能小而美:从按钮到弹窗,从徽章到导航栏,覆盖高频场景,样式参考vant,使用过vant的,都知道vant样式有多好看!
但今天咱们不光是“用组件”,而是要打开引擎盖,看看里面的“黑科技”!我们发起一个源码共读计划,目标很简单:
- 拆解设计思想:比如TextEllipsis 文本省略,它是怎么实现文本省略的?
- 偷师IBest-UI:在源码中捕捉ArkTS的高阶用法,学习如何在HarmonyOS NEXT中用声明式UI开发“丝滑”应用。
- 边学边玩:欢迎随时抛出问题、提交PR,咱们一起让IBest-UI变得更强大!
无论你是想提升源码阅读能力,还是想摸透鸿蒙开发的门道,这个系列都会是你的“实战指南”。准备好和我一起挖宝了吗?Let’s go! 🚀
二、准备工作
看一个开源项目,第一步应该是先看 README.md 再看贡献文档 github/CONTRIBUTING.md。
- 克隆源码
- 查看目录结构
根据贡献文档,可以了解到目录结构
根据目录,可以了解到,开发的组件、修复bug,主要是在 library
里进行开发, entry/main/ets/pages
里做组件例子页面。
全局样式变量在 library/src/theme-chalk/...
里定义
三、demo 演示
可以通过快捷方式 Ctrl+Shift+N
转到文件,输入自己所想找到的文件
根据前面的目录结构,我们已经知道了entry是样例文件,那么我们要找的文件,就是在 entry\src\main\ets\pages\base\Watermark.ets
里
运行下demo,看下demo使用效果
四、源码解析
快速找到源代码位置
可以通过快捷方式 Ctrl+Shift+N
转到文件,输入自己所想找到的文件
根据前面的目录结构,我们已经知道了entry是样例文件,library是组件库文件,那么我们要找的文件,就是在 library\src\main\ets\components\watermark
里
进到文件里,我们可以看到有三个文件
color.est文件定义了相关样式,可以看到,在 library\src\main\resources\base\element\color.json
读取样式,好处理全局样式
index.est代码解释
接下来就看下主文件index.est(已添加相关注释)
以下是对IBestWatermark
组件的详细解释,分为几个主要部分进行说明:
1. 导入模块
-
功能:导入了必要的类型和工具函数。
-
IBestStringNumber
:定义了一种支持字符串或数字的类型。getDefaultBaseStyle
和IBEST_UI_NAMESPACE
:用于加载全局主题样式。IBestWatermarkColor
:定义了水印组件的颜色相关配置。WatermarkCanvas
:负责生成水印图片的核心组件。
2. 属性定义
以下是IBestWatermark
组件中定义的属性及其作用:
属性名 | 类型 | 默认值 | 描述 |
---|---|---|---|
baseStyle |
IBestUIBaseStyleObjType |
全局公共样式,从存储中加载默认主题样式。 | |
defaultSlot |
CustomBuilder |
默认插槽内容,用于放置用户自定义的内容。 | |
waterMarkWidth |
number |
图片水印的宽度。 | |
waterMarkHeight |
number |
图片水印的高度。 | |
waterMarkOpacity |
number |
0.3 |
水印透明度,默认值为0.3 。 |
gapX |
number |
水印之间的水平间隔,默认值为0 。 |
|
gapY |
number |
水印之间的垂直间隔,默认值为0 。 |
|
rotateDeg |
number |
水印旋转角度。 | |
imageUrl |
ResourceStr |
图片水印的链接。 | |
text |
ResourceStr |
文字水印内容。 | |
fontSize |
number |
文字水印的字体大小。 | |
fontFamily |
string |
文字水印的字体类型。 | |
fontColor |
`IBestStringNumber | CanvasGradient | CanvasPattern` |
waterMarkZIndex |
number |
-1 |
水印的z-index,默认值为-1 (在背景层)。 |
bgColor |
ResourceColor |
"" |
背景色,默认为空。 |
waterMarkContainHeight |
number |
0 |
水印区域的总高度,动态计算。 |
waterMarkContainWidth |
number |
0 |
水印区域的总宽度,动态计算。 |
url |
string |
'' |
水印图片的URL,由WatermarkCanvas 生成。 |
4. 方法定义
(1) watermarkContent
-
功能:定义水印内容的构建器。
-
逻辑:
-
- 使用
Image
组件加载水印图片this.url
。 - 设置水印图片的宽度、高度和透明度。
- 使用
5. 构建UI (build
方法)
- 功能:构建组件的UI布局。
- 逻辑:
-
- Stack布局容器:
-
-
- 使用
Stack
作为根容器。
- 使用
-
-
- 调用
WatermarkCanvas
生成水印图片:
- 调用
-
-
- 将所有水印相关的属性传递给
WatermarkCanvas
。 - 当水印生成完成后,通过
onComplete
回调更新this.url
。
- 将所有水印相关的属性传递给
-
-
- 根据
z-index
决定水印位置:
- 根据
-
-
- 如果
z-index <= 0
,将水印放在背景层。 - 如果
z-index > 0
,将水印放在前景层。
- 如果
-
-
- Column容器:
-
-
- 定义一个
Column
容器,用于放置默认插槽内容。 - 监听区域变化事件,动态更新水印区域的宽度和高度。
- 定义一个
-
-
- 设置背景色:
-
-
- 调用
getBgColor
方法设置背景色。
- 调用
-
6. 总结
- 核心功能:
IBestWatermark
组件实现了灵活的水印功能,支持图片水印和文字水印,并允许用户自定义水印的样式、位置和透明度。 - 动态特性:通过监听区域变化事件,动态调整水印区域的尺寸。
- 扩展性:支持通过插槽插入用户自定义内容,增强了组件的灵活性和可复用性。
WatermarkCanvas.est代码介绍
接下来就看下文件WatermarkCanvas.est(已添加相关注释)
以下是WatermarkCanvas
组件的详细解释,分为几个主要部分进行说明:
1. 导入模块
-
功能:导入了必要的类型和工具函数。
-
IBestStringNumber
:定义了一种支持字符串或数字的类型。GlobalStore
:用于访问全局存储。base64ToPixelMap
和getResourceStr
:工具函数,分别用于将Base64字符串转换为像素映射和获取资源字符串。
2. 定义接口
-
功能:定义了两个接口,用于类型检查。
-
DrawCanvasItemArgs
:用于传递绘制单个水印块的参数。CountData
:用于存储计算出的水平和垂直方向上的水印数量。
3. 定义WatermarkCanvas
组件
- 功能:定义了一个名为
WatermarkCanvas
的组件,负责生成和管理水印的绘制。
4. 属性定义
以下是WatermarkCanvas
组件中定义的属性及其作用:
属性名 | 类型 | 默认值 | 描述 |
---|---|---|---|
colorMode |
ColorMode |
ColorMode.LIGHT |
全局颜色模式,从存储中加载。 |
settings |
RenderingContextSettings |
渲染上下文设置。 | |
context |
CanvasRenderingContext2D |
2D画布渲染上下文。 | |
waterMarkWidth |
number |
100 |
图片水印的宽度。 |
waterMarkHeight |
number |
100 |
图片水印的高度。 |
waterMarkOpacity |
number |
0.3 |
水印透明度,默认值为0.3 。 |
rotateDeg |
number |
-22 |
水印旋转角度。 |
waterMarkContainHeight |
number |
0 |
水印区域的总高度。 |
waterMarkContainWidth |
number |
0 |
水印区域的总宽度。 |
gapX |
number |
0 |
水印之间的水平间隔,默认值为0 。 |
gapY |
number |
0 |
水印之间的垂直间隔,默认值为0 。 |
imageUrl |
ResourceStr |
'' |
图片水印的链接。 |
text |
ResourceStr |
'' |
文字水印内容。 |
fontSize |
number |
14 |
文字水印的字体大小。 |
fontFamily |
string |
'sans-serif' |
文字水印的字体类型。 |
fontColor |
`IBestStringNumber | CanvasGradient | CanvasPattern` |
onComplete |
(url: string) => void |
() => {} |
水印生成完成后的回调函数。 |
resourceManager |
GlobalStore.context.resourceManager |
资源管理器,用于获取资源内容。 |
5. 方法定义
(1) getFontColor
-
功能:获取文字颜色。
-
逻辑:
-
- 如果
fontColor
已设置,则返回fontColor
。 - 否则根据
colorMode
选择默认颜色:
- 如果
-
-
colorMode == 0
:返回#f5f5f5
。- 否则返回
#323232
。
-
(2) getText
-
功能:获取文字水印的文案。
-
逻辑:
-
- 调用
getResourceStr
函数获取资源字符串this.text
。
- 调用
(3) clearCanvas
-
功能:清除画布上的内容。
-
逻辑:
-
- 使用
clearRect
方法清除指定区域的内容。
- 使用
(4) getXYCount
- 功能:计算在给定区域内绘制水印所需的水平和垂直数量。
- 逻辑:
-
- 计算水平数量 (
countX
):
- 计算水平数量 (
-
-
- 根据
waterMarkContainWidth
、width
和gapX
计算水平方向上的水印数量。 - 确保至少有一个水印。
- 根据
-
-
- 计算垂直数量 (
countY
):
- 计算垂直数量 (
-
-
- 根据
waterMarkContainHeight
、height
和gapY
计算垂直方向上的水印数量。 - 确保至少有一个水印。
- 根据
-
-
- 避免留白:
-
-
- 为了防止旋转导致的部分留白,增加
countY
和countX
各2个单位。
- 为了防止旋转导致的部分留白,增加
-
-
- 返回结果:
-
-
- 返回包含
countX
和countY
的对象。
- 返回包含
-
(5) drawCanvasItem
- 功能:在画布上绘制多个水印块。
- 逻辑:
-
- 获取参数:
-
-
- 从
data
对象中提取width
、height
、rotateDeg
和callback
。
- 从
-
-
- 计算绘制数量:
-
-
- 调用
getXYCount
方法获取水平和垂直方向上的绘制数量countX
和countY
。
- 调用
-
-
- 绘制矩阵:
-
-
- 使用嵌套的
for
循环遍历每个水印块的位置。 - 计算每个水印块的
x
和y
坐标。 - 保存当前的渲染状态。
- 平移和旋转画布到指定位置。
- 调用
callback
函数执行具体的绘制操作。 - 还原之前的渲染状态。
- 使用嵌套的
-
-
- 生成数据URL:
-
-
- 将绘制好的画布内容转换为PNG格式的数据URL。
- 如果生成的URL不是默认的
data:image/png
,则调用onComplete
回调函数并传递生成的URL。
-
(6) handleLayoutImg
- 功能:绘制图片水印。
- 逻辑:
-
- 加载图片模型:
-
-
- 根据
imageUrl
的类型加载图片模型。 - 如果
imageUrl
是字符串:
- 根据
-
-
-
-
- 如果以
data:image
开头,调用base64ToPixelMap
将Base64字符串转换为PixelMap
。 - 否则,创建
ImageBitmap
对象。
- 如果以
-
-
-
-
- 如果
imageUrl
是资源对象,调用resourceManager.getMediaContentBase64Sync
获取Base64字符串,再转换为PixelMap
。
- 如果
-
-
- 绘制图片:
-
-
- 调用
drawCanvasItem
方法,传递图片的宽度、高度、旋转角度和绘制回调函数。 - 在回调函数中,使用
drawImage
方法在指定位置绘制图片。
- 调用
-
(7) handleLayoutText
- 功能:绘制文字水印。
- 逻辑:
-
- 获取文字和颜色:
-
-
- 调用
getText
方法获取文字内容。 - 调用
getFontColor
方法获取文字颜色。
- 调用
-
-
- 设置字体样式:
-
-
- 设置
fillStyle
为文字颜色。 - 设置
font
属性为字体大小和字体类型。
- 设置
-
-
- 测量文字宽度:
-
-
- 使用
measureText
方法测量文字的宽度。
- 使用
-
-
- 绘制文字:
-
-
- 调用
drawCanvasItem
方法,传递文字的宽度、高度、旋转角度和绘制回调函数。 - 在回调函数中,使用
fillText
方法在指定位置绘制文字。
- 调用
-
(8) drawWaterMark
- 功能:根据配置绘制水印。
- 逻辑:
-
- 清除画布:
-
-
- 调用
clearCanvas
方法清除画布上的内容。
- 调用
-
-
- 设置文本基线:
-
-
- 设置
textBaseline
为'top'
,确保文字从顶部对齐。
- 设置
-
-
- 转换旋转角度:
-
-
- 将
rotateDeg
从度数转换为弧度。
- 将
-
-
- 绘制水印:
-
-
- 如果
text
属性存在,调用handleLayoutText
方法绘制文字水印。 - 否则如果
imageUrl
属性存在,调用handleLayoutImg
方法绘制图片水印。
- 如果
-
(9) build
- 功能:构建Canvas组件并设置其属性。
- 逻辑:
-
- 创建Canvas组件:
-
-
- 使用
Canvas
组件并传入context
。
- 使用
-
-
- 设置宽度和高度:
-
-
- 设置Canvas的宽度为
waterMarkContainWidth
。 - 设置Canvas的高度为
waterMarkContainHeight
。
- 设置Canvas的宽度为
-
-
- 设置就绪回调:
-
-
- 在Canvas准备好后,调用
drawWaterMark
方法绘制水印。
- 在Canvas准备好后,调用
-
-
- 设置透明度和启用状态:
-
-
- 设置Canvas的透明度为
0
,使其不可见。 - 设置Canvas的启用状态为
false
,使其不可交互。
- 设置Canvas的透明度为
-
6. 流程图
7. 总结
- 核心功能:
WatermarkCanvas
组件负责生成和管理水印的绘制,支持图片水印和文字水印。 - 动态特性:根据传入的属性动态计算水印的数量和位置,并支持旋转和透明度设置。
- 扩展性:通过回调函数
onComplete
,允许外部处理生成的水印图片。
五. 总结
快速回顾:Watermark 组件核心亮点
- 动态渲染引擎
-
- 通过
Canvas
实现文字/图片水印的矩阵排列,支持旋转、透明度、间隔动态调节。 - 自动适配容器尺寸变化(
onAreaChange
监听),实时更新布局。
- 通过
- 主题与资源兼容
-
- 深浅模式自动切换水印颜色,兼容本地资源、网络图片、Base64 格式。
- 通过
@StorageLink
实时响应全局主题变更。
- 性能优化设计
-
- 仅在
Canvas
就绪后绘制(onReady
),避免白屏。 - 通过
toDataURL
生成静态图片,减少重复绘制开销。
- 仅在
- 开发者友好
-
- 属性驱动配置(
gapX
/gapY
/rotateDeg
),支持插槽嵌入自定义内容。 - 源码模块化清晰(
WatermarkCanvas
负责绘制逻辑,IBestWatermark
负责 UI 组合)。
- 属性驱动配置(
一句话总结:IBest-UI 的 Watermark 组件通过声明式 API 和 Canvas 高效渲染,为鸿蒙开发者提供了“零负担”的水印解决方案,是学习 ArkTS 进阶技巧的优质案例。
