HarmonyOS NEXT Practical: Page Watermark

In software development, a watermark is a mark embedded in an application page, image, or document, typically presented in the form of text or graphics. Watermarks are typically used for the following purposes: Source identification: can be used to identify the source or author of applications, various files, and ensure the ownership of property rights. Copyright protection: It can carry copyright protection information, effectively preventing others from tampering, stealing, and illegal copying. Artistic effect: It can be used as an artistic effect to add a unique style to images or applications. Implementation idea: Create a Canvas canvas and draw a watermark on it. Use the floating layer overlay property to integrate the canvas with UI page components for display. Knowledge points: Canvas provides canvas components for custom drawing of graphics. Use the CanvasRendering Context2D object to draw on the Canvas component, where the fillText() method is used to draw text and the drawImage() method is used to draw images. Canvas.onReady Event callback when Canvas component initialization is completed or when Canvas component size changes. When the event is triggered, the canvas is cleared, and after the event, the width and height of the Canvas component are determined and available for drawing using Canvas related APIs. When the Canvas component only undergoes a positional change, only the onAreaChange event is triggered and the onReady event is not triggered. The onAreaChange event is triggered after the onReady event. Canvas.hitTestBehavior Set the touch test type for the component. Default value: HitTestMode.Default Implement the draw() method for drawing watermarks. The default starting point for drawing is the origin of the coordinate axis (upper left corner of the canvas). By translating and rotating the coordinate axis, watermarks can be drawn at different positions and angles on the canvas. If the watermark has a certain rotation angle, in order to ensure that the first watermark can be displayed completely, it is necessary to translate the starting point of the drawing, and the translation distance is calculated based on the rotation angle and the width and height of the watermark. Finally, the watermark text was drawn using the CanvasRendering Context2D. tilText() method. fillText(text: string, x: number, y: number, maxWidth?: number): void Draw fill type text. text: The text content that needs to be drawn. x: The x-coordinate of the bottom left corner of the text to be drawn. Default Unit: vp。 y: The y-coordinate of the bottom left corner of the text to be drawn. Default Unit: vp。 maxWidth: Specify the maximum width allowed for the text. Default unit: vp. Default value: unlimited width. Create watermark: 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 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); } } } Use watermark: 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()) } }

Mar 26, 2025 - 14:14
 0
HarmonyOS NEXT Practical: Page Watermark

In software development, a watermark is a mark embedded in an application page, image, or document, typically presented in the form of text or graphics. Watermarks are typically used for the following purposes:

  • Source identification: can be used to identify the source or author of applications, various files, and ensure the ownership of property rights.
  • Copyright protection: It can carry copyright protection information, effectively preventing others from tampering, stealing, and illegal copying.
  • Artistic effect: It can be used as an artistic effect to add a unique style to images or applications.

Implementation idea:

  1. Create a Canvas canvas and draw a watermark on it.
  2. Use the floating layer overlay property to integrate the canvas with UI page components for display.

Knowledge points:
Canvas provides canvas components for custom drawing of graphics. Use the CanvasRendering Context2D object to draw on the Canvas component, where the fillText() method is used to draw text and the drawImage() method is used to draw images.

Canvas.onReady
Event callback when Canvas component initialization is completed or when Canvas component size changes.
When the event is triggered, the canvas is cleared, and after the event, the width and height of the Canvas component are determined and available for drawing using Canvas related APIs. When the Canvas component only undergoes a positional change, only the onAreaChange event is triggered and the onReady event is not triggered. The onAreaChange event is triggered after the onReady event.

Canvas.hitTestBehavior
Set the touch test type for the component. Default value: HitTestMode.Default

Implement the draw() method for drawing watermarks. The default starting point for drawing is the origin of the coordinate axis (upper left corner of the canvas). By translating and rotating the coordinate axis, watermarks can be drawn at different positions and angles on the canvas. If the watermark has a certain rotation angle, in order to ensure that the first watermark can be displayed completely, it is necessary to translate the starting point of the drawing, and the translation distance is calculated based on the rotation angle and the width and height of the watermark.
Finally, the watermark text was drawn using the CanvasRendering Context2D. tilText() method.

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

Draw fill type text.
text: The text content that needs to be drawn.
x: The x-coordinate of the bottom left corner of the text to be drawn. Default Unit: vp。
y: The y-coordinate of the bottom left corner of the text to be drawn. Default Unit: vp。
maxWidth: Specify the maximum width allowed for the text. Default unit: vp. Default value: unlimited width.

Create watermark: 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);
    }
  }
}

Use watermark:

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())
  }
}