{"version":3,"sources":["jsdelivr-header.js","/npm/@openspacelabs/react-native-zoomable-view@2.4.2/lib/module/helper/coordinateConversion.ts","/npm/@openspacelabs/react-native-zoomable-view@2.4.2/lib/module/components/AnimatedTouchFeedback.tsx","/npm/@openspacelabs/react-native-zoomable-view@2.4.2/lib/module/debugHelper/index.tsx","/npm/@openspacelabs/react-native-zoomable-view@2.4.2/lib/module/helper/calcNewScaledOffsetForZoomCentering.ts","/npm/@openspacelabs/react-native-zoomable-view@2.4.2/lib/module/helper/index.ts","/npm/@openspacelabs/react-native-zoomable-view@2.4.2/lib/module/components/StaticPin.tsx","/npm/@openspacelabs/react-native-zoomable-view@2.4.2/lib/module/ReactNativeZoomableView.tsx","/npm/@openspacelabs/react-native-zoomable-view@2.4.2/lib/module/animations/index.ts","/npm/@openspacelabs/react-native-zoomable-view@2.4.2/lib/module/helper/applyPanBoundariesToOffset.ts"],"names":[],"mappings":"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","file":"/npm/@openspacelabs/react-native-zoomable-view@2.4.2/lib/module/index.js/+esm","sourceRoot":"","sourcesContent":["/**\n * Bundled by jsDelivr using Rollup v2.79.2 and Terser v5.39.0.\n * Original file: /npm/@openspacelabs/react-native-zoomable-view@2.4.2/lib/module/index.js\n *\n * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files\n */\n","import { Size2D, Vec2D, ZoomableViewEvent } from 'src/typings';\n\nexport const defaultTransformSubjectData: ZoomableViewEvent = {\n  offsetX: 0,\n  offsetY: 0,\n  zoomLevel: 0,\n  originalWidth: 0,\n  originalHeight: 0,\n  originalPageX: 0,\n  originalPageY: 0,\n};\n\n/**\n * Assuming you have an image that's being resized to fit into a container\n * using the \"contain\" resize mode. You can use this function to calculate the\n * size of the image after fitting.\n *\n * Since our sheet is resized in this manner, we need this function\n * for things like pan boundaries and marker placement\n *\n * @param imgSize\n * @param containerSize\n */\nexport function applyContainResizeMode(\n  imgSize: Size2D,\n  containerSize: Size2D\n): { size: Size2D; scale: number } | { size: null; scale: null } {\n  const { width: imageWidth, height: imageHeight } = imgSize;\n  const { width: areaWidth, height: areaHeight } = containerSize;\n  const imageAspect = imageWidth / imageHeight;\n  const areaAspect = areaWidth / areaHeight;\n\n  let newSize;\n  if (imageAspect >= areaAspect) {\n    // longest edge is horizontal\n    newSize = { width: areaWidth, height: areaWidth / imageAspect };\n  } else {\n    // longest edge is vertical\n    newSize = { width: areaHeight * imageAspect, height: areaHeight };\n  }\n\n  if (isNaN(newSize.height)) newSize.height = areaHeight;\n  if (isNaN(newSize.width)) newSize.width = areaWidth;\n\n  const scale = imageWidth\n    ? newSize.width / imageWidth\n    : newSize.height / imageHeight;\n\n  if (!isFinite(scale)) return { size: null, scale: null };\n\n  return {\n    size: newSize,\n    scale,\n  };\n}\n\n/**\n * get the coord of image's origin relative to the transformSubject\n * @param resizedImageSize\n * @param transformSubject\n */\nexport function getImageOriginOnTransformSubject(\n  resizedImageSize: Size2D,\n  transformSubject: ZoomableViewEvent\n) {\n  const { offsetX, offsetY, zoomLevel, originalWidth, originalHeight } =\n    transformSubject;\n  return {\n    x:\n      offsetX * zoomLevel +\n      originalWidth / 2 -\n      (resizedImageSize.width / 2) * zoomLevel,\n    y:\n      offsetY * zoomLevel +\n      originalHeight / 2 -\n      (resizedImageSize.height / 2) * zoomLevel,\n  };\n}\n\n/**\n * Translates the coord system of a point from the viewport's space to the image's space\n *\n * @param pointOnContainer\n * @param sheetImageSize\n * @param transformSubject\n *\n * @return {Vec2D} returns null if point is out of the sheet's bound\n */\nexport function viewportPositionToImagePosition({\n  viewportPosition,\n  imageSize,\n  zoomableEvent,\n}: {\n  viewportPosition: Vec2D;\n  imageSize: Size2D;\n  zoomableEvent: ZoomableViewEvent;\n}): Vec2D | null {\n  const { size: resizedImgSize, scale: resizedImgScale } =\n    applyContainResizeMode(imageSize, {\n      width: zoomableEvent.originalWidth,\n      height: zoomableEvent.originalHeight,\n    });\n\n  if (resizedImgScale == null) return null;\n\n  const sheetOriginOnContainer = getImageOriginOnTransformSubject(\n    resizedImgSize,\n    zoomableEvent\n  );\n\n  const pointOnSheet = {\n    x:\n      (viewportPosition.x - sheetOriginOnContainer.x) /\n      zoomableEvent.zoomLevel /\n      resizedImgScale,\n    y:\n      (viewportPosition.y - sheetOriginOnContainer.y) /\n      zoomableEvent.zoomLevel /\n      resizedImgScale,\n  };\n\n  return pointOnSheet;\n}\n","import React, { useEffect, useRef } from 'react';\nimport { Animated, Easing, StyleSheet } from 'react-native';\n\nexport const AnimatedTouchFeedback = ({\n  x,\n  y,\n  animationDelay,\n  animationDuration,\n  onAnimationDone,\n}: {\n  x: number;\n  y: number;\n  animationDuration: number;\n  animationDelay?: number;\n  onAnimationDone?: () => void;\n}) => {\n  const appearDisappearAnimRef = useRef<Animated.Value>(new Animated.Value(0));\n  const onAnimationDoneRef = useRef(onAnimationDone);\n  onAnimationDoneRef.current = onAnimationDone;\n\n  useEffect(() => {\n    appearDisappearAnimRef.current.setValue(0);\n    const inDuration = animationDuration * 0.8;\n    const outDuration = animationDuration - inDuration;\n    Animated.sequence([\n      Animated.timing(appearDisappearAnimRef.current, {\n        delay: animationDelay || 0,\n        toValue: 1,\n        duration: inDuration,\n        easing: Easing.linear,\n        useNativeDriver: true,\n      }),\n      Animated.timing(appearDisappearAnimRef.current, {\n        toValue: 0,\n        duration: outDuration,\n        easing: Easing.out(Easing.ease),\n        useNativeDriver: true,\n      }),\n    ]).start(() => onAnimationDoneRef.current?.());\n  }, [animationDelay, animationDuration]);\n\n  return (\n    <Animated.View\n      pointerEvents=\"none\"\n      style={[\n        styles.animatedTouchFeedback,\n        {\n          opacity: appearDisappearAnimRef.current.interpolate({\n            inputRange: [0, 1],\n            outputRange: [0, 0.3],\n          }),\n          left: x - 20,\n          top: y - 20,\n          transform: [\n            {\n              scale: appearDisappearAnimRef.current.interpolate({\n                inputRange: [0, 1],\n                outputRange: [0.5, 1],\n              }),\n            },\n          ],\n        },\n      ]}\n    />\n  );\n};\n\nconst styles = StyleSheet.create({\n  animatedTouchFeedback: {\n    backgroundColor: 'lightgray',\n    borderRadius: 40,\n    height: 40,\n    position: 'absolute',\n    width: 40,\n  },\n});\n","import { View, StyleSheet } from 'react-native';\nimport React from 'react';\n\nexport const DebugTouchPoint = ({\n  diameter = 20,\n  x = 0,\n  y = 0,\n  color = 'yellow',\n}) => {\n  const radius = diameter / 2;\n  return (\n    <View\n      style={[\n        styles.debugPoint,\n        {\n          width: diameter,\n          height: diameter,\n          borderRadius: diameter,\n          backgroundColor: color,\n          left: x - radius,\n          top: y - radius,\n        },\n      ]}\n      pointerEvents=\"none\"\n    />\n  );\n};\nexport const DebugRect = ({\n  height,\n  x = 0,\n  y = 0,\n  color = 'yellow',\n}: {\n  height: number;\n  x: number;\n  y: number;\n  color: string;\n}) => {\n  const width = 5;\n  return (\n    <View\n      style={[\n        styles.debugRect,\n        {\n          width,\n          height,\n          backgroundColor: color,\n          left: x - width / 2,\n          top: y,\n        },\n      ]}\n      pointerEvents=\"none\"\n    />\n  );\n};\n\nconst styles = StyleSheet.create({\n  debugPoint: {\n    opacity: 0.7,\n    position: 'absolute',\n  },\n  debugRect: {\n    opacity: 0.5,\n    position: 'absolute',\n  },\n});\n","/**\n * Calculates the new offset for the zoomSubject to ensure zoom center position is retained after zooming.\n * Parameters should correspond to whether we need the offset for the X or Y axis\n *\n * ## Terms Used:\n *\n * - Zoom Subject: the view that's being zoomed and panned\n * - Zoom Center: the point whose relative position to the window is retained\n * - Unscaled: a measurement in pixels denoting the true size as observed by the users' eyes\n * - Scaled: a measurement in pixels scaled to the \"scale transformation\" of the zoom subject to match with its true size.\n *  *For example:*\n *   If the scale on the zoom subject is 0.5,\n *   then to draw an actual 4px line on the zoom subject, we need to scale it to 4px / 0.5 = 8px\n *   8px is the scaled measurement\n *\n * ## Overall idea of this algorithm:\n *\n * When users perform zooming by pinching the screen,\n * we need to shift the zoom subject so that the position of the zoom center is always the same.\n * The offset amount to shift the layer is the returned value.\n *\n *\n * ## How we achieve our goal:\n *\n * To retain, the zoom center position, whenever a zoom action is performed,\n * we just need to make sure the distances from all the points in the zoom subject\n * to the zoom center increases or decreases by the growth rate of the scale.\n *\n * ```\n * newDistanceAnyPointToZoomCenter = oldDistanceAnyPointToZoomCenter * (newScale/oldScale)\n * ```\n *\n * We can't calculate that for all the points because there are unlimited points on a plain.\n * However, due to the way `transform` works in RN, every point is scaled from the zoom subject center.\n * Therefore, it's sufficient to base our calculation on the distance from the zoom subject center to the zoom center.\n *\n * ```\n * newDistanceZoomSubjectCenterToZoomCenter = oldDistanceZoomSubjectCenterToZoomCenter * (newScale/oldScale)\n * ```\n *\n * Once we have `newDistanceZoomSubjectCenterToZoomCenter`,\n * we can easily calculate the position of the new center, which leads us to the offset amount.\n * Refer to the code for more details\n *\n * @param oldOffsetXOrYScaled\n * @param zoomSubjectOriginalWidthOrHeight\n * @param oldScale\n * @param newScale\n * @param zoomCenterXOrY\n */\nexport function calcNewScaledOffsetForZoomCentering(\n  oldOffsetXOrYScaled: number,\n  zoomSubjectOriginalWidthOrHeight: number,\n  oldScale: number,\n  newScale: number,\n  zoomCenterXOrY: number\n) {\n  const oldOffSetUnscaled = oldOffsetXOrYScaled * oldScale;\n  const growthRate = newScale / oldScale;\n\n  // these act like namespaces just for the sake of readability\n  const zoomSubjectOriginalCenter = {} as Center;\n  const zoomSubjectCurrentCenter = {} as Center;\n  const zoomSubjectNewCenter = {} as Center;\n\n  zoomSubjectOriginalCenter.xOrY = zoomSubjectOriginalWidthOrHeight / 2;\n  zoomSubjectCurrentCenter.xOrY =\n    zoomSubjectOriginalCenter.xOrY + oldOffSetUnscaled;\n  zoomSubjectCurrentCenter.distanceToZoomCenter =\n    zoomSubjectCurrentCenter.xOrY - zoomCenterXOrY;\n\n  zoomSubjectNewCenter.distanceToZoomCenter =\n    zoomSubjectCurrentCenter.distanceToZoomCenter * growthRate;\n  zoomSubjectNewCenter.xOrY =\n    zoomSubjectNewCenter.distanceToZoomCenter + zoomCenterXOrY;\n\n  const newOffsetUnscaled =\n    zoomSubjectNewCenter.xOrY - zoomSubjectOriginalCenter.xOrY;\n\n  return newOffsetUnscaled / newScale;\n}\n\ninterface Center {\n  xOrY: number;\n  distanceToZoomCenter: number;\n}\n","import { GestureResponderEvent, PanResponderGestureState } from 'react-native';\nimport { Vec2D } from '../typings';\n\nexport { calcNewScaledOffsetForZoomCentering } from './calcNewScaledOffsetForZoomCentering';\n\n/**\n * Calculates the gesture center point relative to the page coordinate system\n *\n * We're unable to use touch.locationX/Y\n * because locationX uses the axis system of the leaf element that the touch occurs on,\n * which makes it even more complicated to translate into our container's axis system.\n *\n * We're also unable to use gestureState.moveX/Y\n * because gestureState.moveX/Y is messed up on real device\n * (Sometimes it's the center point, but sometimes it randomly takes the position of one of the touches)\n */\nexport function calcGestureCenterPoint(\n  e: GestureResponderEvent,\n  gestureState: PanResponderGestureState\n): Vec2D | null {\n  const touches = e.nativeEvent.touches;\n  if (!touches[0]) return null;\n\n  if (gestureState.numberActiveTouches === 2) {\n    if (!touches[1]) return null;\n    return {\n      x: (touches[0].pageX + touches[1].pageX) / 2,\n      y: (touches[0].pageY + touches[1].pageY) / 2,\n    };\n  }\n  if (gestureState.numberActiveTouches === 1) {\n    return {\n      x: touches[0].pageX,\n      y: touches[0].pageY,\n    };\n  }\n\n  return null;\n}\n\nexport function calcGestureTouchDistance(\n  e: GestureResponderEvent,\n  gestureState: PanResponderGestureState\n): number | null {\n  const touches = e.nativeEvent.touches;\n  if (gestureState.numberActiveTouches !== 2 || !touches[0] || !touches[1])\n    return null;\n\n  const dx = Math.abs(touches[0].pageX - touches[1].pageX);\n  const dy = Math.abs(touches[0].pageY - touches[1].pageY);\n  return Math.sqrt(dx * dx + dy * dy);\n}\n","import React from 'react';\nimport {\n  Animated,\n  View,\n  Image,\n  StyleSheet,\n  GestureResponderEvent,\n  PanResponderGestureState,\n  PanResponder,\n  ViewProps,\n} from 'react-native';\nimport { Size2D } from 'src/typings';\n\nexport const StaticPin = ({\n  staticPinPosition,\n  pinAnim,\n  staticPinIcon,\n  pinSize,\n  onParentMove,\n  onPress,\n  onLongPress,\n  setPinSize,\n  pinProps = {},\n}: {\n  staticPinPosition: { x: number; y: number };\n  pinAnim: Animated.ValueXY;\n  staticPinIcon: React.ReactNode;\n  pinSize: Size2D;\n  /** Internal handler for passing move event to parent */\n  onParentMove: (\n    evt: GestureResponderEvent,\n    gestureState: PanResponderGestureState\n  ) => boolean | undefined;\n  onPress?: (evt: GestureResponderEvent) => void;\n  onLongPress?: (evt: GestureResponderEvent) => void;\n  setPinSize: (size: Size2D) => void;\n  pinProps?: ViewProps;\n}) => {\n  const tapTime = React.useRef(0);\n  const transform = [\n    { translateY: -pinSize.height },\n    { translateX: -pinSize.width / 2 },\n    ...pinAnim.getTranslateTransform(),\n  ];\n\n  const opacity = pinSize.width && pinSize.height ? 1 : 0;\n\n  const panResponder = React.useRef(\n    PanResponder.create({\n      onStartShouldSetPanResponder: () => {\n        tapTime.current = Date.now();\n\n        // We want to handle tap on this so set true\n        return true;\n      },\n      onPanResponderMove: (evt, gestureState) => {\n        // However if the user moves finger we want to pass this evt to parent\n        // to handle panning (tap not recognized)\n        if (Math.abs(gestureState.dx) > 5 && Math.abs(gestureState.dy) > 5)\n          onParentMove(evt, gestureState);\n      },\n      onPanResponderRelease: (evt, gestureState) => {\n        if (Math.abs(gestureState.dx) > 5 || Math.abs(gestureState.dy) > 5)\n          return;\n        const dt = Date.now() - tapTime.current;\n        if (onPress && dt < 500) {\n          onPress(evt);\n        }\n        if (onLongPress && dt > 500) {\n          // RN long press is 500ms\n          onLongPress(evt);\n        }\n      },\n    })\n  ).current;\n\n  return (\n    <Animated.View\n      style={[\n        {\n          left: staticPinPosition.x,\n          top: staticPinPosition.y,\n        },\n        styles.pinWrapper,\n        { opacity, transform },\n      ]}\n      {...pinProps}\n    >\n      <View\n        onLayout={({ nativeEvent: { layout } }) => {\n          setPinSize(layout);\n        }}\n        {...panResponder.panHandlers}\n      >\n        {staticPinIcon || (\n          // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n          <Image source={require('../assets/pin.png')} style={styles.pin} />\n        )}\n      </View>\n    </Animated.View>\n  );\n};\n\nconst styles = StyleSheet.create({\n  pin: {\n    height: 64,\n    width: 48,\n  },\n  pinWrapper: {\n    position: 'absolute',\n  },\n});\n","import React, { Component, createRef, RefObject } from 'react';\nimport {\n  Animated,\n  Easing,\n  GestureResponderEvent,\n  PanResponder,\n  PanResponderCallbacks,\n  PanResponderGestureState,\n  PanResponderInstance,\n  StyleSheet,\n  View,\n} from 'react-native';\n\nimport {\n  Vec2D,\n  ReactNativeZoomableViewProps,\n  ReactNativeZoomableViewState,\n  TouchPoint,\n  ZoomableViewEvent,\n  Size2D,\n} from './typings';\n\nimport { AnimatedTouchFeedback } from './components';\nimport { DebugTouchPoint } from './debugHelper';\nimport {\n  calcGestureCenterPoint,\n  calcGestureTouchDistance,\n  calcNewScaledOffsetForZoomCentering,\n} from './helper';\nimport { applyPanBoundariesToOffset } from './helper/applyPanBoundariesToOffset';\nimport { viewportPositionToImagePosition } from './helper/coordinateConversion';\nimport { StaticPin } from './components/StaticPin';\nimport { debounce } from 'lodash';\nimport {\n  getBoundaryCrossedAnim,\n  getPanMomentumDecayAnim,\n  getZoomToAnimation,\n} from './animations';\n\nconst initialState: ReactNativeZoomableViewState = {\n  originalWidth: 0,\n  originalHeight: 0,\n  originalPageX: 0,\n  originalPageY: 0,\n  originalX: 0,\n  originalY: 0,\n  pinSize: { width: 0, height: 0 },\n};\n\nclass ReactNativeZoomableView extends Component<\n  ReactNativeZoomableViewProps,\n  ReactNativeZoomableViewState\n> {\n  zoomSubjectWrapperRef: RefObject<View>;\n  gestureHandlers: PanResponderInstance;\n  doubleTapFirstTapReleaseTimestamp: number | undefined;\n\n  static defaultProps = {\n    zoomEnabled: true,\n    panEnabled: true,\n    initialZoom: 1,\n    initialOffsetX: 0,\n    initialOffsetY: 0,\n    maxZoom: 1.5,\n    minZoom: 0.5,\n    pinchToZoomInSensitivity: 1,\n    pinchToZoomOutSensitivity: 1,\n    movementSensibility: 1,\n    doubleTapDelay: 300,\n    bindToBorders: true,\n    zoomStep: 0.5,\n    onLongPress: null,\n    longPressDuration: 700,\n    contentWidth: undefined,\n    contentHeight: undefined,\n    panBoundaryPadding: 0,\n    visualTouchFeedbackEnabled: true,\n    staticPinPosition: undefined,\n    staticPinIcon: undefined,\n    onStaticPinPositionChange: undefined,\n    onStaticPinPositionMove: undefined,\n    animatePin: true,\n    disablePanOnInitialZoom: false,\n  };\n\n  private panAnim = new Animated.ValueXY({ x: 0, y: 0 });\n  private zoomAnim = new Animated.Value(1);\n  private pinAnim = new Animated.ValueXY({ x: 0, y: 0 });\n\n  private __offsets = {\n    x: {\n      value: 0,\n      boundaryCrossedAnimInEffect: false,\n    },\n    y: {\n      value: 0,\n      boundaryCrossedAnimInEffect: false,\n    },\n  };\n\n  private zoomLevel = 1;\n  private lastGestureCenterPosition: { x: number; y: number } | null = null;\n  private lastGestureTouchDistance: number | null = null;\n  private gestureType: 'pinch' | 'shift' | null;\n\n  private _gestureStarted = false;\n  private set gestureStarted(v: boolean) {\n    this._gestureStarted = v;\n  }\n\n  public get gestureStarted() {\n    return this._gestureStarted;\n  }\n\n  /**\n   * Last press time (used to evaluate whether user double tapped)\n   * @type {number}\n   */\n  private longPressTimeout: NodeJS.Timeout | null = null;\n  private onTransformInvocationInitialized: boolean | undefined;\n  private singleTapTimeoutId: NodeJS.Timeout | undefined;\n  private touches: TouchPoint[] = [];\n  private doubleTapFirstTap: TouchPoint | undefined;\n  private measureZoomSubjectInterval: NodeJS.Timer | undefined;\n\n  constructor(props: ReactNativeZoomableViewProps) {\n    super(props);\n    // This needs to be done before anything else to initialize the state.\n    // Otherwise, the logic below may reference the state when it's undefined,\n    // causing runtime errors.\n    this.state = { ...initialState };\n\n    this.gestureHandlers = PanResponder.create({\n      onStartShouldSetPanResponder: this._handleStartShouldSetPanResponder,\n      onPanResponderGrant: this._handlePanResponderGrant,\n      onPanResponderMove: this._handlePanResponderMove,\n      onPanResponderRelease: this._handlePanResponderEnd,\n      onPanResponderTerminate: (evt, gestureState) => {\n        // We should also call _handlePanResponderEnd\n        // to properly perform cleanups when the gesture is terminated\n        // (aka gesture handling responsibility is taken over by another component).\n        // This also fixes a weird issue where\n        // on real device, sometimes onPanResponderRelease is not called when you lift 2 fingers up,\n        // but onPanResponderTerminate is called instead for no apparent reason.\n        this._handlePanResponderEnd(evt, gestureState);\n        this.props.onPanResponderTerminate?.(\n          evt,\n          gestureState,\n          this._getZoomableViewEventObject()\n        );\n      },\n      onPanResponderTerminationRequest: (evt, gestureState) =>\n        !!this.props.onPanResponderTerminationRequest?.(\n          evt,\n          gestureState,\n          this._getZoomableViewEventObject()\n        ),\n      // Defaults to true to prevent parent components, such as React Navigation's tab view, from taking over as responder.\n      onShouldBlockNativeResponder: (evt, gestureState) =>\n        this.props.onShouldBlockNativeResponder?.(\n          evt,\n          gestureState,\n          this._getZoomableViewEventObject()\n        ) ?? true,\n      onStartShouldSetPanResponderCapture: (evt, gestureState) =>\n        !!this.props.onStartShouldSetPanResponderCapture?.(evt, gestureState),\n      onMoveShouldSetPanResponderCapture: (evt, gestureState) =>\n        !!this.props.onMoveShouldSetPanResponderCapture?.(evt, gestureState),\n    });\n\n    this.zoomSubjectWrapperRef = createRef<View>();\n\n    if (this.props.zoomAnimatedValue)\n      this.zoomAnim = this.props.zoomAnimatedValue;\n    if (this.props.panAnimatedValueXY)\n      this.panAnim = this.props.panAnimatedValueXY;\n\n    if (this.props.initialZoom) this.zoomLevel = this.props.initialZoom;\n    if (this.props.initialOffsetX != null)\n      this.offsetX = this.props.initialOffsetX;\n    if (this.props.initialOffsetY != null)\n      this.offsetY = this.props.initialOffsetY;\n\n    this.panAnim.setValue({ x: this.offsetX, y: this.offsetY });\n    this.zoomAnim.setValue(this.zoomLevel);\n    this.panAnim.addListener(({ x, y }) => {\n      this.offsetX = x;\n      this.offsetY = y;\n    });\n    this.zoomAnim.addListener(({ value }) => {\n      this.zoomLevel = value;\n    });\n\n    this.lastGestureTouchDistance = 150;\n\n    this.gestureType = null;\n  }\n\n  private raisePin() {\n    if (!this.props.animatePin) return;\n    Animated.timing(this.pinAnim, {\n      toValue: { x: 0, y: -10 },\n      useNativeDriver: true,\n      easing: Easing.out(Easing.ease),\n      duration: 100,\n    }).start();\n  }\n\n  private dropPin() {\n    if (!this.props.animatePin) return;\n    Animated.timing(this.pinAnim, {\n      toValue: { x: 0, y: 0 },\n      useNativeDriver: true,\n      easing: Easing.out(Easing.ease),\n      duration: 100,\n    }).start();\n  }\n\n  private set offsetX(x: number) {\n    this.__setOffset('x', x);\n  }\n\n  private set offsetY(y: number) {\n    this.__setOffset('y', y);\n  }\n\n  private get offsetX() {\n    return this.__getOffset('x');\n  }\n\n  private get offsetY() {\n    return this.__getOffset('y');\n  }\n\n  private __setOffset(axis: 'x' | 'y', offset: number) {\n    const offsetState = this.__offsets[axis];\n\n    if (this.props.bindToBorders) {\n      const containerSize =\n        axis === 'x' ? this.state.originalWidth : this.state.originalHeight;\n      const contentSize =\n        axis === 'x'\n          ? this.props.contentWidth || this.state.originalWidth\n          : this.props.contentHeight || this.state.originalHeight;\n\n      const boundOffset =\n        contentSize && containerSize && this.props.panBoundaryPadding != null\n          ? applyPanBoundariesToOffset(\n              offset,\n              containerSize,\n              contentSize,\n              this.zoomLevel,\n              this.props.panBoundaryPadding\n            )\n          : offset;\n\n      if (!this.gestureType && !offsetState.boundaryCrossedAnimInEffect) {\n        const boundariesApplied =\n          boundOffset !== offset &&\n          boundOffset.toFixed(3) !== offset.toFixed(3);\n        if (boundariesApplied) {\n          offsetState.boundaryCrossedAnimInEffect = true;\n          getBoundaryCrossedAnim(this.panAnim[axis], boundOffset).start(() => {\n            offsetState.boundaryCrossedAnimInEffect = false;\n          });\n          return;\n        }\n      }\n    }\n\n    offsetState.value = offset;\n  }\n\n  private __getOffset(axis: 'x' | 'y') {\n    return this.__offsets[axis].value;\n  }\n\n  componentDidUpdate(\n    prevProps: ReactNativeZoomableViewProps,\n    prevState: ReactNativeZoomableViewState\n  ) {\n    const { zoomEnabled, initialZoom } = this.props;\n    if (prevProps.zoomEnabled && !zoomEnabled && initialZoom) {\n      this.zoomLevel = initialZoom;\n      this.zoomAnim.setValue(this.zoomLevel);\n    }\n    if (\n      !this.onTransformInvocationInitialized &&\n      this._invokeOnTransform().successful\n    ) {\n      this.panAnim.addListener(() => this._invokeOnTransform());\n      this.zoomAnim.addListener(() => this._invokeOnTransform());\n      this.onTransformInvocationInitialized = true;\n    }\n\n    const currState = this.state;\n    const originalMeasurementsChanged =\n      currState.originalHeight !== prevState.originalHeight ||\n      currState.originalWidth !== prevState.originalWidth ||\n      currState.originalPageX !== prevState.originalPageX ||\n      currState.originalPageY !== prevState.originalPageY ||\n      currState.originalX !== prevState.originalX ||\n      currState.originalY !== prevState.originalY;\n\n    const staticPinPositionChanged =\n      prevProps.staticPinPosition?.x !== this.props.staticPinPosition?.x ||\n      prevProps.staticPinPosition?.y !== this.props.staticPinPosition?.y;\n\n    // We use a custom `onLayout` event, so the clients can stay in-sync\n    // with when the internal measurements are actually saved to the state,\n    // thus helping them apply transformations at more accurate timings\n    if (originalMeasurementsChanged) {\n      const layout = {\n        width: currState.originalWidth,\n        height: currState.originalHeight,\n        x: currState.originalX,\n        y: currState.originalY,\n      };\n      this.props.onLayout?.({ nativeEvent: { layout } });\n    }\n\n    if (\n      this.onTransformInvocationInitialized &&\n      (originalMeasurementsChanged || staticPinPositionChanged)\n    ) {\n      this._invokeOnTransform();\n    }\n  }\n\n  componentDidMount() {\n    this.measureZoomSubject();\n    // We've already run `grabZoomSubjectOriginalMeasurements` at various events\n    // to make sure the measurements are promptly updated.\n    // However, there might be cases we haven't accounted for, especially when\n    // native processes are involved. To account for those cases,\n    // we'll use an interval here to ensure we're always up-to-date.\n    // The `setState` in `grabZoomSubjectOriginalMeasurements` won't trigger a rerender\n    // if the values given haven't changed, so we're not running performance risk here.\n    this.measureZoomSubjectInterval = setInterval(this.measureZoomSubject, 1e3);\n  }\n\n  componentWillUnmount() {\n    this.measureZoomSubjectInterval &&\n      clearInterval(this.measureZoomSubjectInterval);\n  }\n\n  debouncedOnStaticPinPositionChange = debounce(\n    (position: Vec2D) => this.props.onStaticPinPositionChange?.(position),\n    100\n  );\n\n  /**\n   * try to invoke onTransform\n   * @private\n   */\n  _invokeOnTransform() {\n    const zoomableViewEvent = this._getZoomableViewEventObject();\n    const position = this._staticPinPosition();\n\n    if (!zoomableViewEvent.originalWidth || !zoomableViewEvent.originalHeight)\n      return { successful: false };\n\n    this.props.onTransform?.(zoomableViewEvent);\n\n    if (position) {\n      this.props.onStaticPinPositionMove?.(position);\n      this.debouncedOnStaticPinPositionChange(position);\n    }\n\n    return { successful: true };\n  }\n\n  /**\n   * Returns additional information about components current state for external event hooks\n   *\n   * @returns {{}}\n   * @private\n   */\n  _getZoomableViewEventObject(overwriteObj = {}): ZoomableViewEvent {\n    return {\n      zoomLevel: this.zoomLevel,\n      offsetX: this.offsetX,\n      offsetY: this.offsetY,\n      originalHeight: this.state.originalHeight,\n      originalWidth: this.state.originalWidth,\n      originalPageX: this.state.originalPageX,\n      originalPageY: this.state.originalPageY,\n      ...overwriteObj,\n    } as ZoomableViewEvent;\n  }\n\n  /**\n   * Get the original box dimensions and save them for later use.\n   * (They will be used to calculate boxBorders)\n   *\n   * @private\n   */\n  private measureZoomSubject = () => {\n    // make sure we measure after animations are complete\n    requestAnimationFrame(() => {\n      // this setTimeout is here to fix a weird issue on iOS where the measurements are all `0`\n      // when navigating back (react-navigation stack) from another view\n      // while closing the keyboard at the same time\n      setTimeout(() => {\n        // In normal conditions, we're supposed to measure zoomSubject instead of its wrapper.\n        // However, our zoomSubject may have been transformed by an initial zoomLevel or offset,\n        // in which case these measurements will not represent the true \"original\" measurements.\n        // We just need to make sure the zoomSubjectWrapper perfectly aligns with the zoomSubject\n        // (no border, space, or anything between them)\n        this.zoomSubjectWrapperRef.current?.measure(\n          (x, y, width, height, pageX, pageY) => {\n            // When the component is off-screen, these become all 0s, so we don't set them\n            // to avoid messing up calculations, especially ones that are done right after\n            // the component transitions from hidden to visible.\n            if (!pageX && !pageY && !width && !height) return;\n\n            // If these values are all the same, don't re-set them in state\n            // this way we don't re-render\n            if (\n              this.state.originalX === x &&\n              this.state.originalY === y &&\n              this.state.originalWidth === width &&\n              this.state.originalHeight === height &&\n              this.state.originalPageX === pageX &&\n              this.state.originalPageY === pageY\n            ) {\n              return;\n            }\n\n            this.setState({\n              originalX: x,\n              originalY: y,\n              originalWidth: width,\n              originalHeight: height,\n              originalPageX: pageX,\n              originalPageY: pageY,\n            });\n          }\n        );\n      });\n    });\n  };\n\n  /**\n   * Handles the start of touch events and checks for taps\n   *\n   * @param e\n   * @param gestureState\n   * @returns {boolean}\n   *\n   * @private\n   */\n  _handleStartShouldSetPanResponder = (\n    e: GestureResponderEvent,\n    gestureState: PanResponderGestureState\n  ) => {\n    if (this.props.onStartShouldSetPanResponder) {\n      this.props.onStartShouldSetPanResponder(\n        e,\n        gestureState,\n        this._getZoomableViewEventObject(),\n        false\n      );\n    }\n\n    // Always set pan responder on start\n    // of gesture so we can handle tap.\n    // \"Pan threshold validation\" will be handled\n    // in `onPanResponderMove` instead of in `onMoveShouldSetPanResponder`\n    return true;\n  };\n\n  /**\n   * Calculates pinch distance\n   *\n   * @param e\n   * @param gestureState\n   * @private\n   */\n  _handlePanResponderGrant: NonNullable<\n    PanResponderCallbacks['onPanResponderGrant']\n  > = (e, gestureState) => {\n    if (this.props.onLongPress) {\n      this.longPressTimeout = setTimeout(() => {\n        this.props.onLongPress?.(\n          e,\n          gestureState,\n          this._getZoomableViewEventObject()\n        );\n        this.longPressTimeout = null;\n      }, this.props.longPressDuration);\n    }\n\n    this.props.onPanResponderGrant?.(\n      e,\n      gestureState,\n      this._getZoomableViewEventObject()\n    );\n\n    this.panAnim.stopAnimation();\n    this.zoomAnim.stopAnimation();\n    this.gestureStarted = true;\n\n    this.raisePin();\n  };\n\n  /**\n   * Handles the end of touch events\n   *\n   * @param e\n   * @param gestureState\n   *\n   * @private\n   */\n  _handlePanResponderEnd: NonNullable<\n    PanResponderCallbacks['onPanResponderEnd']\n  > = (e, gestureState) => {\n    if (!this.gestureType) {\n      this._resolveAndHandleTap(e);\n    }\n\n    this.setState({ debugPoints: [] });\n\n    this.lastGestureCenterPosition = null;\n\n    const disableMomentum =\n      this.props.disableMomentum ||\n      (this.props.panEnabled &&\n        this.gestureType === 'shift' &&\n        this.props.disablePanOnInitialZoom &&\n        this.zoomLevel === this.props.initialZoom);\n\n    // Trigger final shift animation unless disablePanOnInitialZoom is set and we're on the initial zoom level\n    // or disableMomentum\n    if (!disableMomentum) {\n      getPanMomentumDecayAnim(this.panAnim, {\n        x: gestureState.vx / this.zoomLevel,\n        y: gestureState.vy / this.zoomLevel,\n      }).start();\n    }\n\n    if (this.longPressTimeout) {\n      clearTimeout(this.longPressTimeout);\n      this.longPressTimeout = null;\n    }\n\n    this.props.onPanResponderEnd?.(\n      e,\n      gestureState,\n      this._getZoomableViewEventObject()\n    );\n\n    if (this.gestureType === 'pinch') {\n      this.props.onZoomEnd?.(\n        e,\n        gestureState,\n        this._getZoomableViewEventObject()\n      );\n    } else if (this.gestureType === 'shift') {\n      this.props.onShiftingEnd?.(\n        e,\n        gestureState,\n        this._getZoomableViewEventObject()\n      );\n    }\n\n    if (this.props.staticPinPosition) {\n      this._updateStaticPin();\n    }\n\n    this.dropPin();\n\n    this.gestureType = null;\n    this.gestureStarted = false;\n  };\n\n  /**\n   * Handles the actual movement of our pan responder\n   *\n   * @param e\n   * @param gestureState\n   *\n   * @private\n   */\n  _handlePanResponderMove = (\n    e: GestureResponderEvent,\n    gestureState: PanResponderGestureState\n  ) => {\n    if (this.props.onPanResponderMove) {\n      if (\n        this.props.onPanResponderMove(\n          e,\n          gestureState,\n          this._getZoomableViewEventObject()\n        )\n      ) {\n        return false;\n      }\n    }\n\n    // Only supports 2 touches and below,\n    // any invalid number will cause the gesture to end.\n    if (gestureState.numberActiveTouches <= 2) {\n      if (!this.gestureStarted) {\n        this._handlePanResponderGrant(e, gestureState);\n      }\n    } else {\n      if (this.gestureStarted) {\n        this._handlePanResponderEnd(e, gestureState);\n      }\n      return true;\n    }\n\n    if (gestureState.numberActiveTouches === 2) {\n      if (this.longPressTimeout) {\n        clearTimeout(this.longPressTimeout);\n        this.longPressTimeout = null;\n      }\n\n      // change some measurement states when switching gesture to ensure a smooth transition\n      if (this.gestureType !== 'pinch') {\n        this.lastGestureCenterPosition = calcGestureCenterPoint(\n          e,\n          gestureState\n        );\n        this.lastGestureTouchDistance = calcGestureTouchDistance(\n          e,\n          gestureState\n        );\n      }\n      this.gestureType = 'pinch';\n      this._handlePinching(e, gestureState);\n    } else if (gestureState.numberActiveTouches === 1) {\n      if (\n        this.longPressTimeout &&\n        (Math.abs(gestureState.dx) > 5 || Math.abs(gestureState.dy) > 5)\n      ) {\n        clearTimeout(this.longPressTimeout);\n        this.longPressTimeout = null;\n      }\n      // change some measurement states when switching gesture to ensure a smooth transition\n      if (this.gestureType !== 'shift') {\n        this.lastGestureCenterPosition = calcGestureCenterPoint(\n          e,\n          gestureState\n        );\n      }\n\n      const { dx, dy } = gestureState;\n      const isShiftGesture = Math.abs(dx) > 2 || Math.abs(dy) > 2;\n      if (isShiftGesture) {\n        this.gestureType = 'shift';\n        this._handleShifting(gestureState);\n      }\n    }\n  };\n\n  /**\n   * Handles the pinch movement and zooming\n   *\n   * @param e\n   * @param gestureState\n   *\n   * @private\n   */\n  _handlePinching(\n    e: GestureResponderEvent,\n    gestureState: PanResponderGestureState\n  ) {\n    if (!this.props.zoomEnabled) return;\n\n    const {\n      maxZoom,\n      minZoom,\n      pinchToZoomInSensitivity,\n      pinchToZoomOutSensitivity,\n    } = this.props;\n\n    const distance = calcGestureTouchDistance(e, gestureState);\n\n    if (\n      this.props.onZoomBefore &&\n      this.props.onZoomBefore(\n        e,\n        gestureState,\n        this._getZoomableViewEventObject()\n      )\n    ) {\n      return;\n    }\n\n    if (!distance) return;\n    if (!this.lastGestureTouchDistance) return;\n\n    // define the new zoom level and take zoom level sensitivity into consideration\n    const zoomGrowthFromLastGestureState =\n      distance / this.lastGestureTouchDistance;\n    this.lastGestureTouchDistance = distance;\n\n    const pinchToZoomSensitivity =\n      zoomGrowthFromLastGestureState < 1\n        ? pinchToZoomOutSensitivity\n        : pinchToZoomInSensitivity;\n\n    if (pinchToZoomSensitivity == null) return;\n    const deltaGrowth = zoomGrowthFromLastGestureState - 1;\n    // 0 - no resistance\n    // 10 - 90% resistance\n    const deltaGrowthAdjustedBySensitivity =\n      deltaGrowth * (1 - (pinchToZoomSensitivity * 9) / 100);\n\n    let newZoomLevel = this.zoomLevel * (1 + deltaGrowthAdjustedBySensitivity);\n\n    // make sure max and min zoom levels are respected\n    if (maxZoom != null && newZoomLevel > maxZoom) {\n      newZoomLevel = maxZoom;\n    }\n\n    if (minZoom != null && newZoomLevel < minZoom) {\n      newZoomLevel = minZoom;\n    }\n\n    const gestureCenterPoint = calcGestureCenterPoint(e, gestureState);\n\n    if (!gestureCenterPoint) return;\n\n    let zoomCenter = {\n      x: gestureCenterPoint.x - this.state.originalPageX,\n      y: gestureCenterPoint.y - this.state.originalPageY,\n    };\n\n    if (this.props.staticPinPosition) {\n      // When we use a static pin position, the zoom centre is the same as that position,\n      // otherwise the pin moves around way too much while zooming.\n      zoomCenter = {\n        x: this.props.staticPinPosition.x,\n        y: this.props.staticPinPosition.y,\n      };\n    }\n\n    // Uncomment to debug\n    this.props.debug && this._setPinchDebugPoints(e, zoomCenter);\n\n    const { originalHeight, originalWidth } = this.state;\n\n    const oldOffsetX = this.offsetX;\n    const oldOffsetY = this.offsetY;\n    const oldScale = this.zoomLevel;\n    const newScale = newZoomLevel;\n\n    if (!originalHeight || !originalWidth) return;\n\n    let offsetY = calcNewScaledOffsetForZoomCentering(\n      oldOffsetY,\n      originalHeight,\n      oldScale,\n      newScale,\n      zoomCenter.y\n    );\n    let offsetX = calcNewScaledOffsetForZoomCentering(\n      oldOffsetX,\n      originalWidth,\n      oldScale,\n      newScale,\n      zoomCenter.x\n    );\n\n    const offsetShift =\n      this._calcOffsetShiftSinceLastGestureState(gestureCenterPoint);\n    if (offsetShift) {\n      offsetX += offsetShift.x;\n      offsetY += offsetShift.y;\n    }\n\n    this.offsetX = offsetX;\n    this.offsetY = offsetY;\n    this.zoomLevel = newScale;\n\n    this.panAnim.setValue({ x: this.offsetX, y: this.offsetY });\n    this.zoomAnim.setValue(this.zoomLevel);\n\n    this.props.onZoomAfter?.(\n      e,\n      gestureState,\n      this._getZoomableViewEventObject()\n    );\n  }\n\n  /**\n   * Used to debug pinch events\n   * @param gestureResponderEvent\n   * @param zoomCenter\n   * @param points\n   */\n  _setPinchDebugPoints(\n    gestureResponderEvent: GestureResponderEvent,\n    zoomCenter: Vec2D,\n    ...points: Vec2D[]\n  ) {\n    const { touches } = gestureResponderEvent.nativeEvent;\n    const { originalPageY, originalPageX } = this.state;\n\n    this.setState({\n      debugPoints: [\n        {\n          x: touches[0].pageX - originalPageX,\n          y: touches[0].pageY - originalPageY,\n        },\n        {\n          x: touches[1].pageX - originalPageX,\n          y: touches[1].pageY - originalPageY,\n        },\n        zoomCenter,\n        ...points,\n      ],\n    });\n  }\n\n  /**\n   * Calculates the amount the offset should shift since the last position during panning\n   *\n   * @param {Vec2D} gestureCenterPoint\n   *\n   * @private\n   */\n  _calcOffsetShiftSinceLastGestureState(gestureCenterPoint: Vec2D) {\n    const { movementSensibility } = this.props;\n\n    let shift = null;\n\n    if (this.lastGestureCenterPosition && movementSensibility) {\n      const dx = gestureCenterPoint.x - this.lastGestureCenterPosition.x;\n      const dy = gestureCenterPoint.y - this.lastGestureCenterPosition.y;\n\n      const shiftX = dx / this.zoomLevel / movementSensibility;\n      const shiftY = dy / this.zoomLevel / movementSensibility;\n\n      shift = {\n        x: shiftX,\n        y: shiftY,\n      };\n    }\n\n    this.lastGestureCenterPosition = gestureCenterPoint;\n\n    return shift;\n  }\n\n  /**\n   * Handles movement by tap and move\n   *\n   * @param gestureState\n   *\n   * @private\n   */\n  _handleShifting(gestureState: PanResponderGestureState) {\n    // Skips shifting if panEnabled is false or disablePanOnInitialZoom is true and we're on the initial zoom level\n    if (\n      !this.props.panEnabled ||\n      (this.props.disablePanOnInitialZoom &&\n        this.zoomLevel === this.props.initialZoom)\n    ) {\n      return;\n    }\n    const shift = this._calcOffsetShiftSinceLastGestureState({\n      x: gestureState.moveX,\n      y: gestureState.moveY,\n    });\n    if (!shift) return;\n\n    const offsetX = this.offsetX + shift.x;\n    const offsetY = this.offsetY + shift.y;\n\n    if (\n      this.props.debug &&\n      this.state.originalPageX &&\n      this.state.originalPageY\n    ) {\n      const x = gestureState.moveX - this.state.originalPageX;\n      const y = gestureState.moveY - this.state.originalPageY;\n      this.setState({ debugPoints: [{ x, y }] });\n    }\n\n    this._setNewOffsetPosition(offsetX, offsetY);\n\n    this.raisePin();\n  }\n\n  /**\n   * Set the state to offset moved\n   *\n   * @param {number} newOffsetX\n   * @param {number} newOffsetY\n   * @returns\n   */\n  _setNewOffsetPosition(newOffsetX: number, newOffsetY: number) {\n    const { onShiftingBefore, onShiftingAfter } = this.props;\n\n    if (onShiftingBefore?.(null, null, this._getZoomableViewEventObject())) {\n      return;\n    }\n\n    this.offsetX = newOffsetX;\n    this.offsetY = newOffsetY;\n\n    this.panAnim.setValue({ x: this.offsetX, y: this.offsetY });\n    this.zoomAnim.setValue(this.zoomLevel);\n\n    onShiftingAfter?.(null, null, this._getZoomableViewEventObject());\n  }\n\n  /**\n   * Check whether the press event is double tap\n   * or single tap and handle the event accordingly\n   *\n   * @param e\n   *\n   * @private\n   */\n  private _resolveAndHandleTap = (e: GestureResponderEvent) => {\n    const now = Date.now();\n    if (\n      this.doubleTapFirstTapReleaseTimestamp &&\n      this.props.doubleTapDelay &&\n      now - this.doubleTapFirstTapReleaseTimestamp < this.props.doubleTapDelay\n    ) {\n      this.doubleTapFirstTap &&\n        this._addTouch({\n          ...this.doubleTapFirstTap,\n          id: now.toString(),\n          isSecondTap: true,\n        });\n      this.singleTapTimeoutId && clearTimeout(this.singleTapTimeoutId);\n      delete this.doubleTapFirstTapReleaseTimestamp;\n      delete this.singleTapTimeoutId;\n      delete this.doubleTapFirstTap;\n      this._handleDoubleTap(e);\n    } else {\n      this.doubleTapFirstTapReleaseTimestamp = now;\n      this.doubleTapFirstTap = {\n        id: now.toString(),\n        x: e.nativeEvent.pageX - this.state.originalPageX,\n        y: e.nativeEvent.pageY - this.state.originalPageY,\n      };\n      this._addTouch(this.doubleTapFirstTap);\n\n      // persist event so e.nativeEvent is preserved after a timeout delay\n      e.persist();\n      this.singleTapTimeoutId = setTimeout(() => {\n        delete this.doubleTapFirstTapReleaseTimestamp;\n        delete this.singleTapTimeoutId;\n\n        // Pan to the tapped location\n        if (this.props.staticPinPosition && this.doubleTapFirstTap) {\n          const tapX =\n            this.props.staticPinPosition.x - this.doubleTapFirstTap.x;\n          const tapY =\n            this.props.staticPinPosition.y - this.doubleTapFirstTap.y;\n\n          Animated.timing(this.panAnim, {\n            toValue: {\n              x: this.offsetX + tapX / this.zoomLevel,\n              y: this.offsetY + tapY / this.zoomLevel,\n            },\n            useNativeDriver: true,\n            duration: 200,\n          }).start(() => {\n            this._updateStaticPin();\n          });\n        }\n\n        this.props.onSingleTap?.(e, this._getZoomableViewEventObject());\n      }, this.props.doubleTapDelay);\n    }\n  };\n\n  moveStaticPinTo = (position: Vec2D, duration?: number) => {\n    const { originalWidth, originalHeight } = this.state;\n    const { staticPinPosition, contentWidth, contentHeight } = this.props;\n\n    if (!staticPinPosition) return;\n    if (!originalWidth || !originalHeight) return;\n    if (!contentWidth || !contentHeight) return;\n\n    // Offset for the static pin\n    const pinX = staticPinPosition.x - originalWidth / 2;\n    const pinY = staticPinPosition.y - originalHeight / 2;\n\n    this.offsetX = contentWidth / 2 - position.x + pinX / this.zoomLevel;\n    this.offsetY = contentHeight / 2 - position.y + pinY / this.zoomLevel;\n\n    if (duration) {\n      Animated.timing(this.panAnim, {\n        toValue: { x: this.offsetX, y: this.offsetY },\n        useNativeDriver: true,\n        duration,\n      }).start();\n    } else {\n      this.panAnim.setValue({ x: this.offsetX, y: this.offsetY });\n    }\n  };\n\n  private _staticPinPosition = () => {\n    if (!this.props.staticPinPosition) return;\n    if (!this.props.contentWidth || !this.props.contentHeight) return;\n\n    return viewportPositionToImagePosition({\n      viewportPosition: {\n        x: this.props.staticPinPosition.x,\n        y: this.props.staticPinPosition.y,\n      },\n      imageSize: {\n        height: this.props.contentHeight,\n        width: this.props.contentWidth,\n      },\n      zoomableEvent: {\n        ...this._getZoomableViewEventObject(),\n        offsetX: this.offsetX,\n        offsetY: this.offsetY,\n        zoomLevel: this.zoomLevel,\n      },\n    });\n  };\n\n  private _updateStaticPin = () => {\n    const position = this._staticPinPosition();\n    if (!position) return;\n    this.props.onStaticPinPositionChange?.(position);\n  };\n\n  private _addTouch(touch: TouchPoint) {\n    this.touches.push(touch);\n    this.setState({ touches: [...this.touches] });\n  }\n\n  private _removeTouch(touch: TouchPoint) {\n    this.touches.splice(this.touches.indexOf(touch), 1);\n    this.setState({ touches: [...this.touches] });\n  }\n\n  /**\n   * Handles the double tap event\n   *\n   * @param e\n   *\n   * @private\n   */\n  _handleDoubleTap(e: GestureResponderEvent) {\n    const { onDoubleTapBefore, onDoubleTapAfter, doubleTapZoomToCenter } =\n      this.props;\n\n    onDoubleTapBefore?.(e, this._getZoomableViewEventObject());\n\n    const nextZoomStep = this._getNextZoomStep();\n    if (nextZoomStep == null) return;\n\n    const { originalPageX, originalPageY } = this.state;\n\n    // define new zoom position coordinates\n    const zoomPositionCoordinates = {\n      x: e.nativeEvent.pageX - originalPageX,\n      y: e.nativeEvent.pageY - originalPageY,\n    };\n\n    // if doubleTapZoomToCenter enabled -> always zoom to center instead\n    if (doubleTapZoomToCenter) {\n      zoomPositionCoordinates.x = 0;\n      zoomPositionCoordinates.y = 0;\n    }\n\n    this.zoomTo(nextZoomStep, zoomPositionCoordinates);\n\n    onDoubleTapAfter?.(\n      e,\n      this._getZoomableViewEventObject({ zoomLevel: nextZoomStep })\n    );\n  }\n\n  /**\n   * Returns the next zoom step based on current step and zoomStep property.\n   * If we are zoomed all the way in -> return to initialzoom\n   *\n   * @returns {*}\n   */\n  _getNextZoomStep() {\n    const { zoomStep, maxZoom, initialZoom } = this.props;\n    const { zoomLevel } = this;\n\n    if (maxZoom == null) return;\n\n    if (zoomLevel.toFixed(2) === maxZoom.toFixed(2)) {\n      return initialZoom;\n    }\n\n    if (zoomStep == null) return;\n\n    const nextZoomStep = zoomLevel * (1 + zoomStep);\n    if (nextZoomStep > maxZoom) {\n      return maxZoom;\n    }\n\n    return nextZoomStep;\n  }\n\n  /**\n   * Zooms to a specific level. A \"zoom center\" can be provided, which specifies\n   * the point that will remain in the same position on the screen after the zoom.\n   * The coordinates of the zoom center is relative to the zoom subject.\n   * { x: 0, y: 0 } is the very center of the zoom subject.\n   *\n   * @param newZoomLevel\n   * @param zoomCenter - If not supplied, the container's center is the zoom center\n   */\n  zoomTo(newZoomLevel: number, zoomCenter?: Vec2D) {\n    if (!this.props.zoomEnabled) return false;\n    if (this.props.maxZoom && newZoomLevel > this.props.maxZoom) return false;\n    if (this.props.minZoom && newZoomLevel < this.props.minZoom) return false;\n\n    this.props.onZoomBefore?.(null, null, this._getZoomableViewEventObject());\n\n    // == Perform Pan Animation to preserve the zoom center while zooming ==\n    let listenerId = '';\n    if (zoomCenter) {\n      // Calculates panAnim values based on changes in zoomAnim.\n      let prevScale = this.zoomLevel;\n      // Since zoomAnim is calculated in native driver,\n      //  it will jitter panAnim once in a while,\n      //  because here panAnim is being calculated in js.\n      // However the jittering should mostly occur in simulator.\n      listenerId = this.zoomAnim.addListener(({ value: newScale }) => {\n        this.panAnim.setValue({\n          x: calcNewScaledOffsetForZoomCentering(\n            this.offsetX,\n            this.state.originalWidth,\n            prevScale,\n            newScale,\n            zoomCenter.x\n          ),\n          y: calcNewScaledOffsetForZoomCentering(\n            this.offsetY,\n            this.state.originalHeight,\n            prevScale,\n            newScale,\n            zoomCenter.y\n          ),\n        });\n        prevScale = newScale;\n      });\n    }\n\n    // == Perform Zoom Animation ==\n    getZoomToAnimation(this.zoomAnim, newZoomLevel).start(() => {\n      this.zoomAnim.removeListener(listenerId);\n    });\n    // == Zoom Animation Ends ==\n\n    this.props.onZoomAfter?.(null, null, this._getZoomableViewEventObject());\n    return true;\n  }\n\n  /**\n   * Zooms in or out by a specified change level\n   * Use a positive number for `zoomLevelChange` to zoom in\n   * Use a negative number for `zoomLevelChange` to zoom out\n   *\n   * Returns a promise if everything was updated and a boolean, whether it could be updated or if it exceeded the min/max zoom limits.\n   *\n   * @param {number | null} zoomLevelChange\n   *\n   * @return {bool}\n   */\n  zoomBy(zoomLevelChange: number) {\n    // if no zoom level Change given -> just use zoom step\n    zoomLevelChange ||= this.props.zoomStep || 0;\n    return this.zoomTo(this.zoomLevel + zoomLevelChange);\n  }\n\n  /**\n   * Moves the zoomed view to a specified position\n   * Returns a promise when finished\n   *\n   * @param {number} newOffsetX the new position we want to move it to (x-axis)\n   * @param {number} newOffsetY the new position we want to move it to (y-axis)\n   *\n   * @return {bool}\n   */\n  moveTo(newOffsetX: number, newOffsetY: number) {\n    const { originalWidth, originalHeight } = this.state;\n    if (!originalWidth || !originalHeight) return;\n\n    const offsetX = (newOffsetX - originalWidth / 2) / this.zoomLevel;\n    const offsetY = (newOffsetY - originalHeight / 2) / this.zoomLevel;\n\n    this._setNewOffsetPosition(-offsetX, -offsetY);\n  }\n\n  /**\n   * Moves the zoomed view by a certain amount.\n   *\n   * Returns a promise when finished\n   *\n   * @param {number} offsetChangeX the amount we want to move the offset by (x-axis)\n   * @param {number} offsetChangeY the amount we want to move the offset by (y-axis)\n   *\n   * @return {bool}\n   */\n  moveBy(offsetChangeX: number, offsetChangeY: number) {\n    const offsetX =\n      (this.offsetX * this.zoomLevel - offsetChangeX) / this.zoomLevel;\n    const offsetY =\n      (this.offsetY * this.zoomLevel - offsetChangeY) / this.zoomLevel;\n\n    this._setNewOffsetPosition(offsetX, offsetY);\n  }\n\n  render() {\n    const {\n      staticPinIcon,\n      children,\n      visualTouchFeedbackEnabled,\n      doubleTapDelay,\n      staticPinPosition,\n      onStaticPinLongPress,\n      onStaticPinPress,\n      pinProps,\n    } = this.props;\n\n    const { pinSize, touches, debugPoints = [] } = this.state;\n\n    return (\n      <View\n        style={styles.container}\n        {...this.gestureHandlers.panHandlers}\n        ref={this.zoomSubjectWrapperRef}\n        onLayout={this.measureZoomSubject}\n      >\n        <Animated.View\n          style={[\n            styles.zoomSubject,\n            this.props.style,\n            {\n              transform: [\n                { scale: this.zoomAnim },\n                ...this.panAnim.getTranslateTransform(),\n              ],\n            },\n          ]}\n        >\n          {children}\n        </Animated.View>\n\n        {visualTouchFeedbackEnabled &&\n          touches?.map(\n            (touch) =>\n              doubleTapDelay && (\n                <AnimatedTouchFeedback\n                  x={touch.x}\n                  y={touch.y}\n                  key={touch.id}\n                  animationDuration={doubleTapDelay}\n                  onAnimationDone={() => {\n                    this._removeTouch(touch);\n                  }}\n                />\n              )\n          )}\n\n        {/* For Debugging Only */}\n        {debugPoints.map(({ x, y }, index) => {\n          return <DebugTouchPoint key={index} x={x} y={y} />;\n        })}\n\n        {staticPinPosition && (\n          <StaticPin\n            staticPinIcon={staticPinIcon}\n            staticPinPosition={staticPinPosition}\n            pinSize={pinSize}\n            onPress={onStaticPinPress}\n            onLongPress={onStaticPinLongPress}\n            onParentMove={this._handlePanResponderMove}\n            pinAnim={this.pinAnim}\n            setPinSize={(size: Size2D) => {\n              this.setState({ pinSize: size });\n            }}\n            pinProps={pinProps}\n          />\n        )}\n      </View>\n    );\n  }\n}\n\nconst styles = StyleSheet.create({\n  container: {\n    alignItems: 'center',\n    flex: 1,\n    justifyContent: 'center',\n    overflow: 'hidden',\n    position: 'relative',\n  },\n  zoomSubject: {\n    alignItems: 'center',\n    flex: 1,\n    justifyContent: 'center',\n    width: '100%',\n  },\n});\n\nexport default ReactNativeZoomableView;\n\nexport { ReactNativeZoomableView };\n","import { Animated, Easing } from 'react-native';\nimport { Vec2D } from '../typings';\n\nexport function getBoundaryCrossedAnim(\n  animValue: Animated.Value,\n  toValue: number\n) {\n  return Animated.spring(animValue, {\n    overshootClamping: true,\n    toValue,\n    useNativeDriver: true,\n  });\n}\n\nexport function getPanMomentumDecayAnim(\n  animValue: Animated.Value | Animated.ValueXY,\n  velocity: number | Vec2D\n) {\n  return Animated.decay(animValue, {\n    velocity,\n    deceleration: 0.994,\n    useNativeDriver: true,\n  });\n}\n\nexport function getZoomToAnimation(animValue: Animated.Value, toValue: number) {\n  return Animated.timing(animValue, {\n    easing: Easing.out(Easing.ease),\n    toValue,\n    useNativeDriver: true,\n  });\n}\n","/**\n * Takes a single offset value and calculates the correct offset value\n * to make sure it's within the pan boundaries\n *\n *\n * @param offsetScaled\n * @param containerSize\n * @param contentSize\n * @param scale\n * @param boundaryPadding - see README\n *\n * @returns {number}\n */\nexport function applyPanBoundariesToOffset(\n  offsetScaled: number,\n  containerSize: number,\n  contentSize: number,\n  scale: number,\n  boundaryPadding: number\n) {\n  const contentSizeUnscaled = contentSize * scale;\n  const offsetUnscaled = offsetScaled * scale;\n\n  const contentStartBorderUnscaled =\n    containerSize / 2 + offsetUnscaled - contentSizeUnscaled / 2;\n  const contentEndBorderUnscaled =\n    contentStartBorderUnscaled + contentSizeUnscaled;\n\n  const containerStartBorder = 0;\n  const containerEndBorder = containerStartBorder + containerSize;\n\n  // do not let boundary padding be greater than the container size or less than 0\n  if (!boundaryPadding || boundaryPadding < 0) boundaryPadding = 0;\n  if (boundaryPadding > containerSize) boundaryPadding = containerSize;\n\n  // Calculate container's measurements with boundary padding applied.\n  // this should shrink the container's size by the amount of the boundary padding,\n  // so that the content inside can be panned a bit further away from the original container's boundaries.\n  const paddedContainerSize = containerSize - boundaryPadding * 2;\n  const paddedContainerStartBorder = containerStartBorder + boundaryPadding;\n  const paddedContainerEndBorder = containerEndBorder - boundaryPadding;\n\n  // if content is smaller than the padded container,\n  // don't let the content move\n  if (contentSizeUnscaled < paddedContainerSize) {\n    return 0;\n  }\n\n  // if content is larger than the padded container,\n  // don't let the padded container go outside of content\n\n  // maximum distance the content's center can move from its original position.\n  // assuming the content original center is the container's center.\n  const contentMaxOffsetScaled =\n    (paddedContainerSize / 2 - contentSizeUnscaled / 2) / scale;\n\n  if (\n    // content reaching the end boundary\n    contentEndBorderUnscaled < paddedContainerEndBorder\n  ) {\n    return contentMaxOffsetScaled;\n  }\n  if (\n    // content reaching the start boundary\n    contentStartBorderUnscaled > paddedContainerStartBorder\n  ) {\n    return -contentMaxOffsetScaled;\n  }\n\n  return offsetScaled;\n}\n"]}