基于手势的图片预览与缩放

xcbaby
发布于 2024-8-1 16:34
浏览
0收藏

场景一:对图片进行放大、缩小、拖拽移动,且放大过程中也可同时进行拖拽操作

方案

1、使用组合手势GestureGroup,同时绑定捏合手势PinchGesture和滑动手势PanGesture,设置组合手势识别模式为并行识别模式:Parallel,并行识别组合手势中注册的手势将同时进行识别,直到所有手势识别结束,并行识别手势组合中的手势进行识别时互不影响。

2、在对图片进行双指捏合时,优先触发绑定的PinchGesture手势,对图片进行缩放操作;当滑动拖拽图片时,识别绑定的PanGesture手势,对图片进行拖拽移动。

核心代码

1、绑定组合手势GestureGroup,设置为并行识别模式,添加捏合手势PinchGesture和滑动手势PanGesture。

@Styles 
onImageGesture(){ 
  .gesture( 
    GestureGroup(GestureMode.Parallel, 
      // 双指捏合手势 
      PinchGesture({ fingers: 2, distance: 0.1 }) 
        .onActionUpdate((event: GestureEvent) => { 
          this.onPinchGestureActionUpdate(event); 
        }) 
        .onActionEnd(() => { 
          this.onPinchGestureActionEnd(); 
        }), 
      // 拖动手势 
      PanGesture(this.panOption) 
        .onActionUpdate((event?: GestureEvent) => { 
          this.onPanGestureActionUpdate(event); 
        }) 
        .onActionEnd(() => { 
          this.onPanGestureActionEnd(); 
        }) 
    ) 
  ) 
}

2、在捏合手势的onActionUpdate和onActionEnd回调中修改scale参数,进行图片缩放处理。

// 当捏合手势触发时,可以通过回调函数获取缩放比例,从而修改组件的缩放比例 
onPinchGestureActionUpdate(event: GestureEvent) { 
  const SCALE_VALUE = this.pinchValue * event.scale; 
  if (SCALE_VALUE <= this.imageMeta.MAX_SCALE_VALUE && SCALE_VALUE >= this.MIN_SCALE) { 
    this.scaleValue = SCALE_VALUE; 
    this.pinchX = event.pinchCenterX; 
    this.pinchY = this.getPinchY(event.pinchCenterY); 
    if (this.scaleValue > 1) { 
      this.offsetX = 0; 
    } 
  } 
} 
 
getPinchY(pinchCenterY: number | string) { 
  let pinchY = pinchCenterY; 
  if (this.scaleValue > 1) { 
    // 当为横图时,在留白处放大,图片位置不居中,手动计算pinchY,并移动Y轴中心点,即offsetY 
    if (this.imageMeta.IMAGE_WIDTH >= this.imageMeta.IMAGE_HEIGHT) { 
      const SCREEN_HEIGHT = px2vp(display.getDefaultDisplaySync().height); 
      const MIN_PINCH_Y = SCREEN_HEIGHT / 2 - this.imageMeta.IMAGE_HEIGHT / 2; 
      const MAX_PINCH_Y = SCREEN_HEIGHT / 2 + this.imageMeta.IMAGE_HEIGHT / 2; 
      const MIDDLE_PINCH_Y = SCREEN_HEIGHT / 2; 
      if (pinchY < MIN_PINCH_Y) { 
        pinchY = MIN_PINCH_Y; 
      } 
      if (pinchY > MAX_PINCH_Y) { 
        pinchY = MAX_PINCH_Y; 
      } 
      if (pinchY < MIDDLE_PINCH_Y && typeof this.pinchY === 'number') { 
        this.offsetY = (Number(pinchY) - MIDDLE_PINCH_Y) * (this.scaleValue - 1); 
      } 
      if (pinchY > MIDDLE_PINCH_Y && typeof this.pinchY === 'number') { 
        this.offsetY = (Number(pinchY) - MIDDLE_PINCH_Y) * (this.scaleValue - 1); 
      } 
    } 
  } 
  return pinchY; 
} 
 
// 当捏合手势结束时,计算当前的isScaling 
// 如果缩放比例小于1,弹簧效果重置成原比例 
onPinchGestureActionEnd() { 
  this.pinchValue = this.scaleValue; 
  if (this.pinchValue > 1) { 
    this.scaleEnable(); 
  } else { 
    this.scaleUnEnable(); 
    if (this.pinchValue < 1) { 
      this.reset(); 
    } 
  } 
}

场景二:图片在放大的情况下也可切换到后一张图或者前一张图

方案

1、在panGesture手势的onActionUpdate回调中获取偏移位置,计算图片放大后分别往左或者往右拖动时是否到达边界,记录向左或向右边界的状态。

2、在onTouch事件中识别手指滑动方向,并判断边界是翻页还是拖动,方向为左右且到达边界,执行scaleUnEnable方法,将panGesture手势方向置为none,执行翻页;反之执行滑动手势,不翻页。

核心代码

1、在PanGesture手势的onActionUpDate回调中获取偏移位置,计算拖拽过程中左右两边分别是否已到边界。

// 当拖动手势触发时,可以通过回调函数获取偏移位置,并计算是否已到边界,不可拖出边界 
onPanGestureActionUpdate(event: GestureEvent) { 
  if (!event || this.scaleValue <= 1) { 
    return; 
  } 
  let boundaryOffset: BoundaryOffset = { 
    left: 0, 
    right: 0, 
    top: 0, 
    bottom: 0 
  }; 
  this.xBoundary = HorizontalBoundary.MIDDLE; 
  this.yBoundary = VerticalBoundary.MIDDLE; 
 
  const CENTER_X_RELATIVE = typeof this.pinchX === 'number' ? this.pinchX / this.imageMeta.IMAGE_WIDTH : 0.5; 
  boundaryOffset.left = this.imageMeta.IMAGE_WIDTH * (this.scaleValue - 1) * CENTER_X_RELATIVE; 
  boundaryOffset.right = -this.imageMeta.IMAGE_WIDTH * (this.scaleValue - 1) * (1 - CENTER_X_RELATIVE); 
  const NEWOFFSET_X = this.positionX + event.offsetX; 
  // 更新x轴偏移位置 
  if (NEWOFFSET_X <= boundaryOffset.left && newOffsetX >= boundaryOffset.right) { 
    // 在中间 
    this.offsetX = NEWOFFSET_X; 
  } else { 
    // 在边界 
    if (this.fingerDirection === FingerDirection.LEFT && this.offsetX !== boundaryOffset.right) { 
      this.offsetX = boundaryOffset.right; 
    } 
    if (this.fingerDirection === FingerDirection.RIGHT && this.offsetX !== boundaryOffset.left) { 
      this.offsetX = boundaryOffset.left; 
    } 
  } 
  // 在左边界 
  if (NEWOFFSET_X >= boundaryOffset.left) { 
    this.xBoundary = HorizontalBoundary.LEFT; 
  } 
  // 在右边界 
  if (NEWOFFSET_X <= boundaryOffset.right) { 
    this.xBoundary = HorizontalBoundary.RIGHT; 
  } 
  if (this.imageMeta.PAN_DIRECTION === PanDirection.All || this.imageMeta.PAN_DIRECTION === PanDirection.Vertical||this.imageMeta.PAN_DIRECTION === PanDirection.Horizontal) { 
    // 更新y轴偏移量 
    const NEWOFFSET_Y = this.positionY + event.offsetY; 
    this.offsetY = NEWOFFSET_Y; 
  } 
}

2、通过绑定onTouch事件,在滑动的过程中根据手指方向和是否到达边界通过isPageChangeOnBoundary方法判断后分别执行scaleEnable和scaleUnEnable方法进行翻页或者拖动处理。

// 识别手指滑动方向,并判断边界是翻页还是拖动 
@Styles 
onImageTouch(){ 
  .onTouch((event: TouchEvent) => { 
    if (this.scaleValue > 1) { 
      if (event.type === TouchType.Down) { 
        this.touchPos.x = event.touches[0].x; 
        this.touchPos.y = event.touches[0].y; 
      } 
      if (event.type === TouchType.Move) { 
        this.fingerDirection = getMoveFingerDirection(this.touchPos, event, this.fingerDirection); 
        if (!isPageChangeOnBoundary(this.fingerDirection, this.xBoundary)) { 
          if (!this.isScaling) { 
            this.touchFlag = 1; 
            this.scaleEnable(); 
            this.xBoundary = HorizontalBoundary.MIDDLE; 
            this.temp = this.offsetX; 
            this.tempY = this.offsetY; 
          } 
        } 
      } 
      if (event.type === TouchType.Up) { 
        if (this.touchFlag == 1) { 
          this.onPanGestureActionEnd(); 
        } 
        this.touchFlag = 0; 
        this.temp = 0; 
        this.tempY = 0; 
        if (isPageChangeOnBoundary(this.fingerDirection, this.xBoundary) || this.scaleValue === 1) { 
          this.scaleUnEnable(); 
        } else { 
          this.scaleEnable(); 
        } 
      } 
    } 
  }) 
} 
 
// 拖动,不翻页 
scaleEnable() { 
  if (this.isScaling) { 
    return; 
  } 
  this.panOption.setDirection(this.imageMeta.PAN_DIRECTION); 
  this.isScaling = true; 
} 
 
// 不能拖动,翻页 
scaleUnEnable() { 
  if (!this.isScaling) { 
    return; 
  } 
  this.panOption.setDirection(PanDirection.None); 
  this.isScaling = false; 
}

3、在isPageChangeOnBoundary 方法中根据传入的手指滑动方向和拖动到达的边界方向来判断返回,之后再分别调用不同场景下的方法处理逻辑,进行翻页或者拖动。

// 在边界进行翻页 
export const isPageChangeOnBoundary = (fingerDirection: FingerDirection, xBoundary: HorizontalBoundary) => { 
  return (fingerDirection === FingerDirection.LEFT && xBoundary === HorizontalBoundary.RIGHT || 
    fingerDirection === FingerDirection.RIGHT && xBoundary === HorizontalBoundary.LEFT); 
}

分类
已于2024-8-1 16:34:22修改
收藏
回复
举报
回复
    相关推荐