react-native-blur: 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 react-native-blur@3.0.0-alpha 2 versions have above the problem

eg // 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> //…

`

About this issue

  • Original URL
  • State: open
  • Created 7 years ago
  • Comments: 16 (4 by maintainers)

Most upvoted comments

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:

  1. Get react-native-handle of target view
  2. In native code render said view to canvas, basically creating a screenshot (optionally downsampled to boost performance)
  3. 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)
  4. 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:

  1. Figure out another way to wait for images to be loaded and drawn to canvas first.
  2. Provide a mechanism for images to wait via setTimeout and do animated fade ins. e.g. <BlurImageView src=.../>
  3. 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

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.

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.

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.