react-native-blur
react-native-blur copied to clipboard
Bur is invalid when first time load on android
Bur is invalid when first time load on android
When replacing the picture of the mask below sometimes does not update the content on android
react-native-blur@master and [email protected] 2 versions have above the problem
// my code:
`
imageLoaded() { this.setState({viewRef: findNodeHandle(this.refs.backgroundImage) }); // setTimeout(() => { // this.setState({viewRef:findNodeHandle(this.refs.backgroundImage) }); // }, 200); }
renderBlurView() {
return (
<View >
{this.state.viewRef && <BlurView
viewRef={this.state.viewRef}
style={styles.blurView}
blurRadius={6}
blurType={'dark'}
blurAmount = {12}
/>}
</View>
)
`
`
//render 函数 : <Image source={this.state.customImage} style={[styles.backImage,styles.blurView]} ref={'backgroundImage'} onLoadEnd={this.imageLoaded.bind(this)} /> {this.renderBlurView()} <View style={styles.contentContainer}> <TouchableOpacity onPress={this.headerOnClicked.bind(this)}> <Image style={styles.circleImage} source={this.state.customImage} onError = {this.imageHeadOnloadEndError.bind(this)} > </Image> //......
`
Hi @roselind, sorry about this, it's a very tricky race condition that I've also been struggling with. I think the best workaround is to just add a small timeout, like you did in your example:
setTimeout(() => {
this.setState({viewRef:findNodeHandle(this.refs.backgroundImage) });
}, 200);
I need to figure out the right way to do this. I think there might be a bug in the blurring library that we are using, or perhaps the Android view has not finished rendering before we try to blur it. Any help would be appreciated, if you have time to look at the source code of react-native-blur and could help me figure this out.
tl;dr BlurView basically takes screenshot of target view and blurs it (all libs do this). But when onLoadEnd from Image is called, the image is not rendered yet, just loaded, therefor the screenshot is empty => no blur 😢. Best option right now is to setTimeout and optionally fade in image.
So I've been trying to figure this out as well. Seems like Fresco is loading the image and setting the asynchronously, thus https://github.com/facebook/react-native/blob/3fda6a9a2b380a750b72d369f5fb4f11540ad1ef/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageView.java#L214 is beeing called and will emit onLoadEnd
before view is invalidated and redrawn.
To clarify how I understand blurview works. As soon as we set viewRef
in setState
it will:
- Get react-native-handle of target view
- In native code render said view to canvas, basically creating a screenshot (optionally downsampled to boost performance)
- Add blur to the same canvas via RenderScript utilising
android.renderscript.ScriptIntrinsicBlur
(all libs I found basically to the same, mostly they differ a bit in memory management, some even have a slower java based fallback for older devices: e.g. https://github.com/Manabu-GT/EtsyBlur, https://github.com/mmin18/RealtimeBlurView, https://github.com/gogopop/BlurKit-Android, https://github.com/Dimezis/BlurView, https://github.com/wasabeef/Blurry) - Render blurred canvas to components view
If we use the onLoadEnd
the Image is not rendered yet into the target view, thus only giving back a blurred grey image. If we use setTimeout it will mostly work, but will kind of flash the original image unblurred at first.
Solution I see three options here:
- Figure out another way to wait for images to be loaded and drawn to canvas first.
- Provide a mechanism for images to wait via setTimeout and do animated fade ins. e.g.
<BlurImageView src=.../>
- The next react-native version 0.45 or 0.46 will finally add
blurRadius
to Images, just wait for it to land, but would not be backwards compatible...
Mentions There is a fork that uses Blurry, but it should not make a difference: https://github.com/carlesnunez/react-native-blurry
ScrollView-Support Basically we would need to recreate snapshot and blur on every scrollEvent. For large scroll-components we would need to screenshot only the visible bits. There is already a fork for the underlying lib Android uses, that would kind of work with ScrollView but it is not yet merged and tested, so probably we won't see this any time soon: https://github.com/500px/500px-android-blur/pull/10/files
thanks ,yours answers. I temporarily solved the problem , The effect is still a little slow I found it that BlurView can be forced update by modifying properties , eg blurRadius , downsampleFactor , overlayColor . Changing only one property (viewRef)will not update ,
code:
imageLoaded() {
InteractionManager.runAfterInteractions(() => {
setTimeout(() => {
var blurRadius = this.state.blurRadius ;
blurRadius = getRandomNum_MinAndMax_noValue(6,12,blurRadius);
var downsampleFactor = this.state.downsampleFactor;
downsampleFactor = getRandomNum_MinAndMax_noValue(4,8,downsampleFactor);
var tmpColor = getRandomNum_MinAndMax(0,80);
this.setState({
viewRef: findNodeHandle(this.refs.backgroundImage),
blurRadius:blurRadius,
downsampleFactor:downsampleFactor,
overlayColor:`rgba(${tmpColor},${tmpColor},${tmpColor},0.5)`
});
}, 400);
});
}
Thanks very much for the writeup @DomiR, you're totally right. I was struggling this myself when I fixed the Android version, but I couldn't come up with a good solution to really fix the race condition.
It would be awesome if there was some way to monitor the referenced view and detect rendering updates. It sounds like something that should be possible in theory, but I don't have any idea how to do it.
Wow, it seems that whenever I write up a post or a comment, I always find the answer immediately afterwards. I think I just figured it out: http://stackoverflow.com/a/7735122/304706
We just need to add a global layout listener to the referenced view, and update the blur whenever it changes. This fixes the race condition, but it always mean that we now have a real-time blur whenever the view changes.
I'm going to try this out and see if I can get it to work.
Excuse me, has this problem been solved?
Not solved, My present method is to force updates 2 times ,Most of them are correct
Could you please explain more clearly? Do you mean to refresh the page again? Refresh two times?
I resets state when first time load on android
imageLoaded() { InteractionManager.runAfterInteractions(() => { setTimeout(() => { var blurRadius = this.state.blurRadius ; blurRadius = getRandomNum_MinAndMax_noValue(6,12,blurRadius); var downsampleFactor = this.state.downsampleFactor; downsampleFactor = getRandomNum_MinAndMax_noValue(4,8,downsampleFactor); var tmpColor = getRandomNum_MinAndMax(0,80); this.setState({ viewRef: findNodeHandle(this.refs.backgroundImage), blurRadius:blurRadius, downsampleFactor:downsampleFactor, overlayColor:
rgba(${tmpColor},${tmpColor},${tmpColor},0.5)`
});
}, 400);
});
}
`
Blur on Android requires a reference to the view you want to blur which is not possible to pre-define before initial render (as far as it's controlled by React and there are no hooks we can use to control it).
@ndbroadbent Just wondering if you've made any progress with re. to adding OnGlobalLayoutListener
approach.
No pressure :)
I found it , Only once setting does not work ,Even after the page has been loaded ,I listen a event ,Set it again after a few seconds .
For those it might help, Image's blurRadius
has supposedly been working for both Android and iOS since RN 0.44. See more details here: https://github.com/facebook/react-native/commit/fc09c54324ff7fcec41e4f55edcca3854c9fa76b
@mikelambert Correct me if I'm wrong but blurRadius
only applies to Image
component.
This library allows us to blur Views in general.
Correct on both counts Kumar. My message wasn't meant to imply otherwise.
Many people in this thread just need a solution that works for Image.
I am used expo blur then never see laggy with android