UNPKG

@cometchat/chat-uikit-react-native

Version:

Ready-to-use Chat UI Components for React Native

129 lines (108 loc) 4.46 kB
import { Animated, Easing, GestureResponderEvent, PanResponderGestureState } from "react-native"; import { Vec2D } from "./types"; export function calcGestureTouchDistance( e: GestureResponderEvent, gestureState: PanResponderGestureState ): number | null { const touches = e?.nativeEvent?.touches; if (gestureState.numberActiveTouches !== 2 || !touches[0] || !touches[1]) return null; const dx = Math.abs(touches[0].pageX - touches[1].pageX); const dy = Math.abs(touches[0].pageY - touches[1].pageY); return Math.sqrt(dx * dx + dy * dy); } export function calcNewScaledOffsetForZoomCentering( oldOffsetXOrYScaled: number, zoomSubjectOriginalWidthOrHeight: number, oldScale: number, newScale: number, zoomCenterXOrY: number ) { const oldOffSetUnscaled = oldOffsetXOrYScaled * oldScale; const growthRate = newScale / oldScale; // these act like namespaces just for the sake of readability const zoomSubjectOriginalCenter = {} as Center; const zoomSubjectCurrentCenter = {} as Center; const zoomSubjectNewCenter = {} as Center; zoomSubjectOriginalCenter.xOrY = zoomSubjectOriginalWidthOrHeight / 2; zoomSubjectCurrentCenter.xOrY = zoomSubjectOriginalCenter.xOrY + oldOffSetUnscaled; zoomSubjectCurrentCenter.distanceToZoomCenter = zoomSubjectCurrentCenter.xOrY - zoomCenterXOrY; zoomSubjectNewCenter.distanceToZoomCenter = zoomSubjectCurrentCenter.distanceToZoomCenter * growthRate; zoomSubjectNewCenter.xOrY = zoomSubjectNewCenter.distanceToZoomCenter + zoomCenterXOrY; const newOffsetUnscaled = zoomSubjectNewCenter.xOrY - zoomSubjectOriginalCenter.xOrY; return newOffsetUnscaled / newScale; } interface Center { xOrY: number; distanceToZoomCenter: number; } export function applyPanBoundariesToOffset( offsetScaled: number, containerSize: number, contentSize: number, scale: number, boundaryPadding: number ) { const contentSizeUnscaled = contentSize * scale; const offsetUnscaled = offsetScaled * scale; const contentStartBorderUnscaled = containerSize / 2 + offsetUnscaled - contentSizeUnscaled / 2; const contentEndBorderUnscaled = contentStartBorderUnscaled + contentSizeUnscaled; const containerStartBorder = 0; const containerEndBorder = containerStartBorder + containerSize; // do not let boundary padding be greater than the container size or less than 0 if (!boundaryPadding || boundaryPadding < 0) boundaryPadding = 0; if (boundaryPadding > containerSize) boundaryPadding = containerSize; // Calculate container's measurements with boundary padding applied. // this should shrink the container's size by the amount of the boundary padding, // so that the content inside can be panned a bit further away from the original container's boundaries. const paddedContainerSize = containerSize - boundaryPadding * 2; const paddedContainerStartBorder = containerStartBorder + boundaryPadding; const paddedContainerEndBorder = containerEndBorder - boundaryPadding; // if content is smaller than the padded container, // don't let the content move if (contentSizeUnscaled < paddedContainerSize) { return 0; } // if content is larger than the padded container, // don't let the padded container go outside of content // maximum distance the content's center can move from its original position. // assuming the content original center is the container's center. const contentMaxOffsetScaled = (paddedContainerSize / 2 - contentSizeUnscaled / 2) / scale; if ( // content reaching the end boundary contentEndBorderUnscaled < paddedContainerEndBorder ) { return contentMaxOffsetScaled; } if ( // content reaching the start boundary contentStartBorderUnscaled > paddedContainerStartBorder ) { return -contentMaxOffsetScaled; } return offsetScaled; } export function getBoundaryCrossedAnim(animValue: Animated.Value, toValue: number) { return Animated.spring(animValue, { overshootClamping: true, toValue, useNativeDriver: true, }); } export function getPanMomentumDecayAnim( animValue: Animated.Value | Animated.ValueXY, velocity: number | Vec2D ) { return Animated.decay(animValue, { velocity, deceleration: 0.994, useNativeDriver: true, }); } export function getZoomToAnimation(animValue: Animated.Value, toValue: number) { return Animated.timing(animValue, { easing: Easing.out(Easing.ease), toValue, useNativeDriver: true, }); }