鸿蒙Next图片预览缩放拖动一镜到底实现 原创 精华

auhgnixgnahz
发布于 2025-8-4 08:36
浏览
0收藏

实现目标:

一组图片点击其中一张可以跳转到全屏预览,并且使用一镜到底转场动画,预览界面可以左右滑动查看其他图片,单张图片可以放大查看,向下拖动退出预览。
鸿蒙Next图片预览缩放拖动一镜到底实现-鸿蒙开发者社区

实现原理:

1.一镜到底
设置geometryTransition属性将图片首页和大图页面的图片绑定同一id值,结合属性动画效果实现一镜到底效果
组件内转场主要通过transition属性配置转场参数,在组件插入和删除时显示过渡动效。通过设置opacity,设置组件转场时的透明度效果,为插入时起点和删除时终点的值。
2.拖动手势PanGesture
滑动达到最小滑动距离(默认值为5vp)时拖动手势识别成功,三个可选参数:
fingers:用于声明触发拖动手势所需要的最少手指数量,最小值为1,最大值为10,默认值为1。
direction:用于声明触发拖动的手势方向,此枚举值支持逻辑与(&)和逻辑或(|)运算。默认值为Pandirection.All。
distance:用于声明触发拖动的最小拖动识别距离,单位为vp,默认值为5。
三个可选事件:
onActionStart:Pan手势识别成功回调
onActionUpdate:Pan手势移动过程中回调,fingerList为多根手指时,该回调监听每次只会更新一根手指的位置信息
onActionEnd:Pan手势识别成功,手指抬起后触发回调
3.捏合手势PinchGesture
触发捏合手势的最少手指为2指,最大为5指,最小识别距离为5vp。两个可选参数:
fingers:用于声明触发捏合手势所需要的最少手指数量,最小值为2,最大值为5,默认值为2。
distance:用于声明触发捏合手势的最小距离,单位为vp,默认值为5
可选事件同上。

实现过程:

不想看过程的同学可以直接看最后的全部源码,辛苦总结,免费取用,麻烦转发分享点赞,感谢
1.实现简单的一镜到底转场
鸿蒙Next图片预览缩放拖动一镜到底实现-鸿蒙开发者社区

/// 图片九宫格页面
export const imageList: (string | Resource)[] = [
  $r('app.media.img1'),
  $r('app.media.img2'),
  $r('app.media.img3'),
  $r('app.media.img4'),
  $r('app.media.img5'),
  $r('app.media.img6'),
  $r('app.media.img7'),
  $r('app.media.img8'),
  $r('app.media.img9'),
];

@Builder
export function ImagePreviewTestBuilder() {
  ImagePreviewTest()
}

@ComponentV2
struct ImagePreviewTest{
  pathStack: NavPathStack = new NavPathStack()
  onItemClick(index: number): void {
    let param: Record<string, Object> = {};
    param['selectedIndex'] = index;
    this.getUIContext().animateTo({
      duration: 250,
      curve: Curve.EaseIn,
    }, () => {
      this.pathStack.pushPath({
        name: 'ImagePreviewPage', param: param
      },false)
    })
  }
  build() {
    NavDestination(){
      Grid() {
        ForEach(imageList, (item: string | Resource, index: number) => {
          GridItem() {
            Image(item)
              .width('100%')
              .height('100%')
              .objectFit(ImageFit.Cover)
              .id('item' + index)
              .borderRadius(5)
              .onClick(() => {
                this.onItemClick(index);
              })
              .geometryTransition('app.media.img' + (index+1))
              .transition(TransitionEffect.opacity(0.99))
          }
          .width(80)
          .height(80)
        })
      }
      .columnsTemplate('1fr 1fr 1fr')
      .rowsTemplate('1fr 1fr 1fr')
      .width('100%')
      .height('auto')
    }
    .hideTitleBar(true)
    .onReady((context: NavDestinationContext) => {
      this.pathStack = context.pathStack;
    })
  }
}

/// 图片预览详情页
import { imageList } from "../view/ImagePreviewTest";
import { display } from "@kit.ArkUI"
@Builder
export function ImagePreviewPageBuilder() {
  ImagePreviewPage()
}
@ComponentV2
struct ImagePreviewPage{
  private pathStack: NavPathStack = new NavPathStack();
  @Local selectedIndex: number = 0;
  @Local imageWidth: number = display.getDefaultDisplaySync().width;
  @Local imageHeight: number = display.getDefaultDisplaySync().width;

  private backToFirstPage: () => void = () => {
    this.getUIContext().animateTo({
      duration: 300,
      curve: Curve.EaseIn,
    }, () => {
      this.pathStack.pop(false);
    })
  }
  build() {
    NavDestination(){
      Swiper() {
        ForEach(imageList,  (item: string | Resource, index: number) => {
         Stack(){
           Column()
             .width('100%')
             .height('100%')
             .backgroundColor(Color.Black)
             .transition(TransitionEffect.OPACITY)
           Image(item)
             .width("100%")
             .height("100%")
             .onComplete((event)=>{
               let imageWidth = event?.width ??0
               let imageHeight = event?.height ??0
               this.imageHeight =imageHeight*this.imageWidth / imageWidth
             })
             .objectFit(ImageFit.Contain)
             .geometryTransition('app.media.img' + (index+1))
             .transition(TransitionEffect.OPACITY)
         }
         .width('100%')
         .height('100%')
        }, (item: string) => item)
      }
      .transition(TransitionEffect.opacity(0.99))
      .width('100%')
      .height('100%')
      .clip(false)
      .index(this.selectedIndex)
      .onChange((index: number) => {
        this.selectedIndex = index;
      })
      .indicator(false)
    }
    .onReady((context: NavDestinationContext) => {
      this.pathStack = context.pathStack;
      let param = context.pathInfo?.param as Record<string, Object>;
      if (param === undefined) {
        return;
      }
      this.selectedIndex = param['selectedIndex'] as number;
    })
    .width('100%')
    .height('100%')
    .mode(NavDestinationMode.DIALOG)
    .hideTitleBar(true)
    .expandSafeArea([SafeAreaType.SYSTEM])
    .onBackPressed(() => {
      this.backToFirstPage();
      return true;
    })
    .transition(TransitionEffect.opacity(0.99))
  }
}

2.增加拖动手势,改变图片的位置
鸿蒙Next图片预览缩放拖动一镜到底实现-鸿蒙开发者社区

PanGesture({ direction: this.panDirection })
  .onActionStart((event: GestureEvent) => {
    //初始识别的触发方向是垂直方向,识别成功后改为所有方向可拖动
    this.panDirection = PanDirection.All;
    this.startGestureOffsetX = px2vp(this.imageTranslateX);
    this.startGestureOffsetY = px2vp(this.imageTranslateY);
    console.log('---PanGesture--onActionStart() startGestureOffsetX:'+this.startGestureOffsetX+'startGestureOffsetY:'+this.startGestureOffsetY)
  })
  .onActionUpdate((event: GestureEvent) => {
    let offsetX = event.offsetX;
    let offsetY = event.offsetY;
    // 拖拽状态下,图片跟随手指移动.
    this.imageTranslateX = vp2px(this.startGestureOffsetX + offsetX);
    this.imageTranslateY = vp2px(this.startGestureOffsetY + offsetY);
    //根据垂直方向的拖动距离偏移量计算背景的透明度
    this.imagePullingDownScale = 1 - Math.abs(event.offsetY) / this.getUIContext().px2vp(display.getDefaultDisplaySync().height);
  })
  .onActionEnd((event: GestureEvent) => {
    //手势结束 恢复,后续需要增加判断 垂直方向滑动一定距离退出当前页面
    this.panDirection = PanDirection.Vertical;
    this.imagePullingDownScale = 1;
    this.imageTranslateX = 0;
    this.imageTranslateY = 0;
  })
))

3.增加捏合手势,放大缩小
鸿蒙Next图片预览缩放拖动一镜到底实现-鸿蒙开发者社区

PinchGesture()
  .onActionStart((event: GestureEvent) => {
    this.startGestureScale = this.imageScale;
  })
  .onActionUpdate((event: GestureEvent) => {
    this.imageScale = this.startGestureScale * event.scale;
  })
  .onActionEnd(() => {
    if (this.imageScale < 1) {
      // 如果捏合缩小 默认恢复到初始状态
      this.getUIContext().animateTo({ duration: 250, curve: Curve.EaseOut }, () => {
        this.imageScale = 1;
      })
    }
  })

4.双击手势恢复,单击手势退出

//双击手势
TapGesture({ count: 2})
  .onAction((event: GestureEvent) => {
    if (this.imageScale > 1) {
      this.imageScale = 1
    }
    this.onAllGestureFinish()
  }),
//单击手势
TapGesture({ count: 1 })
  .onAction(() => {
    if ((this.imageScale == 1)) {
      this.backToFirstPage()
    }
  })

源码

九宫格页

export const imageList: (string | Resource)[] = [
  $r('app.media.img1'),
  $r('app.media.img2'),
  $r('app.media.img3'),
  $r('app.media.img4'),
  $r('app.media.img5'),
  $r('app.media.img6'),
  $r('app.media.img7'),
  $r('app.media.img8'),
  $r('app.media.img9'),
];

@Builder
export function ImagePreviewTestBuilder() {
  ImagePreviewTest()
}

@ComponentV2
struct ImagePreviewTest{
  pathStack: NavPathStack = new NavPathStack()
  onItemClick(index: number): void {
    let param: Record<string, Object> = {};
    param['selectedIndex'] = index;
    this.getUIContext().animateTo({
      duration: 250,
      curve: Curve.EaseIn,
    }, () => {
      this.pathStack.pushPath({
        name: 'ImagePreviewPage', param: param
      },false)
    })
  }

  build() {
    NavDestination(){
      Grid() {
        ForEach(imageList, (item: string | Resource, index: number) => {
          GridItem() {
            Image(item)
              .width('100%')
              .height('100%')
              .objectFit(ImageFit.Cover)
              .borderRadius(5)
              .onClick(() => {
                this.onItemClick(index);
              })
              .geometryTransition('app.media.img' + (index+1))
              .transition(TransitionEffect.opacity(0.99))
          }
          .width(90)
          .height(90)
        })
      }
      .columnsTemplate('1fr 1fr 1fr')
      .rowsTemplate('1fr 1fr 1fr')
      .width('100%')
      .height('100%')
    }
    .hideTitleBar(true)
    .onReady((context: NavDestinationContext) => {
      this.pathStack = context.pathStack;
    })
  }
}

详情页

import { imageList } from "../view/ImagePreviewTest";
import { display } from "@kit.ArkUI";
import { PhotoPreviewItem } from "library_common";

@Builder
export function ImagePreviewPageBuilder() {
  ImagePreviewPage()
}
@ComponentV2
struct ImagePreviewPage{
  private pathStack: NavPathStack = new NavPathStack();
  @Local selectedIndex: number = 0;
  @Local imageWidth: number = display.getDefaultDisplaySync().width;
  @Local imageHeight: number = display.getDefaultDisplaySync().width;

  private backToFirstPage: () => void = () => {
    this.getUIContext().animateTo({
      duration: 300,
      curve: Curve.EaseIn,
    }, () => {
      this.pathStack.pop(false);
    })
  }
  build() {
    NavDestination(){
      Swiper() {
        ForEach(imageList,  (item: string | Resource, index: number) => {
          PhotoPreviewItem({
            imageUrl: 'app.media.img' + (index+1),
            backToFirstPage: this.backToFirstPage
          })
        }, (item: string) => item)
      }
      .transition(TransitionEffect.opacity(0.99))
      .width('100%')
      .height('100%')
      .clip(false)
      .index(this.selectedIndex)
      .onChange((index: number) => {
        this.selectedIndex = index;
      })
      .indicator(false)
    }
    .onReady((context: NavDestinationContext) => {
      this.pathStack = context.pathStack;
      let param = context.pathInfo?.param as Record<string, Object>;
      if (param === undefined) {
        return;
      }
      this.selectedIndex = param['selectedIndex'] as number;
    })
    .width('100%')
    .height('100%')
    .mode(NavDestinationMode.DIALOG)
    .hideTitleBar(true)
    .expandSafeArea([SafeAreaType.SYSTEM])
    .onBackPressed(() => {
      this.backToFirstPage();
      return true;
    })
    .transition(TransitionEffect.opacity(0.99))
  }
}

自定义图片组件

import { display, matrix4 } from "@kit.ArkUI";
enum Status {
  IDLE,          //初始
  PINCHING,      //捏合
  PAN_ONLY,      //拖动
  READY_TO_BACK  // 返回
}

@ComponentV2
export struct PhotoPreviewItem {
  @Local status: Status = Status.IDLE;
  @Local gestureCount: number = 0;
  @BuilderParam imageUrl: string;
  @Local imageScale: number = 1; // 缩放手势 改变图片缩放比例
  @Local imagePullingDownScale: number = 1; // 拖拽手势 改变缩放比例
  @Local imageTranslateX: number = 0; // 拖拽手势偏移 距离
  @Local imageTranslateY: number = 0; // 拖拽手势偏移 距离
  @Local geometryPositionX: number = 0;
  @Local geometryPositionY: number = 0;
  @Local centerX: number = 0;
  @Local centerY: number = 0;
  @Local imageWidth: number = display.getDefaultDisplaySync().width;
  @Local imageHeight: number = display.getDefaultDisplaySync().height;
  @Local panDirection: PanDirection = PanDirection.Vertical; //滑动手势 触发方向
  @Local gestureDisabled: boolean = false; //是否想要手势
  //图片拖动偏移量
  @Local maxOffsetX: number = 0;
  @Local minOffsetX: number = 0;
  @Local maxOffsetY: number = 0;
  @Local minOffsetY: number = 0;
  //实际显示边界由图像边界和显示边界共同控制。
  private realDisplayBoundsLeft: number = 0;
  private realDisplayBoundsTop: number = 0;
  private realDisplayBoundsRight: number = 0;
  private realDisplayBoundsBottom: number = 0;

  // 记录手势的初始状态。偏移量的单位是(vp)
  private startGestureOffsetX: number = 0;
  private startGestureOffsetY: number = 0;
  private startGestureScale: number = 1;

  private imagePositionX: number = 0; // 图像变换前的坐标
  private imagePositionY: number = 0; // 图像变换前的坐标

  //当前组件左上角的边界坐标 屏幕的左上角和右下角
  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
  @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;
        let leftTop = this.calculateLeftTopPoint();
        let rightBottom = this.calculateRightBottomPoint();
        let imageWidth = this.imageWidth * this.imageScale;
        if (imageWidth < this.displayRight - this.displayLeft) {
          // 如果图片宽度小于显示范围的宽度,则居中显示
          this.centerX = 0;
          this.imageTranslateX = 0;
        } else if (leftTop[0] > this.displayLeft) {
          this.imageTranslateX += (this.displayLeft - leftTop[0]);
        } else if (rightBottom[0] < this.displayRight) {
          this.imageTranslateX += (this.displayRight - rightBottom[0]);
        }

        let imageHeight = this.imageHeight * this.imageScale;
        if (imageHeight < this.displayBottom - this.displayTop) {
          this.centerY = 0;
          this.imageTranslateY = 0;
        } else if (leftTop[1] > this.displayTop) {
          this.imageTranslateY += (this.displayTop - leftTop[1]);
        } else if (rightBottom[1] < this.displayBottom) {
          this.imageTranslateY += (this.displayBottom - rightBottom[1]);
        }
      })
    }

    if (this.status === Status.READY_TO_BACK) {
      this.status = Status.IDLE;
      this.disableGesture();
      this.getUIContext().animateTo({
        duration: 300,
        onFinish: () => {
          this.resumeGesture();
          this.updatePanDirection();
        }
      }, () => {
        let leftTopPoint = this.calculateLeftTopPoint();
        this.geometryPositionX = leftTopPoint[0] - this.imagePositionX;
        this.geometryPositionY = leftTopPoint[1] - this.imagePositionY;
        this.geometryScale = this.imageScale * this.imagePullingDownScale;
        this.resetTransform();
        this.backToFirstPage();
      })
    }
  }
  //根据中心的变化,调整平移,以确保图像的位置在变换后保持不变
  updateTranslateAccordingToCenter() {
    if (this.lastCenterX === this.centerX && this.lastCenterY === this.centerY) {
      return;
    }
    let lastTranslateX = this.imageTranslateX;
    let lastTranslateY = this.imageTranslateY;
    let matrixOld = matrix4.identity()
      .scale({
        x: this.imageScale * this.imagePullingDownScale,
        y: this.imageScale * this.imagePullingDownScale,
        centerX: this.lastCenterX,
        centerY: this.lastCenterY
      })
      .translate({ x: lastTranslateX, y: lastTranslateY })
    let 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]);
  }
  updateExtremeOffset() {
    let totalScale = this.imageScale * this.imagePullingDownScale;
    let matrix = matrix4.identity()
      .scale({
        x: totalScale,
        y: totalScale,
        centerX: this.centerX,
        centerY: this.centerY
      })

    let p = matrix.transformPoint([-this.imageWidth / 2, -this.imageHeight / 2]);
    let leftTopPointX = p[0] + this.imageWidth / 2 + this.imagePositionX;
    let leftTopPointY = p[1] + this.imageHeight / 2 + this.imagePositionY;
    let rightBottomPointX = leftTopPointX + this.imageWidth * totalScale;
    let rightBottomPointY = leftTopPointY + this.imageHeight * totalScale;
    console.log('---leftTopPointX:'+leftTopPointX+' leftTopPointY: '+leftTopPointY)
    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);
    console.log('---realDisplayBoundsLeft:'+this.realDisplayBoundsLeft+' realDisplayBoundsRight: '+this.realDisplayBoundsRight)

    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('---minOffsetX:'+this.minOffsetX+' maxOffsetX:'+this.maxOffsetX)
    console.log('---minOffsetY:'+this.minOffsetY+' maxOffsetY:'+this.maxOffsetY)
  }

  //重置滑动手势触发方向
  updatePanDirection(): void {
    this.panDirection = PanDirection.Vertical;
    let leftTop = this.calculateLeftTopPoint();
    let rightBottom = this.calculateRightBottomPoint();
    //根据顶点左边 设置滑动方向
    if (leftTop[0] < this.displayLeft - 1) {
      this.panDirection = this.panDirection | PanDirection.Right;
    }
    if (rightBottom[0] > this.displayRight + 1) {
      this.panDirection = 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();
  }
  //左上角的坐标
  updateCenter(gestureCenterPoint: [number, number]) {
    this.lastCenterX = this.centerX;
    this.lastCenterY = this.centerY;
    let 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]); // px
    let leftTopPointX = leftTop[0] + this.imageWidth / 2; // px
    let leftTopPointY = leftTop[1] + this.imageHeight / 2;
    this.centerX = (gestureCenterPoint[0] - leftTopPointX) / this.imageScale - this.imageWidth / 2;
    this.centerY = (gestureCenterPoint[1] - leftTopPointY) / this.imageScale - this.imageHeight / 2;
  }
  //计算变换后图像左上角的坐标
  calculateLeftTopPoint(): [number, number] {
    let 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]);
    let leftTopPointX = leftTop[0] + this.imageWidth / 2 + this.imagePositionX;
    let leftTopPointY = leftTop[1] + this.imageHeight / 2 + this.imagePositionY;
    console.log('calculateLeftTopPoint()  leftTopPointX:'+leftTopPointX+'  leftTopPointY:'+leftTopPointY)
    return [leftTopPointX, leftTopPointY];
  }
  //计算图像变换后右下角的坐标
  calculateRightBottomPoint(): [number, number] {
    let 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]);
    let rightBottomPointX = rightBottom[0] + this.imageWidth / 2 + this.imagePositionX;
    let rightBottomPointY = rightBottom[1] + this.imageHeight / 2 + this.imagePositionY;
    return [rightBottomPointX, rightBottomPointY];
  }
  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();
  }
  build() {
    Stack() {
      Column()
        .width('100%')
        .height('100%')
        .backgroundColor(Color.Black)
        .transition(TransitionEffect.OPACITY)
        .opacity(this.imagePullingDownScale)
      Stack() {
        Image($r(this.imageUrl))
          .width(px2vp(this.imageWidth) * this.geometryScale)
          .height(px2vp(this.imageHeight) * this.geometryScale)
          .onComplete((event) => {
            let imageWidth = event?.width ?? 0
            let imageHeight = event?.height ?? 0
            this.imageHeight = imageHeight * this.imageWidth / imageWidth
          })
          .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
            }))
          .geometryTransition(this.imageUrl)
          .transition(TransitionEffect.OPACITY)
        .position({
          x: px2vp(this.geometryPositionX),
          y: px2vp(this.geometryPositionY)
        })
      }
      .id(this.imageUrl)
      .width(px2vp(this.imageWidth))
      .height(px2vp(this.imageHeight))
      .parallelGesture(GestureGroup(GestureMode.Exclusive,
        PinchGesture()
          .onActionStart((event: GestureEvent) => {
            this.status = Status.PINCHING;
            this.updateCenter([vp2px(event.pinchCenterX), vp2px(event.pinchCenterY)]);
            //根据中心的变化,调整平移量,以确保变换后图像的位置保持不变
            this.updateTranslateAccordingToCenter();
            this.startGestureScale = this.imageScale;
            this.gestureCount++;
          })
          .onActionUpdate((event: GestureEvent) => {
            this.imageScale = this.startGestureScale * event.scale;
            this.updateExtremeOffset();
          })
          .onActionEnd(() => {
            this.status = Status.IDLE;
            if (this.imageScale < 1) {
              // 如果捏合缩小 默认恢复到初始状态
              this.getUIContext().animateTo({ duration: 250, curve: Curve.EaseOut }, () => {
                this.imageScale = 1;
              })
            }
            this.gestureCount--;
          }),
        PanGesture({ direction: this.panDirection })
          .onActionStart((event: GestureEvent) => {
            //初始识别的触发方向是垂直方向,识别成功后改为所有方向可拖动
            this.panDirection = PanDirection.All;
            let leftTop = this.calculateLeftTopPoint();
            // 图片的没有显示超过屏幕 不是放大状态
            if (leftTop[1] >= this.displayTop - 1 && this.status !== Status.PINCHING) {
              this.status = Status.PAN_ONLY;
            }
            if (this.status !== Status.PINCHING) {
              let currentX: number = event.offsetX;
              let currentY: number = event.offsetY;
              if (event.fingerList[0] !== undefined) {
                currentX = event.fingerList[0].globalX;
                currentY = event.fingerList[0].globalY;
              }
              this.updateCenter([vp2px(currentX- this.imagePositionX), vp2px(currentY-this.imagePositionY) ]);
              this.updateTranslateAccordingToCenter();
              this.updateExtremeOffset();
            }
            this.startGestureOffsetX = px2vp(this.imageTranslateX);
            this.startGestureOffsetY = px2vp(this.imageTranslateY);
            console.log('---PanGesture--onActionStart() startGestureOffsetX:'+this.startGestureOffsetX+'startGestureOffsetY:'+this.startGestureOffsetY)
            this.gestureCount++;
          })
          .onActionUpdate((event: GestureEvent) => {
            let offsetX = event.offsetX;
            let 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(display.getDefaultDisplaySync().height);
            }

            if (this.status !== Status.PAN_ONLY) {
              if (offsetX + this.startGestureOffsetX > this.maxOffsetX) {
                let distance = offsetX + this.startGestureOffsetX - this.maxOffsetX;
                offsetX = this.maxOffsetX + distance * (1 - Math.exp(-distance / 300)) - this.startGestureOffsetX;
              }
              if (offsetX + this.startGestureOffsetX < this.minOffsetX) {
                let distance = this.minOffsetX - (offsetX + this.startGestureOffsetX)
                offsetX = this.minOffsetX - distance * (1 - Math.exp(-distance / 300)) - this.startGestureOffsetX;
              }
              if (offsetY + this.startGestureOffsetY > this.maxOffsetY) {
                let distance = offsetY + this.startGestureOffsetY - this.maxOffsetY;
                offsetY = this.maxOffsetY + distance * (1 - Math.exp(-distance / 300)) - this.startGestureOffsetY;
              }
              if (offsetY + this.startGestureOffsetY < this.minOffsetY) {
                let distance = this.minOffsetY - (offsetY + this.startGestureOffsetY)
                offsetY = this.minOffsetY - distance * (1 - Math.exp(-distance / 300)) - this.startGestureOffsetY;
              }
              this.imageTranslateX = vp2px(this.startGestureOffsetX + offsetX);
              this.imageTranslateY = vp2px(this.startGestureOffsetY + offsetY);
            }
          })
          .onActionEnd((event: GestureEvent) => {
            //手势结束 恢复,垂直方向滑动一定距离退出当前页面
            if (this.status === Status.PAN_ONLY) {
              if (event.offsetY > 100) {
                this.status = Status.READY_TO_BACK;
              } else {
                this.status = Status.IDLE;
              }
            }
            this.gestureCount--;
          }),
        //双击手势
        TapGesture({ count: 2})
          .onAction((event: GestureEvent) => {
            if (this.imageScale > 1) {
              this.imageScale = 1
            }
            this.onAllGestureFinish()
          }),
        //单击手势
        TapGesture({ count: 1 })
          .onAction(() => {
            if ((this.imageScale == 1)) {
              this.backToFirstPage()
            }
          }),
      ))
      .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;
      })
    }
    .width('100%')
    .height('100%')
  }
}

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