
回复
水印在我们日常使用的APP中是很常见的。本文记录一下如何实现一个自定义的水印,可以自定义设置水印文字、间距、旋转角度。
实现效果:
实现思路:
核心方法是使用Canvas绘制文字,只需要在指定的间隔上绘制我们想要展示的水印文字内容就可以了。需要使用到Canvas的坐标系平移、坐标系旋转、文字绘制。
源码:
import { WindowUtils } from "../utils/WindowUtils"
@Entry
@ComponentV2
struct WaterMarkTest{
private maxHorizontalSpace:number = Math.floor(px2vp(WindowUtils.getWindowWidth()/2))
private maxVerticalSpace:number = Math.floor(px2vp(WindowUtils.getWindowHeight()/2))
@Local horizontalSpace:number=100 //文字水印中心 水平间距
@Local verticalSpace:number=100 //文字水印中心 垂直间距
@Local rotateAngle:number=10 //文字水印中心 旋转角度
@Local textSize:number=20 //水印字体大小
build() {
Stack(){
Column(){
Row({ space: 10 }) {
Text('水平间距:' + this.horizontalSpace)
Slider({
value: this.horizontalSpace,
min: 30,
max: this.maxHorizontalSpace,
style: SliderStyle.OutSet
}).width('50%')
.onChange((value: number) => {
this.horizontalSpace = value;
})
}
Row({ space: 10 }) {
Text('垂直间距:' + this.verticalSpace)
Slider({
value: this.verticalSpace,
min: 30,
max: this.maxVerticalSpace,
style: SliderStyle.OutSet
}).width('50%')
.onChange((value: number) => {
this.verticalSpace = value;
})
}
Row({ space: 10 }) {
Text('旋转角度:' + this.rotateAngle)
Slider({
value: this.rotateAngle,
min: 10,
max: 90,
style: SliderStyle.OutSet
}).width('50%')
.onChange((value: number) => {
this.rotateAngle = value;
})
}
Row({ space: 10 }) {
Text('textSize:' + this.textSize)
Slider({
value: this.textSize,
min: 20,
max: 40,
style: SliderStyle.OutSet
}).width('50%')
.onChange((value: number) => {
this.textSize = value;
})
}
}
WaterMark({waterMarkProperties: { horizontalSpace:this.horizontalSpace,
verticalSpace:this.verticalSpace,
rotateAngle:this.rotateAngle,
textColor:'#80000000',
textSize:this.textSize+'vp'}})
}
//以上是测试演示方法,实际使用可以使用overlay添加浮层
// .overlay(createWaterMarkView())
.width('100%')
.height('100%')
}
}
export interface WaterMarkProperties {
horizontalSpace: number //文字水印中心 水平间距
verticalSpace: number //文字水印中心 垂直间距
rotateAngle: number //文字水印中心 旋转角度
textColor: string //水印字体颜色
textSize: string //水印字体大小
}
@Builder
export function createWaterMarkView(waterMarkProperties:WaterMarkProperties={
horizontalSpace:60,
verticalSpace:60,
rotateAngle:30,
textColor:'#10000000',
textSize:'20vp'
}) {
WaterMark({waterMarkProperties:waterMarkProperties}).hitTestBehavior(HitTestMode.None)
}
@ComponentV2
struct WaterMark{
private waterMarkSettings: RenderingContextSettings = new RenderingContextSettings(true);
private waterMarkContext: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.waterMarkSettings);
@Require @Param waterMarkProperties:WaterMarkProperties
private waterMarkText:string='HarmonyOS开发笔记';
private waterMarkTextWidth:number = 0
@Monitor('waterMarkProperties')
draw(){
this.waterMarkContext.reset()
this.waterMarkTextWidth=this.getUIContext().getMeasureUtils().measureTextSize({
textContent: this.waterMarkText,
fontSize: this.waterMarkProperties.textSize
}).width as number
//设置水印文字的填充颜色
this.waterMarkContext.fillStyle = this.waterMarkProperties.textColor;
//设置水印文字大小
this.waterMarkContext.font = this.waterMarkProperties.textSize;
//设置水印对齐方式 文字水平和垂直方向居中
this.waterMarkContext.textAlign = 'center';
this.waterMarkContext.textBaseline = 'middle';
//外层循环:水平方向遍历(控制水印的水平分布)
//循环次数 = Canvas宽度 / 水平间距
for (let i = 0; i < this.waterMarkContext.width / this.waterMarkProperties.horizontalSpace; i++) {
//将Canvas原点(0,0)向右移动一个间距(每次循环右移,实现水平排列)
this.waterMarkContext.translate(this.waterMarkProperties.horizontalSpace, 0);
let j = 0;
// 内层循环:垂直方向遍历(控制水印的垂直分布)
// 循环次数 = Canvas高度 / 垂直间距
for (; j < this.waterMarkContext.height /this.waterMarkProperties.verticalSpace; j++) {
//文字旋转:将文字逆时针旋转指定角度(公式:Math.PI/180 * 角度)
this.waterMarkContext.rotate(-Math.PI / 180 * this.waterMarkProperties.rotateAngle);
this.waterMarkContext.fillText(this.waterMarkText, -px2vp(this.waterMarkTextWidth)/2, 0);
//恢复旋转:将Canvas旋转状态还原
this.waterMarkContext.rotate(Math.PI / 180 * this.waterMarkProperties.rotateAngle);
// 垂直平移:将Canvas原点向下移动一个间距(每次循环下移,实现垂直排列)
this.waterMarkContext.translate(0, this.waterMarkProperties.verticalSpace);
}
// 恢复垂直原点:内层循环结束后,将垂直方向的原点还原到初始高度
this.waterMarkContext.translate(0, -this.waterMarkProperties.verticalSpace * j);
}
}
build() {
Canvas(this.waterMarkContext)
.width('100%')
.height('100%')
.hitTestBehavior(HitTestMode.None)
.onReady(() => {
this.draw()
})
}
}
注意:
以上为了演示间距和角度,把水印组件的属性设置成可动态变化的,实际使用时,我们可以直接将配置的参数写死,不要重复渲染。给需要添加水印的组件使用overlay的方法添加。