鸿蒙Next图片预览优化增加长按弹框事件 原创 精华

auhgnixgnahz
发布于 2025-8-8 18:49
浏览
0收藏

基于上篇一镜到底实现,做出一些优化,增加长按事件弹出操作框。
演示:
鸿蒙Next图片预览优化增加长按弹框事件-鸿蒙开发者社区
上篇不足:
1.图片初始位置相对屏幕Y轴的偏移没有赋值,影响图片放大后拖动的上下边界,导致上边会有黑边,下边有一部分显示不全
2.图片缩放没有限制,图片可以放大很大
3.手势识别区域为图片初始大小,放大之后其他区域没有响应
优化:
1.图片绘制完成之后,获取图片左上角相对页面左上角的位置

.onAreaChange((oldValue: Area, newValue: Area) => {
  this.imagePositionY = vp2px(Number(newValue.globalPosition.y))
})

2.通过图片原始像素和屏幕像素的对比,设置图片的最大放大比例,再捏合手势里限制图片放大倍数

// 计算图片最大缩放比例(基于窗口和图片尺寸)
calcFitScaleRatio(imageWidth: number,imageHeight:number): number {
  let ratio: number = 1.0;
  if (this.componentWidth > imageWidth) {
    ratio = this.componentWidth / imageWidth;
  } else {
    ratio = this.componentHeight / imageHeight;
  }
  return ratio + 2;  // 如果嫌小可以自定义增大倍数
}

3.Image的背景Stack改为充满屏幕,这样手势可以全屏识别,更具图片的高度和屏幕高度计算图片Y轴偏移

屏幕高度-图片高度-导航栏-状态栏
 this.geometryPositionY = (this.componentHeight-this.imageHeight-this.systemBarHeight)/2

4.图片放大之后禁用Swiper滑动
5.增加图片长按弹框,可以添加图片相关操作事件,给Stack绑定全屏模态页面

@Builder
  sheetBuilder(){
    Stack({alignContent:Alignment.Bottom}){
      Column(){
        Row({space:10}){
          Column({space:5}){
            Image($r('app.media.icon_pic_save')).objectFit(ImageFit.Contain).padding(5).width(40).height(40).backgroundColor(Color.White).borderRadius(5)
            Text('保存图片').fontSize(12)
          }.onClick(()=>{
            promptAction.showToast({
              message: '保存图片成功',
              duration: 1000,
              alignment:Alignment.Center
            });
          })
        }
        .width('100%')
        .justifyContent(FlexAlign.Start)
        .alignItems(VerticalAlign.Center)
        .padding({top:30,bottom:20,left:10,right:10})
        Divider().color(Color.Gray)
        Text('取消').textAlign(TextAlign.Center).fontColor(Color.Blue).margin({top:20}).width('100%').height(60)
          .onClick(()=>{
            this.isShowSheet=false
          })
      }
      .borderRadius({
        topLeft:10,
        topRight:10
      })
      .width('100%')
      .backgroundColor('#F1F1F1')
      .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#66000000')
  }

源码:

import { display, matrix4, promptAction } from "@kit.ArkUI";
enum Status {
  IDLE,          // 初始状态(无手势)
  PINCHING,      // 双指捏合缩放中
  PAN_ONLY,      // 单指拖动中
  READY_TO_BACK  // 准备返回上一页
}

@ComponentV2
export struct PhotoPreviewItem {
  @Consumer()   isDisableSwipe:boolean=true
  // 屏幕尺寸(vp)
  public componentHeight: number = display.getDefaultDisplaySync().height;
  public componentWidth: number = display.getDefaultDisplaySync().width;
  @Local status: Status = Status.IDLE; // 当前手势状态
  @Local gestureCount: number = 0; // 手势并发计数(用于判断所有手势是否结束)
  @BuilderParam imageUrl: string; // 图片资源路径
  @BuilderParam systemBarHeight: number; // 导航栏和状态栏高度
  @Local imageScale: number = 1; // 图片缩放比例(双指捏合控制)
  public maxScale: number = 1;
  @Local imagePullingDownScale: number = 1; // 下拉拖动时的缩放比例(影响整体大小)
  @Local backOpacity: number = 1; // 黑色背景和下拉比例有关
  @Local imageTranslateX: number = 0; // X轴偏移量(px,拖动控制)
  @Local imageTranslateY: number = 0; // Y轴偏移量(px,拖动控制)
  @Local geometryPositionX: number = 0; // 几何过渡X坐标
  @Local geometryPositionY: number = 0; // 几何过渡Y坐标
  @Local centerX: number = 0; // 缩放中心X(相对图片的比例)
  @Local centerY: number = 0; // 缩放中心Y(相对图片的比例)
  @Local imageWidth: number = display.getDefaultDisplaySync().width; // 图片原始宽度(px)
  @Local imageHeight: number = display.getDefaultDisplaySync().height; // 图片原始高度(px)
  @Local panDirection: PanDirection = PanDirection.Vertical; // 拖动手势允许的方向
  @Local gestureDisabled: boolean = false; // 是否禁用所有手势

  // 图片拖动边界限制(vp)
  @Local maxOffsetX: number = 0; // X轴最大偏移(右边界)
  @Local minOffsetX: number = 0; // X轴最小偏移(左边界)
  @Local maxOffsetY: number = 0; // Y轴最大偏移(下边界)
  @Local minOffsetY: number = 0; // Y轴最小偏移(上边界)

  // 实际显示区域边界(px,由图片和屏幕共同决定)
  private realDisplayBoundsLeft: number = 0;
  private realDisplayBoundsTop: number = 0;
  private realDisplayBoundsRight: number = 0;
  private realDisplayBoundsBottom: number = 0;

  // 手势起始状态记录(用于计算增量)
  private startGestureOffsetX: number = 0; // 起始X偏移(vp)
  private startGestureOffsetY: number = 0; // 起始Y偏移(vp)
  private startGestureScale: number = 1; // 起始缩放比例

  // 图片原始位置(未变换时的坐标,px)
  private imagePositionX: number = 0;
  private imagePositionY: number = 0;

  // 屏幕边界(px)
  private displayLeft: number = 0;
  private displayTop: number = 0;
  private displayRight: number = display.getDefaultDisplaySync().width;
  private displayBottom: number = display.getDefaultDisplaySync().height;

  // 上一次缩放中心(用于计算偏移补偿)
  private lastCenterX: number = 0;
  private lastCenterY: number = 0;
  private geometryScale: number = 1; // 几何过渡缩放比例
  private firstStarted = true; // 是否首次加载
  @BuilderParam backToFirstPage: () => void; // 返回上一页的回调
  @Local isShowSheet: boolean = false

  // 监听手势计数变化,所有手势结束后触发
  @Monitor('gestureCount')
  onAllGestureFinish(): void {
    if (this.firstStarted) {
      this.firstStarted = false;
      return;
    }
    if (this.gestureCount !== 0) return;

    // 空闲状态:修正图片位置(避免超出边界)
    if (this.status === Status.IDLE) {
      this.disableGesture();
      this.getUIContext().animateTo({
        duration: 300,
        onFinish: () => {
          this.updatePanDirection();
          this.resumeGesture();
        }
      }, () => {
        this.imagePullingDownScale = 1;
        this.backOpacity = this.imagePullingDownScale
        let leftTop = this.calculateLeftTopPoint();
        let leftTopX = leftTop[0];
        let leftTopY = leftTop[1];

        let rightBottom = this.calculateRightBottomPoint();
        let rightBottomX = rightBottom[0];
        let rightBottomY = rightBottom[1];

        const imageWidth = this.imageWidth * this.imageScale;
        const imageHeight = this.imageHeight * this.imageScale;

        // X轴居中或边界修正
        if (imageWidth < this.displayRight - this.displayLeft) {
          this.centerX = 0;
          this.imageTranslateX = 0;
        } else if (leftTopX > this.displayLeft) {
          this.imageTranslateX += (this.displayLeft - leftTopX);
        } else if (rightBottomX < this.displayRight) {
          this.imageTranslateX += (this.displayRight - rightBottomX);
        }

        // Y轴居中或边界修正
        if (imageHeight < this.displayBottom - this.displayTop) {
          this.centerY = 0;
          this.imageTranslateY = 0;
        } else if (leftTopY > this.displayTop) {
          this.imageTranslateY += (this.displayTop - leftTopY);
        } else if (rightBottomY < this.displayBottom) {
          this.imageTranslateY += (this.displayBottom - rightBottomY);
        }
      });
    }

    // 准备返回状态:执行过渡动画后返回上一页
    if (this.status === Status.READY_TO_BACK) {
      this.status = Status.IDLE;
      this.disableGesture();
      this.getUIContext().animateTo({
        duration: 100,
        onFinish: () => {
          this.resumeGesture();
          this.updatePanDirection();
          this.backToFirstPage();
        }
      }, () => {
        let leftTop = this.calculateLeftTopPoint();
        let leftTopX = leftTop[0];
        let leftTopY = leftTop[1];
        this.geometryPositionX = leftTopX - this.imagePositionX;
        this.geometryPositionY = leftTopY - this.imagePositionY+(this.componentHeight-this.imageHeight-this.systemBarHeight)/2;
        this.geometryScale = this.imageScale * this.imagePullingDownScale;
        this.resetTransform();
      });
    }
  }

  /**
   * 根据缩放中心变化调整偏移量,确保缩放后图片位置不变
   * (解决缩放中心改变时图片"跳变"的问题)
   */
  updateTranslateAccordingToCenter() {
    if (this.lastCenterX === this.centerX && this.lastCenterY === this.centerY) {
      return; // 中心未变化,无需调整
    }

    // 计算中心变化前后的矩阵变换差异
    const matrixOld = matrix4.identity()
      .scale({
        x: this.imageScale * this.imagePullingDownScale,
        y: this.imageScale * this.imagePullingDownScale,
        centerX: this.lastCenterX,
        centerY: this.lastCenterY
      })
      .translate({ x: this.imageTranslateX, y: this.imageTranslateY });

    const matrixNew = matrix4.identity()
      .scale({
        x: this.imageScale * this.imagePullingDownScale,
        y: this.imageScale * this.imagePullingDownScale,
        centerX: this.centerX,
        centerY: this.centerY
      })
      .translate({ x: this.imageTranslateX, y: this.imageTranslateY });

    // 计算图片左上角点在新旧矩阵下的位置差异,补偿偏移量
    let leftTopOld = matrixOld.transformPoint([-this.imageWidth / 2, -this.imageHeight / 2]);
    let leftTopNew = matrixNew.transformPoint([-this.imageWidth / 2, -this.imageHeight / 2]);
    this.imageTranslateX += (leftTopOld[0] - leftTopNew[0]);
    this.imageTranslateY += (leftTopOld[1] - leftTopNew[1]);
    console.log('缩放过程中x,y偏移:  X轴偏移量'+this.imageTranslateX+' Y轴偏移量'+this.imageTranslateY)
  }

  /**
   * 更新图片可拖动的边界范围
   * (根据当前缩放比例和屏幕大小计算,避免拖动时出现过大空白)
   */
  updateExtremeOffset() {
    const totalScale = this.imageScale * this.imagePullingDownScale;
    const matrix = matrix4.identity()
      .scale({
        x: totalScale,
        y: totalScale,
        centerX: this.centerX,
        centerY: this.centerY
      });

    // 计算图片左上角和右下角在变换后的坐标(px)
    let leftTop = matrix.transformPoint([-this.imageWidth / 2, -this.imageHeight / 2]);
    let leftTopX = leftTop[0];
    let leftTopY = leftTop[1];

    const leftTopPointX = leftTopX + this.imageWidth / 2 + this.imagePositionX;
    const leftTopPointY = leftTopY + this.imageHeight / 2 + this.imagePositionY;
    const rightBottomPointX = leftTopPointX + this.imageWidth * totalScale;
    const rightBottomPointY = leftTopPointY + this.imageHeight * totalScale;

    // 计算实际显示边界(取图片和屏幕的交集)
    this.realDisplayBoundsLeft = Math.max(this.displayLeft, leftTopPointX);
    this.realDisplayBoundsRight = Math.min(this.displayRight, rightBottomPointX);
    this.realDisplayBoundsTop= Math.max(this.displayTop, leftTopPointY);
    this.realDisplayBoundsBottom  = Math.min(this.displayBottom, rightBottomPointY);

    // 转换为可拖动的偏移边界(vp)
    this.minOffsetX = px2vp(this.realDisplayBoundsRight - rightBottomPointX);
    this.maxOffsetX = px2vp(this.realDisplayBoundsLeft - leftTopPointX);
    this.minOffsetY = px2vp(this.realDisplayBoundsBottom - rightBottomPointY);
    this.maxOffsetY = px2vp(this.realDisplayBoundsTop - leftTopPointY);
    console.log('边界:maxOffsetY: '+this.maxOffsetY+'  minOffsetY: '+this.minOffsetY)
  }

  // 重置拖动方向(根据图片是否超出屏幕边界动态调整)
  updatePanDirection(): void {
    this.panDirection = PanDirection.Vertical;
    let leftTop = this.calculateLeftTopPoint();
    let leftTopX = leftTop[0];

    let rightBottom = this.calculateRightBottomPoint();
    let rightBottomX = rightBottom[0];

    // 图片左边界超出屏幕左边缘:允许向右拖动
    if (leftTopX < this.displayLeft - 1) {
      this.panDirection |= PanDirection.Right;
    }
    // 图片右边界超出屏幕右边缘:允许向左拖动
    if (rightBottomX > this.displayRight + 1) {
      this.panDirection |= PanDirection.Left;
    }
  }

  // 重置图片变换状态(位置、缩放均恢复初始值)
  resetTransform(): void {
    this.imageTranslateX = 0;
    this.imageTranslateY = 0;
    this.imageScale = 1;
    this.imagePullingDownScale = 1;
    this.centerX = 0;
    this.centerY = 0;
    this.updateExtremeOffset();
  }

  /**
   * 更新缩放中心(基于手势中心点)
   * @param gestureCenterPoint 手势中心点坐标(px)
   */
  updateCenter(gestureCenterPoint: [number, number]) {
    this.lastCenterX = this.centerX;
    this.lastCenterY = this.centerY;

    // 计算当前矩阵变换下的图片左上角坐标
    const matrix = matrix4.identity()
      .scale({
        x: this.imageScale * this.imagePullingDownScale,
        y: this.imageScale * this.imagePullingDownScale,
        centerX: this.lastCenterX,
        centerY: this.lastCenterY
      })
      .translate({ x: this.imageTranslateX, y: this.imageTranslateY });

    let leftTop = matrix.transformPoint([-this.imageWidth / 2, -this.imageHeight / 2]);
    let leftTopX = leftTop[0];
    let leftTopY = leftTop[1];

    const leftTopPointX = leftTopX + this.imageWidth / 2; // 图片左上角X(px)
    const leftTopPointY = leftTopY + this.imageHeight / 2; // 图片左上角Y(px)

    // 计算手势中心相对图片的比例(作为新的缩放中心)
    this.centerX = (gestureCenterPoint[0] - leftTopPointX) / this.imageScale - this.imageWidth / 2;
    this.centerY = (gestureCenterPoint[1] - leftTopPointY - this.imagePositionY) / this.imageScale - this.imageHeight / 2 ;
    console.log('缩放中心: centerX:'+this.centerX+' centerY:'+this.centerY)
  }
  // 计算变换后图片左上角坐标(px)
  calculateLeftTopPoint(): [number, number] {
    const matrix = matrix4.identity()
      .scale({
        x: this.imageScale * this.imagePullingDownScale,
        y: this.imageScale * this.imagePullingDownScale,
        centerX: this.centerX,
        centerY: this.centerY
      })
      .translate({ x: this.imageTranslateX, y: this.imageTranslateY });

    let leftTop = matrix.transformPoint([-this.imageWidth / 2, -this.imageHeight / 2]);
    return [
      leftTop[0] + this.imageWidth / 2 + this.imagePositionX,
      leftTop[1] + this.imageHeight / 2 + this.imagePositionY
    ];
  }

  // 计算变换后图片右下角坐标(px)
  calculateRightBottomPoint(): [number, number] {
    const matrix = matrix4.identity()
      .scale({
        x: this.imageScale * this.imagePullingDownScale,
        y: this.imageScale * this.imagePullingDownScale,
        centerX: this.centerX,
        centerY: this.centerY
      })
      .translate({ x: this.imageTranslateX, y: this.imageTranslateY });

    let rightBottom = matrix.transformPoint([this.imageWidth / 2, this.imageHeight / 2]);
    return [
      rightBottom[0] + this.imageWidth / 2 + this.imagePositionX,
      rightBottom[1] + this.imageHeight / 2 + this.imagePositionY
    ];
  }

  // 禁用所有手势
  disableGesture(): void {
    this.gestureDisabled = true;
  }

  // 恢复所有手势
  resumeGesture(): void {
    this.gestureDisabled = false;
  }

  // 判断是否禁用拖动手势
  cannotPan(): boolean {
    return this.gestureDisabled;
  }

  // 判断是否禁用捏合手势
  cannotPinch(): boolean {
    return this.status === Status.PAN_ONLY || this.gestureDisabled;
  }

  // 组件初始化时执行
  aboutToAppear(): void {
    this.updateExtremeOffset(); // 初始化边界
    this.updatePanDirection(); // 初始化拖动方向
  }

  @Builder
  sheetBuilder(){
    Stack({alignContent:Alignment.Bottom}){
      Column(){
        Row({space:10}){
          Column({space:5}){
            Image($r('app.media.icon_pic_save')).objectFit(ImageFit.Contain).padding(5).width(40).height(40).backgroundColor(Color.White).borderRadius(5)
            Text('保存图片').fontSize(12)
          }.onClick(()=>{
            promptAction.showToast({
              message: '保存图片成功',
              duration: 1000,
              alignment:Alignment.Center
            });
          })
        }
        .width('100%')
        .justifyContent(FlexAlign.Start)
        .alignItems(VerticalAlign.Center)
        .padding({top:30,bottom:20,left:10,right:10})
        Divider().color(Color.Gray)
        Text('取消').textAlign(TextAlign.Center).fontColor(Color.Blue).margin({top:20}).width('100%').height(60)
          .onClick(()=>{
            this.isShowSheet=false
          })
      }
      .borderRadius({
        topLeft:10,
        topRight:10
      })
      .width('100%')
      .backgroundColor('#F1F1F1')
      .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#66000000')
  }

  build() {
    Stack() {
      // 背景层(随下拉透明度变化)
      Column()
        .width('100%')
        .height('100%')
        .backgroundColor(Color.Black)
        .transition(TransitionEffect.OPACITY)
        .opacity(this.backOpacity)

      // 图片层(核心内容)
      Stack() {
        Image($r(this.imageUrl))
          .width(px2vp(this.imageWidth) * this.geometryScale)
          .height(px2vp(this.imageHeight) * this.geometryScale)
          .onComplete((event) => {
            // 图片加载完成后修正宽高比例
            const imageWidth = event?.width ?? 0;
            const imageHeight = event?.height ?? 0;
            this.maxScale = this.calcFitScaleRatio(imageWidth,imageHeight)
            this.imageHeight = imageHeight * this.imageWidth / imageWidth;
            this.geometryPositionY = (this.componentHeight-this.imageHeight-this.systemBarHeight)/2
          })
          .transform(matrix4.identity()
            .scale({
              x: this.imageScale * this.imagePullingDownScale,
              y: this.imageScale * this.imagePullingDownScale,
              centerX: this.centerX,
              centerY: this.centerY
            })
            .translate({ x: this.imageTranslateX, y: this.imageTranslateY })
          )
          .id(this.imageUrl)
          .geometryTransition(this.imageUrl)
          .transition(TransitionEffect.OPACITY)
          .position({
            x: px2vp(this.geometryPositionX),
            y: px2vp(this.geometryPositionY)
          })
          .onAreaChange((oldValue: Area, newValue: Area) => {
            this.imagePositionY = vp2px(Number(newValue.globalPosition.y))
          })
          .draggable(false)
      }
      .width('100%')
      .height('100%')
      .parallelGesture(
        GestureGroup(GestureMode.Exclusive,
          // 双指捏合缩放手势(核心优化部分)
          PinchGesture({ fingers: 2, distance: 1 })
            .onActionStart((event: GestureEvent) => {
              // 1. 标记状态为"捏合中",禁用拖动冲突
              this.status = Status.PINCHING;
              // 2. 计算手势中心点(转换为px单位,与图片坐标统一)
              let gestureCenter: [number, number] = [vp2px(event.pinchCenterX), vp2px(event.pinchCenterY)];
              this.updateCenter(gestureCenter);
              // 3. 补偿偏移量,避免中心变化导致图片跳变
              this.updateTranslateAccordingToCenter();
              // 4. 记录起始缩放比例(用于计算增量)
              this.startGestureScale = this.imageScale;
              // 5. 增加手势计数(标记手势开始)
              this.gestureCount++;
            })
            .onActionUpdate((event: GestureEvent) => {
              // 1. 计算当前缩放比例(基于起始比例和手势缩放增量)
              let newScale = this.startGestureScale * event.scale;
              // 2. 限制缩放范围(避免过度缩放)
              newScale = Math.max(0.5, Math.min(newScale, this.maxScale));
              this.imageScale = newScale;
              // 3. 实时更新边界,确保拖动限制有效
              this.updateExtremeOffset();
            })
            .onActionEnd(() => {
              // 1. 恢复状态为"空闲"
              this.status = Status.IDLE;
              // 2. 缩放小于1时,动画恢复到1倍(优化体验)
              if (this.imageScale < 1) {
                this.getUIContext().animateTo({ duration: 250, curve: Curve.EaseOut }, () => {
                  this.imageScale = 1;
                });
              }
              if (this.imageScale>1) {
                this.isDisableSwipe = true
              }else {
                this.isDisableSwipe = false
              }
              // 3. 减少手势计数(标记手势结束)
              this.gestureCount--;
            }),

          // 单指拖动手势
          PanGesture({ direction: this.panDirection })
            .onActionStart((event: GestureEvent) => {
              // 初始方向为垂直,识别后改为全方向
              this.panDirection = PanDirection.All;

              let leftTop = this.calculateLeftTopPoint();
              let leftTopY = leftTop[1];

              // 图片未超出屏幕且非捏合状态:标记为"拖动中"
              if (leftTopY >= this.displayTop - 1 && this.status !== Status.PINCHING) {
                this.status = Status.PAN_ONLY;
              }

              // 非捏合状态下,更新拖动中心
              if (this.status !== Status.PINCHING) {
                const finger = event.fingerList[0];
                const currentX = finger?.globalX ?? event.offsetX;
                const currentY = finger?.globalY ?? event.offsetY;
                let centerPoint : [number, number]= [vp2px(currentX), vp2px(currentY )];
                this.updateCenter(centerPoint);
                this.updateTranslateAccordingToCenter();
                this.updateExtremeOffset();
              }

              // 记录起始偏移量
              this.startGestureOffsetX = px2vp(this.imageTranslateX);
              this.startGestureOffsetY = px2vp(this.imageTranslateY);
              this.gestureCount++;
            })
            .onActionUpdate((event: GestureEvent) => {
              const offsetX = event.offsetX;
              const offsetY = event.offsetY;

              // 拖动状态:直接更新偏移(带动画效果)
              if (this.status === Status.PAN_ONLY) {
                this.imageTranslateX = vp2px(this.startGestureOffsetX + offsetX);
                this.imageTranslateY = vp2px(this.startGestureOffsetY + offsetY);
                // 下拉时降低背景透明度
                this.imagePullingDownScale = 1 - Math.abs(event.offsetY) / px2vp(this.displayBottom);
                this.backOpacity = this.imagePullingDownScale
                console.log('拖动时偏移: imageTranslateX:'+this.imageTranslateX+' imageTranslateY:'+this.imageTranslateY)
              }

              // 非拖动状态(缩放后拖动):带边界限制
              if (this.status !== Status.PAN_ONLY) {
                // 边界弹性限制(超出边界时阻力增大)
                let limitedOffsetX = offsetX;
                let limitedOffsetY = offsetY;

                // X轴边界限制
                if (offsetX + this.startGestureOffsetX > this.maxOffsetX) {
                  const distance = offsetX + this.startGestureOffsetX - this.maxOffsetX;
                  limitedOffsetX = this.maxOffsetX + distance * (1 - Math.exp(-distance / 300)) - this.startGestureOffsetX;
                } else if (offsetX + this.startGestureOffsetX < this.minOffsetX) {
                  const distance = this.minOffsetX - (offsetX + this.startGestureOffsetX);
                  limitedOffsetX = this.minOffsetX - distance * (1 - Math.exp(-distance / 300)) - this.startGestureOffsetX;
                }

                // Y轴边界限制
                if (offsetY + this.startGestureOffsetY > this.maxOffsetY) {
                  const distance = offsetY + this.startGestureOffsetY - this.maxOffsetY;
                  limitedOffsetY = this.maxOffsetY + distance * (1 - Math.exp(-distance / 300)) - this.startGestureOffsetY;
                } else if (offsetY + this.startGestureOffsetY < this.minOffsetY) {
                  const distance = this.minOffsetY - (offsetY + this.startGestureOffsetY);
                  limitedOffsetY = this.minOffsetY - distance * (1 - Math.exp(-distance / 300)) - this.startGestureOffsetY;
                }
                // console.log('边界: limitedOffsetX: '+limitedOffsetX+'limitedOffsetY: '+limitedOffsetY)
                // 应用限制后的偏移
                this.imageTranslateX = vp2px(this.startGestureOffsetX + limitedOffsetX);
                this.imageTranslateY = vp2px(this.startGestureOffsetY + limitedOffsetY);
                console.log('拖动时偏移: imageTranslateX:'+this.imageTranslateX+' imageTranslateY:'+this.imageTranslateY)
              }
            })
            .onActionEnd((event: GestureEvent) => {
              // 拖动结束:判断是否返回上一页
              if (this.status === Status.PAN_ONLY) {
                this.status = event.offsetY > 100 ? Status.READY_TO_BACK : Status.IDLE;
              }
              this.gestureCount--;
            }),

          // 双击手势(放大/还原)
          TapGesture({ count: 2 })
            .onAction(() => {
              this.imageScale = this.imageScale > 1 ? 1 : 2; // 双击切换1倍/2倍
              if (this.imageScale==1) {
                this.isDisableSwipe = false
              }else {
                this.isDisableSwipe = true
                this.panDirection = PanDirection.All;
              }
              this.onAllGestureFinish();
            }),

          // 单击手势(未缩放时返回)
          TapGesture({ count: 1 })
            .onAction(() => {
              if (this.imageScale === 1) {
                this.backToFirstPage();
              }
            }),

          //长按手势
          LongPressGesture()
            .onAction(()=>{
              if (this.status === Status.IDLE) {
                this.isShowSheet = true
              }
            })
        )
      )
      .onGestureJudgeBegin((gestureInfo: GestureInfo, event: BaseGestureEvent) => {
        // 手势冲突判断:优先处理当前状态允许的手势
        if (gestureInfo.type === GestureControl.GestureType.PAN_GESTURE) {
          return this.cannotPan() ? GestureJudgeResult.REJECT : GestureJudgeResult.CONTINUE;
        }
        if (gestureInfo.type === GestureControl.GestureType.PINCH_GESTURE) {
          return this.cannotPinch() ? GestureJudgeResult.REJECT : GestureJudgeResult.CONTINUE;
        }
        return GestureJudgeResult.CONTINUE;
      })
      .bindContentCover(this.isShowSheet,this.sheetBuilder())
    }
    .width('100%')
    .height('100%')
  }
  // 计算图片最大缩放比例(基于窗口和图片尺寸)
  calcFitScaleRatio(imageWidth: number,imageHeight:number): number {
    let ratio: number = 1.0;
    if (this.componentWidth > imageWidth) {
      ratio = this.componentWidth / imageWidth;
    } else {
      ratio = this.componentHeight / imageHeight;
    }
    return ratio + 2;
  }

}

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
分类
标签
1
收藏
回复
举报
回复
    相关推荐