HarmonyOS鸿蒙Next开发者技术支持-水印实现案例

HarmonyOS鸿蒙Next开发者技术支持-水印实现案例

一、项目概述 1.1 功能特性

基于HarmonyOS最新API实现

多种水印类型:文字水印、图片水印、二维码水印、时间戳水印

灵活的水印布局:平铺、居中、九宫格、自定义位置

水印样式定制:字体、颜色、透明度、旋转角度、大小

动态水印支持:时间、位置、用户信息等动态内容

批量水印处理:支持多张图片批量添加水印

水印安全保护:防篡改、防移除、隐形水印

二、架构设计 2.1 核心组件结构

水印系统
├── WatermarkManager.ets (水印管理器)
├── TextWatermark.ets (文字水印)
├── ImageWatermark.ets (图片水印)
├── QRWatermark.ets (二维码水印)
├── WatermarkLayout.ets (水印布局)
├── WatermarkCanvas.ets (水印画布)
└── WatermarkSecurity.ets (水印安全)

2.2 数据模型定义

// WatermarkModel.ets
// 水印类型枚举
export enum WatermarkType {
  TEXT = 'text',        // 文字水印
  IMAGE = 'image',      // 图片水印
  QR_CODE = 'qr_code',  // 二维码水印
  TIMESTAMP = 'timestamp', // 时间戳水印
  DYNAMIC = 'dynamic'   // 动态水印
}

// 水印布局枚举
export enum WatermarkLayout {
  TILE = 'tile',        // 平铺
  CENTER = 'center',    // 居中
  CORNER = 'corner',    // 四角
  GRID = 'grid',        // 九宫格
  CUSTOM = 'custom'     // 自定义
}

// 水印位置
export interface WatermarkPosition {
  x: number;           // X坐标(百分比或像素)
  y: number;           // Y坐标(百分比或像素)
  unit: 'percent' | 'pixel'; // 单位类型
}

// 水印样式配置
export interface WatermarkStyle {
  opacity: number;     // 透明度(0-1)
  rotation: number;    // 旋转角度(-180~180)
  scale: number;       // 缩放比例
  blendMode: BlendMode; // 混合模式
  shadow: WatermarkShadow; // 阴影效果
}

// 文字水印配置
export interface TextWatermarkConfig {
  type: WatermarkType.TEXT;
  content: string;     // 文字内容
  font: WatermarkFont; // 字体配置
  color: ResourceColor; // 文字颜色
}

// 图片水印配置
export interface ImageWatermarkConfig {
  type: WatermarkType.IMAGE;
  imageUrl: string;    // 图片URL
  width: number;       // 宽度
  height: number;      // 高度
}

// 水印配置
export interface WatermarkConfig {
  id: string;                     // 水印ID
  type: WatermarkType;            // 水印类型
  layout: WatermarkLayout;        // 布局方式
  positions: WatermarkPosition[ ]; // 位置列表
  style: WatermarkStyle;          // 样式配置
  textConfig?: TextWatermarkConfig; // 文字配置
  imageConfig?: ImageWatermarkConfig; // 图片配置
  security: WatermarkSecurity;    // 安全配置
}

// 默认配置
export class WatermarkDefaultConfig {
  static readonly DEFAULT_CONFIG: WatermarkConfig = {
    id: 'default',
    type: WatermarkType.TEXT,
    layout: WatermarkLayout.TILE,
    positions: [{ x: 50, y: 50, unit: 'percent' }],
    style: {
      opacity: 0.7,
      rotation: -15,
      scale: 1.0,
      blendMode: BlendMode.SourceOver,
      shadow: {
        enabled: false,
        color: '#000000',
        blur: 2,
        offsetX: 1,
        offsetY: 1
      }
    },
    textConfig: {
      type: WatermarkType.TEXT,
      content: 'Confidential',
      font: {
        size: 24,
        family: 'HarmonyOS Sans',
        weight: FontWeight.Bold
      },
      color: '#FF0000'
    },
    security: {
      antiRemoval: true,
      antiTamper: false,
      invisible: false
    }
  };
}

这里定义了水印系统的核心数据模型。WatermarkType枚举定义了支持的水印类型。WatermarkConfig接口包含水印的所有配置参数。

三、核心实现

3.1 水印管理器组件

// WatermarkManager.ets
[@Component](/user/Component)
export struct WatermarkManager {
  @State private watermarkConfigs: Map<string, WatermarkConfig> = new Map();
  @State private currentWatermark: WatermarkConfig = WatermarkDefaultConfig.DEFAULT_CONFIG;
  @State private isProcessing: boolean = false;

  private imageSource: image.ImageSource = image.createImageSource();
  private canvasRenderingContext: CanvasRenderingContext2D;

  // 添加水印到图片
  async addWatermarkToImage(imageUri: string, config: WatermarkConfig): Promise<string> {
    if (this.isProcessing) {
      throw new Error('正在处理中,请稍后');
    }

    this.isProcessing = true;

    try {
      // 步骤1:加载原始图片
      const originalImage = await this.loadImage(imageUri);
      
      // 步骤2:创建画布
      const canvas = await this.createCanvas(originalImage.width, originalImage.height);
      
      // 步骤3:绘制原始图片
      await this.drawImageToCanvas(canvas, originalImage);
      
      // 步骤4:添加水印
      await this.addWatermark(canvas, config);
      
      // 步骤5:导出处理后的图片
      const resultUri = await this.exportCanvasToImage(canvas);
      
      return resultUri;
      
    } catch (error) {
      throw new Error(`添加水印失败: ${error.message}`);
    } finally {
      this.isProcessing = false;
    }
  }

  // 批量添加水印
  async batchAddWatermark(imageUris: string[ ], config: WatermarkConfig): Promise<string[ ]> {
    const results: string[ ] = [ ];

    for (const imageUri of imageUris) {
      try {
        const result = await this.addWatermarkToImage(imageUri, config);
        results.push(result);
      } catch (error) {
        logger.error(`处理图片失败: ${imageUri}`, error);
        results.push(imageUri); // 返回原图
      }
    }

    return results;
  }

  // 创建水印画布
  private async createCanvas(width: number, height: number): Promise<CanvasRenderingContext2D> {
    const canvas = new Canvas();
    canvas.width = width;
    canvas.height = height;

    return canvas.getContext('2d');
  }

  // 加载图片
  private async loadImage(uri: string): Promise<ImageInfo> {
    try {
      const imageSource = image.createImageSource(uri);
      const imageInfo = await imageSource.getImageInfo();
      return imageInfo;
    } catch (error) {
      throw new Error(`加载图片失败: ${error.message}`);
    }
  }

  // 绘制图片到画布
  private async drawImageToCanvas(context: CanvasRenderingContext2D, imageInfo: ImageInfo): Promise<void> {
    const imageBitmap = await createImageBitmap(imageInfo.uri);
    context.drawImage(imageBitmap, 0, 0, imageInfo.width, imageInfo.height);
  }
}

WatermarkManager组件是水印系统的核心,负责水印的添加和管理。addWatermarkToImage方法实现完整的图片水印添加流程。

3.2 文字水印组件

// TextWatermark.ets
[@Component](/user/Component)
export struct TextWatermark {
  [@Prop](/user/Prop) config: TextWatermarkConfig;
  [@Prop](/user/Prop) style: WatermarkStyle;
  [@Prop](/user/Prop) position: WatermarkPosition;

  private canvasContext: CanvasRenderingContext2D;

  // 绘制文字水印
  async drawTextWatermark(context: CanvasRenderingContext2D, canvasWidth: number, canvasHeight: number): Promise<void> {
    this.canvasContext = context;

    // 设置文字样式
    this.setupTextStyle();

    // 计算水印位置
    const positions = this.calculateWatermarkPositions(canvasWidth, canvasHeight);

    // 绘制水印
    for (const pos of positions) {
      this.drawSingleWatermark(pos.x, pos.y);
    }
  }

  // 设置文字样式
  private setupTextStyle(): void {
    this.canvasContext.font = `${this.config.font.weight} ${this.config.font.size}px ${this.config.font.family}`;
    this.canvasContext.fillStyle = this.config.color;
    this.canvasContext.globalAlpha = this.style.opacity;

    // 设置文字阴影
    if (this.style.shadow.enabled) {
      this.canvasContext.shadowColor = this.style.shadow.color;
      this.canvasContext.shadowBlur = this.style.shadow.blur;
      this.canvasContext.shadowOffsetX = this.style.shadow.offsetX;
      this.canvasContext.shadowOffsetY = this.style.shadow.offsetY;
    }
  }

  // 计算水印位置
  private calculateWatermarkPositions(canvasWidth: number, canvasHeight: number): { x: number, y: number }[ ] {
    const positions: { x: number, y: number }[ ] = [ ];

    // 测量文字宽度
    const textMetrics = this.canvasContext.measureText(this.config.content);
    const textWidth = textMetrics.width;
    const textHeight = this.config.font.size;

    // 根据布局计算位置
    switch (this.layout) {
      case WatermarkLayout.TILE:
        // 平铺布局
        const horizontalSpacing = textWidth * 1.5;
        const verticalSpacing = textHeight * 2;
        
        for (let y = textHeight; y < canvasHeight; y += verticalSpacing) {
          for (let x = textWidth / 2; x < canvasWidth; x += horizontalSpacing) {
            positions.push({ x, y });
          }
        }
        break;
        
      case WatermarkLayout.CENTER:
        // 居中布局
        positions.push({
          x: canvasWidth / 2,
          y: canvasHeight / 2
        });
        break;
        
      case WatermarkLayout.CORNER:
        // 四角布局
        const margin = 20;
        positions.push(
          { x: margin, y: margin }, // 左上
          { x: canvasWidth - margin - textWidth, y: margin }, // 右上
          { x: margin, y: canvasHeight - margin }, // 左下
          { x: canvasWidth - margin - textWidth, y: canvasHeight - margin } // 右下
        );
        break;
        
      case WatermarkLayout.GRID:
        // 九宫格布局
        const gridPositions = [
          { x: canvasWidth * 0.25, y: canvasHeight * 0.25 },
          { x: canvasWidth * 0.5, y: canvasHeight * 0.25 },
          { x: canvasWidth * 0.75, y: canvasHeight * 0.25 },
          { x: canvasWidth * 0.25, y: canvasHeight * 0.5 },
          { x: canvasWidth * 0.5, y: canvasHeight * 0.5 },
          { x: canvasWidth * 0.75, y: canvasHeight * 0.5 },
          { x: canvasWidth * 0.25, y: canvasHeight * 0.75 },
          { x: canvasWidth * 0.5, y: canvasHeight * 0.75 },
          { x: canvasWidth * 0.75, y: canvasHeight * 0.75 }
        ];
        positions.push(...gridPositions);
        break;
        
      case WatermarkLayout.CUSTOM:
        // 自定义布局
        for (const pos of this.positions) {
          const x = pos.unit === 'percent' ? (pos.x / 100) * canvasWidth : pos.x;
          const y = pos.unit === 'percent' ? (pos.y / 100) * canvasHeight : pos.y;
          positions.push({ x, y });
        }
        break;
    }

    return positions;
  }

  // 绘制单个水印
  private drawSingleWatermark(x: number, y: number): void {
    this.canvasContext.save();

    // 应用旋转
    this.canvasContext.translate(x, y);
    this.canvasContext.rotate((this.style.rotation * Math.PI) / 180);

    // 绘制文字
    this.canvasContext.fillText(this.config.content, 0, 0);

    this.canvasContext.restore();
  }
}

TextWatermark组件实现文字水印功能。drawTextWatermark方法绘制文字水印,支持多种布局方式和样式效果。

3.3 图片水印组件

// ImageWatermark.ets
[@Component](/user/Component)
export struct ImageWatermark {
  [@Prop](/user/Prop) config: ImageWatermarkConfig;
  [@Prop](/user/Prop) style: WatermarkStyle;
  [@Prop](/user/Prop) position: WatermarkPosition;

  private canvasContext: CanvasRenderingContext2D;

  // 绘制图片水印
  async drawImageWatermark(context: CanvasRenderingContext2D, canvasWidth: number, canvasHeight: number): Promise<void> {
    this.canvasContext = context;

    // 加载水印图片
    const watermarkImage = await this.loadWatermarkImage();

    // 计算水印位置
    const positions = this.calculateImagePositions(canvasWidth, canvasHeight);

    // 绘制水印
    for (const pos of positions) {
      await this.drawSingleImageWatermark(watermarkImage, pos.x, pos.y);
    }
  }

  // 加载水印图片
  private async loadWatermarkImage(): Promise<ImageBitmap> {
    try {
      const imageSource = image.createImageSource(this.config.imageUrl);
      const imageInfo = await imageSource.getImageInfo();

      // 调整图片大小
      const resizedImage = await this.resizeImage(imageInfo, this.config.width, this.config.height);
      
      return await createImageBitmap(resizedImage);
    } catch (error) {
      throw new Error(`加载水印图片失败: ${error.message}`);
    }
  }

  // 调整图片大小
  private async resizeImage(imageInfo: ImageInfo, targetWidth: number, targetHeight: number): Promise<PixelMap> {
    const imageSource = image.createImageSource(imageInfo.uri);
    const resizeOptions = {
      desiredSize: {
        width: targetWidth,
        height: targetHeight
      }
    };

    return await imageSource.createPixelMap(resizeOptions);
  }

  // 计算图片水印位置
  private calculateImagePositions(canvasWidth: number, canvasHeight: number): { x: number, y: number }[ ] {
    const positions: { x: number, y: number }[ ] = [ ];

    switch (this.layout) {
      case WatermarkLayout.TILE:
        // 平铺布局
        const horizontalSpacing = this.config.width * 1.2;
        const verticalSpacing = this.config.height * 1.5;
        
        for (let y = this.config.height; y < canvasHeight; y += verticalSpacing) {
          for (let x = this.config.width / 2; x < canvasWidth; x += horizontalSpacing) {
            positions.push({ x, y });
          }
        }
        break;
        
      case WatermarkLayout.CENTER:
        // 居中布局
        positions.push({
          x: (canvasWidth - this.config.width) / 2,
          y: (canvasHeight - this.config.height) / 2
        });
        break;
        
      case WatermarkLayout.CORNER:
        // 四角布局
        const margin = 10;
        positions.push(
          { x: margin, y: margin }, // 左上
          { x: canvasWidth - this.config.width - margin, y: margin }, // 右上
          { x: margin, y: canvasHeight - this.config.height - margin }, // 左下
          { x: canvasWidth - this.config.width - margin, y: canvasHeight - this.config.height - margin } // 右下
        );
        break;
        
      case WatermarkLayout.CUSTOM:
        // 自定义布局
        for (const pos of this.positions) {
          const x = pos.unit === 'percent' ? 
            (pos.x / 100) * canvasWidth - this.config.width / 2 : pos.x;
          const y = pos.unit === 'percent' ? 
            (pos.y / 100) * canvasHeight - this.config.height / 2 : pos.y;
          positions.push({ x, y });
        }
        break;
    }

    return positions;
  }

  // 绘制单个图片水印
  private async drawSingleImageWatermark(watermarkImage: ImageBitmap, x: number, y: number): Promise<void> {
    this.canvasContext.save();

    // 设置透明度
    this.canvasContext.globalAlpha = this.style.opacity;

    // 应用旋转
    this.canvasContext.translate(x + this.config.width / 2, y + this.config.height / 2);
    this.canvasContext.rotate((this.style.rotation * Math.PI) / 180);

    // 绘制图片
    this.canvasContext.drawImage(
      watermarkImage, 
      -this.config.width / 2, 
      -this.config.height / 2,
      this.config.width,
      this.config.height
    );

    this.canvasContext.restore();
  }
}

ImageWatermark组件实现图片水印功能。drawImageWatermark方法绘制图片水印,支持图片缩放、旋转和多种布局方式。

3.4 水印布局管理器

// WatermarkLayout.ets
[@Component](/user/Component)
export struct WatermarkLayout {
  [@Prop](/user/Prop) layoutType: WatermarkLayout;

  [@Prop](/user/Prop) positions: WatermarkPosition[ ];

  [@Prop](/user/Prop) canvasWidth: number;
  [@Prop](/user/Prop) canvasHeight: number;

  // 计算水印布局
  calculateLayout(): { x: number,

更多关于HarmonyOS鸿蒙Next开发者技术支持-水印实现案例的实战教程也可以访问 https://www.itying.com/category-93-b0.html

3 回复

666

更多关于HarmonyOS鸿蒙Next开发者技术支持-水印实现案例的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


鸿蒙Next水印实现基于ArkUI框架,可通过Canvas组件绘制。使用CanvasRenderingContext2D的fillText方法添加文本水印,设置透明度、旋转角度和重复平铺。关键步骤:创建Canvas组件,获取绘图上下文,循环绘制水印文本。支持动态调整文本内容、颜色和密度。

这是一个非常详尽和专业的HarmonyOS Next水印实现案例分享,架构清晰,代码完整,覆盖了从基础到高级的多种应用场景。以下是对您方案的几点专业点评:

架构设计的亮点:

  1. 模块化设计优秀:将水印管理器、不同类型的水印组件、布局和安全模块分离,符合高内聚、低耦合的原则,便于维护和扩展。
  2. 数据模型定义完备WatermarkConfig 及相关接口设计得很全面,涵盖了样式、布局、安全等所有配置项,为灵活定制打下了良好基础。
  3. 类型安全:全程使用TypeScript,并定义了完整的枚举和接口,能有效减少运行时错误。

核心实现的技术要点:

  1. WatermarkManager 流程清晰:加载图片、创建画布、绘制、导出的异步流程处理得当,错误处理机制完善。
  2. 布局算法实用:平铺、九宫格、四角等布局的计算逻辑考虑到了实际视觉效果,例如平铺布局的交错排列。
  3. Canvas API运用熟练:正确使用了 save()/restore() 来管理旋转、透明度等状态,避免了样式污染。

高级特性的深入性:

  1. 动态水印:整合系统能力(如地理位置)生成动态内容,增加了水印的实用性和实时性。
  2. 安全保护:防移除(冗余数据)、防篡改(哈希校验)、隐形水印(LSB隐写)的实现方案,展示了较高的安全水位。需要注意的是,隐写术等高级安全功能在实现时需要仔细评估其对图像质量和性能的影响。
  3. 二维码水印:将信息编码为二维码嵌入,是一个很好的结合,适用于版权追踪、信息关联等场景。

HarmonyOS Next API 使用的合规性与建议: 您案例中使用的 CanvasImage 相关API是ArkTS/OpenHarmony的标准能力。对于HarmonyOS Next,以下几点可供参考:

  • 资源管理:案例中使用了 ResourceColor,这符合HarmonyOS的资源访问规范。对于图片资源,确保使用 $r$rawfile 等方式正确引用。
  • 权限声明:如果动态水印涉及获取位置 (geoLocationManager),或安全模块使用加密 (crypto),需要在应用的 module.json5 配置文件中声明相应的权限 (ohos.permission.LOCATION, ohos.permission.CRYPTO)。
  • Worker使用:您提到了批量处理优化可使用Worker。在HarmonyOS中,可以使用 TaskPoolWorker 来执行密集的图片处理任务,避免阻塞UI线程,提升用户体验。

性能与最佳实践的补充: 您提到的性能优化和用户体验建议都非常关键。此外,在处理超大图片或批量处理时,可以:

  • 采用分块或渐进式处理,避免一次性加载过多数据导致内存峰值。
  • 利用 PixelMap 进行高效的像素级操作,它比通用的 Canvas 在某些场景下性能更优。
  • 安全模块的加密操作(如AES)属于CPU密集型任务,务必在非UI线程执行。

总结: 您提供的这个案例远超一个简单的“示例”,它是一套具备生产级参考价值的完整解决方案。结构清晰、功能全面、代码规范,并充分考虑到了安全性、性能和用户体验。这对于其他开发者学习如何在HarmonyOS Next上实现复杂图像处理功能具有很高的借鉴意义。

回到顶部