HarmonyOS NEXT实战:页面水印

5 阅读2分钟

##HarmonyOS Next实战##HarmonyOS SDK应用服务##教育##

在软件开发中,水印是一种在应用页面、图片或文档中嵌入的标记,它通常采用文字或图案的形式展现。水印通常有以下用途:

  • 标识来源:可用于标识应用、各种文件的来源或作者,确保产权的归属。
  • 版权保护:可携带版权保护信息,有效防止他人篡改、盗用、非法复制。
  • 艺术效果:可作为一种艺术效果,为图片或应用增添独特的风格。

实现思路:

  1. 创建Canvas画布,在画布上绘制水印。
  2. 使用浮层overlay属性,将画布与UI页面组件融合显示。

知识点: Canvas提供画布组件,用于自定义绘制图形。使用CanvasRenderingContext2D对象在Canvas组件上进行绘制,其中fillText()方法用于绘制文本,drawImage()方法用于图像绘制。

Canvas.onReady Canvas组件初始化完成时或者Canvas组件发生大小变化时的事件回调。 当该事件被触发时画布被清空,该事件之后Canvas组件宽高确定且可获取,可使用Canvas相关API进行绘制。当Canvas组件仅发生位置变化时,只触发onAreaChange事件,不触发onReady事件。onAreaChange事件在onReady事件后触发。

Canvas.hitTestBehavior 设置组件的触摸测试类型。默认值: HitTestMode.Default

实现绘制水印draw()方法。绘制的起点默认为坐标轴的原点(画布的左上角),通过坐标轴的平移及旋转,实现在画布的不同位置、不同角度绘制水印。如果水印有一定旋转角度,想保证第一个水印能完整显示,需要对绘制的起点做平移,平移距离通过旋转角度及水印宽高计算。 最终通过CanvasRenderingContext2D.fillText()方法进行水印文字的绘制。

fillText(text: string, x: number, y: number, maxWidth?: number): void

绘制填充类文本。 text:需要绘制的文本内容。 x:需要绘制的文本的左下角x坐标。默认单位:vp。 y:需要绘制的文本的左下角y坐标。默认单位:vp。 maxWidth:指定文本允许的最大宽度。默认单位:vp。默认值:不限制宽度。

创建水印:BuildWatermark

@Builder
export function BuildWatermark() {
  Watermark()
    .width('100%')
    .height('100%')
}

@Component
struct Watermark {
  private settings: RenderingContextSettings = new RenderingContextSettings(true);
  private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings);
  @Prop watermarkWidth: number = 120;
  @Prop watermarkHeight: number = 120;
  @Prop watermarkText: string = '这是文字水印';
  @Prop rotationAngle: number = -30;
  @Prop fillColor: string | number | CanvasGradient | CanvasPattern = '#10000000';
  @Prop font: string = '16vp';

  build() {
    Canvas(this.context)
      .width('100%')
      .height('100%')
      .hitTestBehavior(HitTestMode.Transparent)
      .onReady(() => this.draw())
  }

  draw() {
    this.context.fillStyle = this.fillColor;
    this.context.font = this.font;
    const colCount = Math.ceil(this.context.width / this.watermarkWidth);
    const rowCount = Math.ceil(this.context.height / this.watermarkHeight);
    for (let col = 0; col <= colCount; col++) {
      let row = 0;
      for (; row <= rowCount; row++) {
        const angle = this.rotationAngle * Math.PI / 180;
        this.context.rotate(angle);
        const positionX = this.rotationAngle > 0 ? this.watermarkHeight * Math.tan(angle) : 0;
        const positionY = this.rotationAngle > 0 ? 0 : this.watermarkWidth * Math.tan(-angle);
        this.context.fillText(this.watermarkText, positionX, positionY);
        this.context.rotate(-angle);
        this.context.translate(0, this.watermarkHeight);
      }
      this.context.translate(0, -this.watermarkHeight * row);
      this.context.translate(this.watermarkWidth, 0);
    }
  }
}

使用水印:

import { BuildWatermark } from './BuildWatermark';

@Entry
@Component
struct WatermarkDemoPage {
  @State message: string = 'WatermarkDemo';

  build() {
    Column() {
      Text(this.message)
        .fontWeight(FontWeight.Bold)
    }
    .height('100%')
    .width('100%')
    .overlay(BuildWatermark())
  }
}