当前位置: 首页 > article >正文

61.Harmonyos NEXT 图片预览组件之数据模型设计与实现

温馨提示:本篇博客的详细代码已发布到 git : https://gitcode.com/nutpi/HarmonyosNext 可以下载运行哦!

Harmonyos NEXT 图片预览组件之数据模型设计与实现

文章目录

  • Harmonyos NEXT 图片预览组件之数据模型设计与实现
    • 效果预览
    • 一、数据模型概述
      • 1. 数据模型的作用
      • 2. 数据模型的设计原则
    • 二、ScaleModel - 缩放模型
      • 1. 模型定义
      • 2. 属性说明
      • 3. 方法说明
      • 4. 使用示例
    • 三、RotateModel - 旋转模型
      • 1. 模型定义
      • 2. 属性说明
      • 3. 方法说明
      • 4. 使用示例
    • 四、OffsetModel - 偏移模型
      • 1. 模型定义
      • 2. 属性说明
      • 3. 方法说明
      • 4. 使用示例
    • 五、CommonLazyDataSourceModel - 懒加载数据源模型
      • 1. 模型定义
      • 2. 继承关系
    • 六 总结

效果预览

一、数据模型概述

图片预览组件采用了模型驱动的设计思想,将不同的交互状态抽象为独立的数据模型,实现了状态管理的解耦和代码的高内聚。本文将详细介绍图片预览组件中的四个核心数据模型:ScaleModel、RotateModel、OffsetModel和CommonLazyDataSourceModel。

1. 数据模型的作用

在图片预览组件中,数据模型主要承担以下职责:

  1. 状态封装:将相关的状态变量和操作方法封装在一起
  2. 数据持久化:记录交互过程中的状态变化
  3. 行为一致性:提供统一的接口和行为模式
  4. 状态重置:支持状态的重置和恢复

2. 数据模型的设计原则

设计原则说明实现方式
单一职责每个模型只负责一种类型的状态管理分离缩放、旋转、偏移为独立模型
状态可观察状态变化可被组件感知和响应使用@Observed装饰器
接口一致性提供统一的方法接口所有模型都实现reset()和stash()方法
默认值设置提供合理的默认值构造函数中设置默认参数

二、ScaleModel - 缩放模型

1. 模型定义

@Observed
export class ScaleModel {
  // 本次缩放因子,用于控制图片的大小显示
  public scaleValue: number;
  // 记录上次缩放完后的缩放因子
  public lastValue: number;
  // 最大放大值
  public maxScaleValue: number;
  // 额外比例值
  public extraScaleValue: number;
  // 默认缩放值
  public readonly defaultScaleValue: number = 1;

  constructor(scaleValue: number = 1.0, lastValue: number = 1.0,
              maxScaleValue: number = 1.5, extraScaleValue: number = 0.2) {
    this.scaleValue = scaleValue;
    this.lastValue = lastValue;
    this.maxScaleValue = maxScaleValue;
    this.extraScaleValue = extraScaleValue;
  }

  reset(): void {
    this.scaleValue = this.defaultScaleValue;
    this.lastValue = this.scaleValue;
  }

  stash(): void {
    this.lastValue = this.scaleValue;
  }
}

2. 属性说明

属性名类型说明
scaleValuenumber当前缩放因子,用于控制图片的显示大小
lastValuenumber记录上次缩放完成后的缩放因子,用于计算相对缩放
maxScaleValuenumber最大允许的放大值,防止过度放大
extraScaleValuenumber额外比例值,用于提供弹性缩放体验
defaultScaleValuenumber默认缩放值,固定为1.0

3. 方法说明

  • reset(): 重置缩放状态到默认值
  • stash(): 保存当前缩放值为最后缩放值,用于下次缩放的基准计算

4. 使用示例

// 创建缩放模型实例
this.imageScaleInfo = new ScaleModel(1.0, 1.0, 1.5, 0.3);

// 在双指缩放手势中使用
PinchGesture({ fingers: 2, distance: 1 })
    .onActionUpdate((event: GestureEvent) => {
        // 计算新的缩放值
        let scale = this.imageScaleInfo.lastValue * event.scale;
        
        // 限制缩放范围
        if (scale > this.imageScaleInfo.maxScaleValue * (1 + this.imageScaleInfo.extraScaleValue)) {
            scale = this.imageScaleInfo.maxScaleValue * (1 + this.imageScaleInfo.extraScaleValue);
        }
        if (scale < this.imageScaleInfo.defaultScaleValue * (1 - this.imageScaleInfo.extraScaleValue)) {
            scale = this.imageScaleInfo.defaultScaleValue * (1 - this.imageScaleInfo.extraScaleValue);
        }
        
        // 更新当前缩放值
        this.imageScaleInfo.scaleValue = scale;
        
        // 应用到矩阵变换
        this.matrix = matrix4.identity().scale({
            x: this.imageScaleInfo.scaleValue,
            y: this.imageScaleInfo.scaleValue,
        }).copy();
    })
    .onActionEnd(() => {
        // 保存当前缩放值为最后缩放值
        this.imageScaleInfo.stash();
    })

三、RotateModel - 旋转模型

1. 模型定义

@Observed
export class RotateModel {
    // 当前旋转角度
    public currentRotate: number;
    // 最后的角度
    public lastRotate: number = 0;
    // 起步触发旋转的角度
    public startAngle:number = 20;

    constructor(currentX: number = 0) {
        this.currentRotate = currentX;
    }

    // 重置
    reset(): void {
        this.currentRotate = 0;
        this.lastRotate = 0;
    }

    // 保存最后的数据
    stash(): void {
        // 一个完整的圆的角度是360度
        let angle = 360;
        this.lastRotate = this.currentRotate % angle;
    }
}

2. 属性说明

属性名类型说明
currentRotatenumber当前旋转角度,用于实时控制图片旋转
lastRotatenumber最后保存的旋转角度,用于计算相对旋转
startAnglenumber触发旋转的最小角度阈值,防止误触

3. 方法说明

  • reset(): 重置旋转状态到0度
  • stash(): 保存当前旋转角度为最后旋转角度,并进行360度取模运算

4. 使用示例

// 创建旋转模型实例
this.imageRotateInfo = new RotateModel();

// 在双指旋转手势中使用
RotationGesture({ angle: this.imageRotateInfo.startAngle })
    .onActionUpdate((event: GestureEvent) => {
        let angle = this.imageRotateInfo.lastRotate + event.angle
        if (event.angle > 0) {
            angle -= this.imageRotateInfo.startAngle;
        } else {
            angle += this.imageRotateInfo.startAngle;
        }
        
        // 应用旋转变换
        this.matrix = matrix4.identity()
            .rotate({
                z: 1,
                angle: angle,
            }).copy();
            
        // 更新当前旋转角度
        this.imageRotateInfo.currentRotate = angle;
    })
    .onActionEnd(() => {
        // 保存当前旋转角度
        this.imageRotateInfo.stash();
    })

四、OffsetModel - 偏移模型

1. 模型定义

@Observed
export class OffsetModel {
  // 当前移动偏移量 X
  public currentX: number;
  // 当前移动偏移量 Y
  public currentY: number;
  // 最后的偏移量 X
  public lastX: number = 0;
  // 最后的偏移量 Y
  public lastY: number = 0;

  constructor(currentX: number = 0, currentY: number = 0) {
    this.currentX = currentX;
    this.currentY = currentY;
  }

  reset(): void {
    this.currentX = 0;
    this.currentY = 0;
    this.lastX = 0;
    this.lastY = 0;
  }

  stash(): void {
    this.lastX = this.currentX;
    this.lastY = this.currentY;
  }
}

2. 属性说明

属性名类型说明
currentXnumber当前X轴偏移量,用于实时控制图片水平位置
currentYnumber当前Y轴偏移量,用于实时控制图片垂直位置
lastXnumber最后保存的X轴偏移量,用于计算相对移动
lastYnumber最后保存的Y轴偏移量,用于计算相对移动

3. 方法说明

  • reset(): 重置所有偏移量为0
  • stash(): 保存当前偏移量为最后偏移量

4. 使用示例

// 创建偏移模型实例
this.imageOffsetInfo = new OffsetModel(0, 0);

// 在单指拖动手势中使用
PanGesture({ fingers: 1 })
    .onActionUpdate((event: GestureEvent) => {
        // 计算新的偏移量
        let calculatedOffsetX = this.imageOffsetInfo.lastX + event.offsetX;
        let calculatedOffsetY = this.imageOffsetInfo.lastY + event.offsetY;
        
        // 更新当前偏移量
        this.imageOffsetInfo.currentX = calculatedOffsetX;
        this.imageOffsetInfo.currentY = calculatedOffsetY;
        
        // 应用偏移
        this.offset({
            x: this.imageOffsetInfo.currentX,
            y: this.imageOffsetInfo.currentY
        });
    })
    .onActionEnd(() => {
        // 保存当前偏移量
        this.imageOffsetInfo.stash();
        // 评估边界
        this.evaluateBound();
    })

五、CommonLazyDataSourceModel - 懒加载数据源模型

1. 模型定义

export class CommonLazyDataSourceModel<T> extends BasicDataSource<T> {
  private dataArray: T[] = [];

  public totalCount(): number {
    return this.dataArray.length;
  }

  public getData(index: number): T {
    return this.dataArray[index];
  }

  public addData(index: number, data: T): void {
    this.dataArray.splice(index, 0, data);
    this.notifyDataAdd(index);
  }

  public pushData(data: T): void {
    this.dataArray.push(data);
    this.notifyDataAdd(this.dataArray.length - 1);
  }

  public clearAndPushAll(data: T[]): void {
    this.dataArray = [];
    this.dataArray.push(...data);
    this.notifyDataReload();
  }
}

2. 继承关系

CommonLazyDataSourceModel继承自BasicDataSource,而BasicDataSource实现了IDataSource接口,提供了数据变化通知机制。

class BasicDataSource <T> implements IDataSource {
  private listeners: DataChangeListener[] = [];
  private originDataArray: T[] = [];

  // 注册数据变化监听器
  registerDataChangeListener(listener: DataChangeListener): void {
    if (this.listeners.indexOf(listener) < 0) {
      this.listeners.push(listener);
    }
  }

  // 取消注册数据变化监听器
  unregisterDataChangeListener(listener: DataChangeListener): void {
    const pos = this.listeners.indexOf(listener);
    if (pos >= 0) {
      this.listeners.splice(pos, 1);
    }
  }

  // 通知数据重载
  notifyDataReload(): void {
    this.listeners.forEach(listener => {
      listener.onDataReloaded();
    })
  }

  // 通知数据添加
  notifyDataAdd(index: number): void {
    this.listeners.forEach(listener => {
      listener.onDataAdd(index);
    })
  }

  // 其他通知方法...
}

六 总结

本文介绍了在开发中常用的几个数据模型,包括OffsetModel、CommonLazyDataSourceModel,以及它们在开发中的应用场景。通过使用这些数据模型,可以简化开发过程,提高代码的可读性和可维护性。


http://www.kler.cn/a/585216.html

相关文章:

  • API自动化测试实战:Postman + Newman/Pytest的深度解析
  • 注意力机制,层归一化,RBA。KAN-ODE,小波KAN
  • 如何使用Postman,通过Mock的方式测试我们的API
  • 【python】一文掌握 Conda 指令 (anaconda备忘清单)
  • 端口转发、隧道与Pivoting技术详解及区别解析
  • 数据类型及sizeof,进制转换
  • 蓝桥杯 排序题目【算法赛】
  • Unity光线追踪移动端降级适配技术指南
  • Mybatis 框架学习
  • C# Type类中Name、FullName、Namespace、AssemblyQualifiedName的区别
  • 了解一下HTTP的短连接和长连接
  • 从波士顿动力到Figure AI:探寻人工智能驱动的机器人智能化
  • UdpClient
  • 什么是 MyBatis?
  • 基于卡尔曼滤波的雷达光电多目标航迹融合算法matlab仿真
  • 基于ssm的宠物医院信息管理系统(全套)
  • 线程池参数调优
  • 游戏引擎学习第152天
  • AI时代研究卷积神经网络(CNN)工具与方法
  • 【即插即用涨点模块】CAA上下文锚点注意力机制:有效捕捉全局信息,助力高效涨点【附源码+注释】