diff options
author | dan <dan.abramov@gmail.com> | 2024-11-06 00:21:35 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-11-06 00:21:35 +0000 |
commit | 206df2ab801d211a412f9ce3694d90bdd053caaa (patch) | |
tree | e185c0694eba262c48bd08fc0f430dd0fb203a47 /src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.android.tsx | |
parent | 6b826fb88dd33fe594fd7bb631a90d1a1713d0df (diff) | |
download | voidsky-206df2ab801d211a412f9ce3694d90bdd053caaa.tar.zst |
Remove SCREEN from lightbox layout (#6124)
* Assign an ID to lightbox and use it as a key * Consolidate lightbox props into an object * Remove unused prop * Move SafeAreaView declaration * Keep SafeAreaView always mounted When exploring Android animation, I noticed its content jumps on the first frame. I think this should help prevent that. * Pass safe area down for measurement * Remove dependency on SCREEN in Android event handlers * Remove dependency on SCREEN in iOS event handlers * Remove dependency on SCREEN on iOS * Remove dependency on SCREEN on Android * Remove dependency on JS calc in controls * Use flex for iOS layout
Diffstat (limited to 'src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.android.tsx')
-rw-r--r-- | src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.android.tsx | 83 |
1 files changed, 48 insertions, 35 deletions
diff --git a/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.android.tsx b/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.android.tsx index 487acf931..ea77ec273 100644 --- a/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.android.tsx +++ b/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.android.tsx @@ -1,7 +1,9 @@ import React, {useState} from 'react' -import {ActivityIndicator, Dimensions, StyleSheet} from 'react-native' +import {ActivityIndicator, StyleSheet, View} from 'react-native' import {Gesture, GestureDetector} from 'react-native-gesture-handler' import Animated, { + AnimatedRef, + measure, runOnJS, useAnimatedReaction, useAnimatedRef, @@ -24,13 +26,6 @@ import { TransformMatrix, } from '../../transforms' -const windowDim = Dimensions.get('window') -const screenDim = Dimensions.get('screen') -const statusBarHeight = windowDim.height - screenDim.height -const SCREEN = { - width: windowDim.width, - height: windowDim.height + statusBarHeight, -} const MIN_DOUBLE_TAP_SCALE = 2 const MAX_ORIGINAL_IMAGE_ZOOM = 2 @@ -43,6 +38,7 @@ type Props = { onZoom: (isZoomed: boolean) => void isScrollViewBeingDragged: boolean showControls: boolean + safeAreaRef: AnimatedRef<View> } const ImageItem = ({ imageSrc, @@ -50,6 +46,7 @@ const ImageItem = ({ onZoom, onRequestClose, isScrollViewBeingDragged, + safeAreaRef, }: Props) => { const [isScaled, setIsScaled] = useState(false) const [imageAspect, imageDimensions] = useImageDimensions({ @@ -102,10 +99,10 @@ const ImageItem = ({ const [translateX, translateY, scale] = readTransform(t) const dismissDistance = dismissSwipeTranslateY.value - const dismissProgress = Math.min( - Math.abs(dismissDistance) / (SCREEN.height / 2), - 1, - ) + const screenSize = measure(safeAreaRef) + const dismissProgress = screenSize + ? Math.min(Math.abs(dismissDistance) / (screenSize.height / 2), 1) + : 0 return { opacity: 1 - dismissProgress, transform: [ @@ -120,6 +117,7 @@ const ImageItem = ({ // If the user tried to pan too hard, this function will provide the negative panning to stay in bounds. function getExtraTranslationToStayInBounds( candidateTransform: TransformMatrix, + screenSize: {width: number; height: number}, ) { 'worklet' if (!imageAspect) { @@ -127,16 +125,20 @@ const ImageItem = ({ } const [nextTranslateX, nextTranslateY, nextScale] = readTransform(candidateTransform) - const scaledDimensions = getScaledDimensions(imageAspect, nextScale) + const scaledDimensions = getScaledDimensions( + imageAspect, + nextScale, + screenSize, + ) const clampedTranslateX = clampTranslation( nextTranslateX, scaledDimensions.width, - SCREEN.width, + screenSize.width, ) const clampedTranslateY = clampTranslation( nextTranslateY, scaledDimensions.height, - SCREEN.height, + screenSize.height, ) const dx = clampedTranslateX - nextTranslateX const dy = clampedTranslateY - nextTranslateY @@ -146,21 +148,26 @@ const ImageItem = ({ const pinch = Gesture.Pinch() .onStart(e => { 'worklet' + const screenSize = measure(safeAreaRef) + if (!screenSize) { + return + } pinchOrigin.value = { - x: e.focalX - SCREEN.width / 2, - y: e.focalY - SCREEN.height / 2, + x: e.focalX - screenSize.width / 2, + y: e.focalY - screenSize.height / 2, } }) .onChange(e => { 'worklet' - if (!imageDimensions) { + const screenSize = measure(safeAreaRef) + if (!imageDimensions || !screenSize) { return } // Don't let the picture zoom in so close that it gets blurry. // Also, like in stock Android apps, don't let the user zoom out further than 1:1. const [, , committedScale] = readTransform(committedTransform.value) const maxCommittedScale = - (imageDimensions.width / SCREEN.width) * MAX_ORIGINAL_IMAGE_ZOOM + (imageDimensions.width / screenSize.width) * MAX_ORIGINAL_IMAGE_ZOOM const minPinchScale = 1 / committedScale const maxPinchScale = maxCommittedScale / committedScale const nextPinchScale = Math.min( @@ -175,7 +182,7 @@ const ImageItem = ({ prependPan(t, panTranslation.value) prependPinch(t, nextPinchScale, pinchOrigin.value, pinchTranslation.value) prependTransform(t, committedTransform.value) - const [dx, dy] = getExtraTranslationToStayInBounds(t) + const [dx, dy] = getExtraTranslationToStayInBounds(t, screenSize) if (dx !== 0 || dy !== 0) { pinchTranslation.value = { x: pinchTranslation.value.x + dx, @@ -209,9 +216,11 @@ const ImageItem = ({ .minPointers(isScaled ? 1 : 2) .onChange(e => { 'worklet' - if (!imageDimensions) { + const screenSize = measure(safeAreaRef) + if (!imageDimensions || !screenSize) { return } + const nextPanTranslation = {x: e.translationX, y: e.translationY} let t = createTransform() prependPan(t, nextPanTranslation) @@ -224,7 +233,7 @@ const ImageItem = ({ prependTransform(t, committedTransform.value) // Prevent panning from going out of bounds. - const [dx, dy] = getExtraTranslationToStayInBounds(t) + const [dx, dy] = getExtraTranslationToStayInBounds(t, screenSize) nextPanTranslation.x += dx nextPanTranslation.y += dy panTranslation.value = nextPanTranslation @@ -251,7 +260,8 @@ const ImageItem = ({ .numberOfTaps(2) .onEnd(e => { 'worklet' - if (!imageDimensions || !imageAspect) { + const screenSize = measure(safeAreaRef) + if (!imageDimensions || !imageAspect || !screenSize) { return } const [, , committedScale] = readTransform(committedTransform.value) @@ -263,7 +273,7 @@ const ImageItem = ({ } // Try to zoom in so that we get rid of the black bars (whatever the orientation was). - const screenAspect = SCREEN.width / SCREEN.height + const screenAspect = screenSize.width / screenSize.height const candidateScale = Math.max( imageAspect / screenAspect, screenAspect / imageAspect, @@ -271,20 +281,23 @@ const ImageItem = ({ ) // But don't zoom in so close that the picture gets blurry. const maxScale = - (imageDimensions.width / SCREEN.width) * MAX_ORIGINAL_IMAGE_ZOOM + (imageDimensions.width / screenSize.width) * MAX_ORIGINAL_IMAGE_ZOOM const scale = Math.min(candidateScale, maxScale) // Calculate where we would be if the user pinched into the double tapped point. // We won't use this transform directly because it may go out of bounds. const candidateTransform = createTransform() const origin = { - x: e.absoluteX - SCREEN.width / 2, - y: e.absoluteY - SCREEN.height / 2, + x: e.absoluteX - screenSize.width / 2, + y: e.absoluteY - screenSize.height / 2, } prependPinch(candidateTransform, scale, origin, {x: 0, y: 0}) // Now we know how much we went out of bounds, so we can shoot correctly. - const [dx, dy] = getExtraTranslationToStayInBounds(candidateTransform) + const [dx, dy] = getExtraTranslationToStayInBounds( + candidateTransform, + screenSize, + ) const finalTransform = createTransform() prependPinch(finalTransform, scale, origin, {x: dx, y: dy}) committedTransform.value = withClampedSpring(finalTransform) @@ -348,8 +361,7 @@ const ImageItem = ({ const styles = StyleSheet.create({ container: { - width: SCREEN.width, - height: SCREEN.height, + height: '100%', overflow: 'hidden', }, image: { @@ -367,19 +379,20 @@ const styles = StyleSheet.create({ function getScaledDimensions( imageAspect: number, scale: number, + screenSize: {width: number; height: number}, ): ImageDimensions { 'worklet' - const screenAspect = SCREEN.width / SCREEN.height + const screenAspect = screenSize.width / screenSize.height const isLandscape = imageAspect > screenAspect if (isLandscape) { return { - width: scale * SCREEN.width, - height: (scale * SCREEN.width) / imageAspect, + width: scale * screenSize.width, + height: (scale * screenSize.width) / imageAspect, } } else { return { - width: scale * SCREEN.height * imageAspect, - height: scale * SCREEN.height, + width: scale * screenSize.height * imageAspect, + height: scale * screenSize.height, } } } |