"Contain" images with missing dimensions instead of cropping them (#6828)
* Show unknown aspect as "contain" for autosize * Fix a flash of wrong position when opening in lightbox * Fix last frame flash on Android
This commit is contained in:
@@ -392,9 +392,9 @@ const ImageItem = ({
|
||||
<Animated.View style={imageCropStyle}>
|
||||
<Animated.View style={imageStyle}>
|
||||
<Image
|
||||
contentFit="cover"
|
||||
contentFit="contain"
|
||||
source={{uri: imageSrc.uri}}
|
||||
placeholderContentFit="cover"
|
||||
placeholderContentFit="contain"
|
||||
placeholder={{uri: imageSrc.thumbUri}}
|
||||
accessibilityLabel={imageSrc.alt}
|
||||
onLoad={
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
// Original code copied and simplified from the link below as the codebase is currently not maintained:
|
||||
// https://github.com/jobtoday/react-native-image-viewing
|
||||
|
||||
import React, {useCallback, useEffect, useState} from 'react'
|
||||
import React, {useCallback, useEffect, useMemo, useState} from 'react'
|
||||
import {
|
||||
LayoutAnimation,
|
||||
PixelRatio,
|
||||
@@ -79,6 +79,15 @@ const FAST_SPRING: WithSpringConfig = {
|
||||
restDisplacementThreshold: 0.01,
|
||||
}
|
||||
|
||||
function canAnimate(lightbox: Lightbox): boolean {
|
||||
return (
|
||||
!PlatformInfo.getIsReducedMotionEnabled() &&
|
||||
lightbox.images.every(
|
||||
img => img.thumbRect && (img.dimensions || img.thumbDimensions),
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
export default function ImageViewRoot({
|
||||
lightbox: nextLightbox,
|
||||
onRequestClose,
|
||||
@@ -104,23 +113,19 @@ export default function ImageViewRoot({
|
||||
return
|
||||
}
|
||||
|
||||
const canAnimate =
|
||||
!PlatformInfo.getIsReducedMotionEnabled() &&
|
||||
nextLightbox.images.every(
|
||||
img => img.thumbRect && (img.dimensions || img.thumbDimensions),
|
||||
)
|
||||
const isAnimated = canAnimate(nextLightbox)
|
||||
|
||||
// https://github.com/software-mansion/react-native-reanimated/issues/6677
|
||||
rAF_FIXED(() => {
|
||||
openProgress.set(() =>
|
||||
canAnimate ? withClampedSpring(1, SLOW_SPRING) : 1,
|
||||
isAnimated ? withClampedSpring(1, SLOW_SPRING) : 1,
|
||||
)
|
||||
})
|
||||
return () => {
|
||||
// https://github.com/software-mansion/react-native-reanimated/issues/6677
|
||||
rAF_FIXED(() => {
|
||||
openProgress.set(() =>
|
||||
canAnimate ? withClampedSpring(0, SLOW_SPRING) : 0,
|
||||
isAnimated ? withClampedSpring(0, SLOW_SPRING) : 0,
|
||||
)
|
||||
})
|
||||
}
|
||||
@@ -185,6 +190,7 @@ function ImageView({
|
||||
openProgress: SharedValue<number>
|
||||
}) {
|
||||
const {images, index: initialImageIndex} = lightbox
|
||||
const isAnimated = useMemo(() => canAnimate(lightbox), [lightbox])
|
||||
const [isScaled, setIsScaled] = useState(false)
|
||||
const [isDragging, setIsDragging] = useState(false)
|
||||
const [imageIndex, setImageIndex] = useState(initialImageIndex)
|
||||
@@ -194,10 +200,19 @@ function ImageView({
|
||||
const isFlyingAway = useSharedValue(false)
|
||||
|
||||
const containerStyle = useAnimatedStyle(() => {
|
||||
if (openProgress.get() < 1 || isFlyingAway.get()) {
|
||||
return {pointerEvents: 'none'}
|
||||
if (openProgress.get() < 1) {
|
||||
return {
|
||||
pointerEvents: 'none',
|
||||
opacity: isAnimated ? 1 : 0,
|
||||
}
|
||||
}
|
||||
return {pointerEvents: 'auto'}
|
||||
if (isFlyingAway.get()) {
|
||||
return {
|
||||
pointerEvents: 'none',
|
||||
opacity: 1,
|
||||
}
|
||||
}
|
||||
return {pointerEvents: 'auto', opacity: 1}
|
||||
})
|
||||
|
||||
const backdropStyle = useAnimatedStyle(() => {
|
||||
|
||||
@@ -85,10 +85,6 @@ export function AutoSizedImage({
|
||||
if (Number.isNaN(aspectRatio)) {
|
||||
aspectRatio = undefined
|
||||
}
|
||||
} else {
|
||||
// If we don't know it synchronously, treat it like a square.
|
||||
// We won't use fetched dimensions to avoid a layout shift.
|
||||
aspectRatio = 1
|
||||
}
|
||||
|
||||
let constrained: number | undefined
|
||||
@@ -103,11 +99,13 @@ export function AutoSizedImage({
|
||||
|
||||
const cropDisabled = crop === 'none'
|
||||
const isCropped = rawIsCropped && !cropDisabled
|
||||
const isContain = aspectRatio === undefined
|
||||
const hasAlt = !!image.alt
|
||||
|
||||
const contents = (
|
||||
<View ref={containerRef} collapsable={false} style={{flex: 1}}>
|
||||
<Image
|
||||
contentFit={isContain ? 'contain' : 'cover'}
|
||||
style={[a.w_full, a.h_full]}
|
||||
source={image.thumb}
|
||||
accessible={true} // Must set for `accessibilityLabel` to work
|
||||
@@ -115,9 +113,11 @@ export function AutoSizedImage({
|
||||
accessibilityLabel={image.alt}
|
||||
accessibilityHint=""
|
||||
onLoad={e => {
|
||||
fetchedDimsRef.current = {
|
||||
width: e.source.width,
|
||||
height: e.source.height,
|
||||
if (!isContain) {
|
||||
fetchedDimsRef.current = {
|
||||
width: e.source.width,
|
||||
height: e.source.height,
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user