9afce6f6创建于 2025年5月7日历史提交
/*
 * Copyright (c) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { image } from '@kit.ImageKit';
import { windowSizeManager } from "./Managers";
import { OffsetModel } from '../model/OffsetModel';
import { runWithAnimation } from './FuncUtils';

/**
 * 图片适配类型
 */
export enum ImageFitType {
  TYPE_WIDTH = 'width',
  TYPE_HEIGHT = 'height',
  TYPE_DEFAULT = 'default'
}


/**
 * 计算图片的偏移结构
 */
export interface ConstrainOffsetAndAnimationType {
  // 此次计算的方向 宽或者高
  dimensionWH: ImageFitType,

  // 此次图片的默认尺寸
  imageDefaultSize: image.Size,

  // 此次图片相对应的偏移信息
  imageOffsetInfo: OffsetModel,

  // 此次图片的放大尺寸
  scaleValue: number,

  // 当前图片的旋转角度
  rotate: number,

  /**
   * 当超出限制多少时去判断
   * 取值:0 ~ 1
   * 当滑动在视图中,超过窗口多少距离时候才去触发切换返回值
   * 默认值:0.2
   */
  TogglePercent: number
  //  List 偏移量
  imageListOffset: number,

  // 主轴方向
  listDirection: Axis
}


// 获取最大偏移量
export function getMaxAllowedOffset(winSize: number, imageSize: number, scale: number) {
  const maxNum = Math.max(imageSize * scale, winSize);
  const minNum = Math.min(imageSize * scale, winSize);
  return (maxNum - minNum) / 2;
}

/**
 * 约束图片偏移,确保不超出视口边界
 * @param offset   -- 当前偏移量
 * @param winSize -- 视口大小
 * @param imageSize -- 图片大小
 * @param scale    -- 缩放
 */
function constrainOffset(offset: number, winSize: number, imageSize: number, scale: number): number {
  let maxAllowedOffset = getMaxAllowedOffset(winSize, imageSize, scale);
  return Math.min(Math.max(offset, -maxAllowedOffset), maxAllowedOffset);
}


/**
 * 查询图片是否需要切换图片
 * @param offset   -- 当前偏移量
 * @param winSize -- 视口大小
 * @param imageSize -- 图片大小
 * @param scale    -- 缩放
 * @param TogglePercent    -- 移动视口多少时候需要切换图片
 */
function isToggle(offset: number, winSize: number, imageSize: number, scale: number, TogglePercent: number): boolean {
  let maxAllowedOffset = getMaxAllowedOffset(winSize, imageSize, scale);
  const deviation = Math.abs(offset) - maxAllowedOffset;
  const switchThreshold = winSize * TogglePercent;

  if (deviation > switchThreshold) {
    return true
  }
  return false
}

// 查询当前是尺寸
export function getImgSize(imageSize: image.Size, rotate: number,
  dimensionWH: ImageFitType.TYPE_WIDTH | ImageFitType.TYPE_HEIGHT): number {
  const isStandardRotation = [90, 270].includes(Math.abs(rotate % 360));
  const Key =
    isStandardRotation ? (dimensionWH == ImageFitType.TYPE_WIDTH ? ImageFitType.TYPE_HEIGHT : ImageFitType.TYPE_WIDTH) :
      dimensionWH;
  // return px2vp(imageSize[Key])
  return imageSize[Key]
}


/**
 * 用来计算 图片所需要的偏移量
 * 防止 图片超出显示视图
 * @param { ConstrainOffsetAndAnimationType } info  -  所需要的数据
 * @return { [boolean, boolean] }  - 返回是否移动
 *    如果是 info.dimensionWH 是 "width" 返回值为 [ 左边超出, 右边超出]
 *    如果是 info.dimensionWH 是 "height" 返回值为 [ 上边超出, 下边超出]
 */
export function constrainOffsetAndAnimation(info: ConstrainOffsetAndAnimationType): [boolean, boolean] {
  if (info.dimensionWH === ImageFitType.TYPE_DEFAULT) {
    return [false, false]
  }
  const WIN_SIZE = windowSizeManager.get();
  // 获取图片在指定轴上的原始尺寸
  // const IMG_SIZE = info.dimensionWH === 'width' ? px2vp(IMG_DOM.size.width) : px2vp(IMG_DOM.size.height);
  const IMG_SIZE = getImgSize(info.imageDefaultSize, info.rotate, info.dimensionWH);
  // 获取窗口对应轴的尺寸
  const WIN_AXIS_SIZE = WIN_SIZE[info.dimensionWH];
  // 获取图片在该轴上的最后偏移量
  let currentOffset: number =
    (info.imageOffsetInfo as object)[`last${info.dimensionWH === ImageFitType.TYPE_WIDTH ? 'X' : 'Y'}`];
  // 计算最后的图片偏移量
  // 主轴方向是Horizontal时,计算X偏移量时需要加上list偏移量
  // 主轴方向是Vertical时,计算Y偏移量时需要加上list偏移量
  if (info.dimensionWH ===
    (info.listDirection === Axis.Horizontal ? ImageFitType.TYPE_WIDTH : ImageFitType.TYPE_HEIGHT)) {
    currentOffset += info.imageListOffset;
  }
  const CLAMPED_OFFSET = constrainOffset(currentOffset, WIN_AXIS_SIZE, IMG_SIZE, info.scaleValue);
  // 如果偏移量发生了变化(即需要修正)
  if (CLAMPED_OFFSET !== currentOffset) {
    let updateFn =
      () => {
        (info.imageOffsetInfo as object)[`current${info.dimensionWH == ImageFitType.TYPE_WIDTH ? 'X' : 'Y'}`] =
          CLAMPED_OFFSET;
        // 保存当前的偏移状态
        info.imageOffsetInfo.stash();
      }
    runWithAnimation(updateFn);
    let bol = isToggle(currentOffset, WIN_AXIS_SIZE, IMG_SIZE, info.scaleValue, info.TogglePercent);
    if (bol) {
      const BOL = currentOffset >= 0 ? true : false
      return [BOL, !BOL]
    }

  }
  return [false, false]
}