Open jamesxabregas opened 3 years ago
Hi @jamesxabregas I had a similar issue recently with an image carousel flashing quickly before changing the image dynamically via swipe. My issue was that I was trying to add the key
property on the encompassing view. Changing the key
value to some random value other than the current carousel index made it work for me.
Yeah this isn't an issue with keys or anything like that. I looked into that. The issue is that until an image is within FastImage's cache this sort of flashing will occur when changing the image source. The solution as per the documentation is to preload the images of course, but realistically this is flawed for two reasons:
I ended up devloping a hacky solution within my own project where I had two images laid on top of each other with the lower level one loading the image first and then firing an onLoad event that set the source of the upper layer image. This meant that image sources could be changed without the flash occuring during the loading phase. However, as stated this is hacky and not particularly performant. This really should be fixed at the native level.
If someone could point me in the right direction I'd be more than happy to tinker with this to get it right.
I am also experiencing this issue and looking for a solution. Thank you
It's 2022 and this isn't fixed yet. Thanks @jamesxabregas , the hack works.
same problem here.
Any updates for this ticket?
Any update?
Any update?
Please leave an upvote on this discussion item if you need this. https://github.com/DylanVann/react-native-fast-image/discussions/825
In the meantime @jamesxabregas hack seems to work pretty well for me - here's a (simplified) code snippet for how I've implemented it
import { Dimensions, StyleSheet, View } from 'react-native';
import FastImage from 'react-native-fast-image';
type ImageSource = { uri: string; headers: { [key: string]: string } };
const { width, height } = Dimensions.get('window');
type Props = {
imageSource: ImageSource
}
export default function Image({ imageSource } : Props) {
const [visibleImageSource, setVisibleImageSource] = useState<ImageSource>();
// This is a hack to get around FastImage's cache-ing limitations
// There can be a short black flash when changing image source before the next image is
// fully loaded. Ideally we'd have FastImage.preload be promise based so we can know
// when the images are cached, but this is not implemented in the library.
// The hack is to have one hidden image component which tries to load
// the image. Once the image loads and onLoad fires, we set the visible image component with the
// right source.
// Credit for this hack: https://github.com/DylanVann/react-native-fast-image/issues/747
return (
<View style={{ flex: 1, width, height }} >
<FastImage
source={imageSource}
style={{ height: 1, width: 1, opacity: 0 }} // height and width must be non-zero or else onLoad does not fire on Android
onLoad={() => {
setVisibleImageSource(imageSource);
}}
/>
<FastImage
source={visibleImageSource}
style={StyleSheet.absoluteFillObject}
/>
</View>
);
}
I tried above fix by @tomskopek but I am still getting flickering on Android.
anyone find a reason or workaround for this white flashing issue? The above hack didn't work for me
edit: adding example video, this is from a flatlist using the preload feature, one is the debug apk, one is the release apk. So might not completely fix it, but it fixed it enough to satisfy me. I would love for the image to just be there but this is close enough! images are 1080 resolution. lowering the image resolution made the image look horrible but fixed the issue. Wasn't an option for my app though cause it's largely image based. hope it helps someone.
Hi everyone, I also encountered this and I wrote an article and a patch for this lib, maybe it's useful. Basically I'm keeping the current UIImage/Drawable as a placeholder until the new one is loaded.
@cristiangu is your patch basically keeping the old image until the new one is loaded and displays it? I think it is a good use case for you example where a low res image is replaced with a higher res after some time. but for a carousel-like UI (like what @bruteforks has), how would this work since swiping to the next item will still be showing blank until the next image is loaded? we would not want the same image to be showing on the next item as that might be confusing..
Hi everyone, I also encountered this and I wrote an article and a patch for this lib, maybe it's useful. Basically I'm keeping the current UIImage/Drawable as a placeholder until the new one is loaded.
@cristiangu thanks for providing this patch. This is exactly what I was looking to do when I encountered this issue but I wasn't sure where in the native code it would need to be updated. This patch should really become part of this library.
@cristiangu is your patch basically keeping the old image until the new one is loaded and displays it? I think it is a good use case for you example where a low res image is replaced with a higher res after some time. but for a carousel-like UI (like what @bruteforks has), how would this work since swiping to the next item will still be showing blank until the next image is loaded? we would not want the same image to be showing on the next item as that might be confusing..
@appsgenie the carousel use case you are referring to is a different issue, as it is related to preloading the image. The initial issue I brought up was related to when the image source is already loaded and is then changed.
<FastImage source={imageSource} style={{ height: 1, width: 1, opacity: 0 }} // height and width must be non-zero or else onLoad does not fire on Android onLoad={() => { setVisibleImageSource(imageSource); }} />
This didn't work, nor did the patch work for me - though my use case might be slightly different - but what did work was changing onLoad= in this code to onLoadEnd
@cristiangu thank you for your patch!
Hi everyone, I also encountered this and I wrote an article and a patch for this lib, maybe it's useful. Basically I'm keeping the current UIImage/Drawable as a placeholder until the new one is loaded.
Thanks @cristiangu, I used your work around and it actually solve the flickering problem, but sometimes when images updating so fast it could cause crashing because bitmap was recycled before the draw event. The safer way I found and use as below:
Instead of setting placeHolder directly using this.getDrawable()
...
.placeholder(mUseLastImageAsDefaultSource ? this.getDrawable() : mDefaultSource) // show until loaded
Copy to new BitmapDrawable and use for default source
public void setSource(@Nullable ReadableMap source) {
mNeedsReload = true;
mSource = source;
if (mUseLastImageAsDefaultSource) {
BitmapDrawable currentDrawable = (BitmapDrawable) this.getDrawable();
Bitmap toBmp = currentDrawable == null ? null : currentDrawable .getBitmap();
if (toBmp != null && !toBmp.isRecycled()) {
this.setDefaultSource(new BitmapDrawable(getResources(), toBmp.copy(toBmp.getConfig(), false)));
}
}
}
@phungthanhdong, it makes sense! Thank you for this!
I am having an issue with flashing when I have a FastImage componenet on which I am changing the source dynamically. My use case is that I am creating an image picker. At first I thought this was a pre-caching issue but it doesn't appear to be as the issue occurs even with pre-caching configured. It seems the issue is that when the image source changes, FastImage renders a blank image momentarily and then renders the new image. The flashing only occurs for a fraction of a second but is quite distracting. It also occurs when loading local resources stored within the React Native app. In comparison React Native's default Image component waits for the Image to finish caching before swapping in the new image so no flashing occurs on change however the image caching on React Native is not great which is why I would prefer to use Fast Image.
I've made a screen recording to demonstrate the issue. All of these images were local files btw, and the video was recorded on a physical iPhone 12 (not emulated) running a production build (it was not connected to Metro).
It seems the flash is not visible on every transition in this recording likely because of the frame rate, but when viewing in person the flash does occur on each transition.
Environment