Skip to content

Commit

Permalink
"Contain" images with missing dimensions instead of cropping them (#6828
Browse files Browse the repository at this point in the history
)

* Show unknown aspect as "contain" for autosize

* Fix a flash of wrong position when opening in lightbox

* Fix last frame flash on Android
  • Loading branch information
gaearon authored Nov 28, 2024
1 parent d08ce0d commit 5f4a0f2
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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={
Expand Down
37 changes: 26 additions & 11 deletions src/view/com/lightbox/ImageViewing/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -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,
)
})
}
Expand Down Expand Up @@ -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)
Expand All @@ -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,
}
}
if (isFlyingAway.get()) {
return {
pointerEvents: 'none',
opacity: 1,
}
}
return {pointerEvents: 'auto'}
return {pointerEvents: 'auto', opacity: 1}
})

const backdropStyle = useAnimatedStyle(() => {
Expand Down
14 changes: 7 additions & 7 deletions src/view/com/util/images/AutoSizedImage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -103,21 +99,25 @@ 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
accessibilityIgnoresInvertColors
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,
}
}
}}
/>
Expand Down

0 comments on commit 5f4a0f2

Please sign in to comment.