react-native-blur icon indicating copy to clipboard operation
react-native-blur copied to clipboard

BlurView on top of scrollview

Open minuitagency opened this issue 5 years ago • 13 comments

Hi,

I'm using this library for a header that is absolutely positioned and I'd like to have a scrollview underneath.

Problem is the blur is not updated in real time, only when I change tab or switch apps.

Does anybody knows a workaround?

Thanks,

Théo

minuitagency avatar Jun 09 '19 21:06 minuitagency

Anyone? @charpeni?

Dexwell avatar Aug 23 '19 11:08 Dexwell

This is a pretty common use case. @Kureev?

Dexwell avatar Sep 30 '19 11:09 Dexwell

any response?

charro0407 avatar Dec 19 '19 17:12 charro0407

This really needs to be addressed. For a library with this many downloads which ALREADY has the invalidate() function built in, it really shouldn't be that hard to update. If anyone gets word on a workaround for this please tell. @Kureev @charpeni

Bricktheworld avatar Feb 02 '20 23:02 Bricktheworld

First of all, I apologize for the horrible workaround you will see.

I used a createAnimatableComponent to create an animatable BlurView

import {Animated} from 'react-native';

const AnimatedBlurView = Animated.createAnimatedComponent (BlurView);

Then declare an animatable variable, I named it as: forceRender

const forceRender = new Animated.Value(0);

AnimatedBlurView will replace your BlurView

<AnimatedBlurView
  style={{
    ... StyleSheet.absoluteFill,
    opacity: forceRender.interpolate ({
      inputRange: [-300, 0], // 😢 you can play with these values
      outputRange: [1, 2], // 😓 this is the only way I made opacity work
      // it should only accept values between 0 and 1, but otherwise it doesn't give me the result I expect
    }),
  }}
  blurType="dark"
  reducedTransparencyFallbackColor="black"
/>

You will have to change your ScrollView or FlatList to an Animated.ScrollView or Animated.FlatList

<Animated.FlatList
  scrollEventThrottle={30}
  onScroll={Animated.event(
    [
      {
        nativeEvent: {
          contentOffset: {
            y: forceRender,
          },
        },
      },
    ],
    {
      useNativeDriver: true,
    },
  )}
  ...
/>

Let me know if it worked for you

devmanny avatar Sep 29 '20 01:09 devmanny

I tested your workaround and it is working! I wonder how strong the impact is on the performance though.

Laurent888 avatar Jan 06 '21 15:01 Laurent888

Inspired by @devmanny 's workaround, here a more generic but still ugly workaround:



  // blur gets stuck otherwise
  const workaroundValue = useRef<Animated.Value>(new Animated.Value(0));
  const workaroundStyle = useRef<AnimatedStyleSheet>({
    opacity: workaroundValue.current.interpolate({
      inputRange: [0, 1],
      outputRange: [1, 2],
    }),
  });

  useEffect(() => {
    Animated.loop(
      Animated.sequence([
        Animated.timing(workaroundValue.current, {
          toValue: 1,
          duration: 1000,
          useNativeDriver: true,
        }),
        Animated.timing(workaroundValue.current, {
          toValue: 0,
          duration: 1000,
          useNativeDriver: true,
        }),
      ]),
    ).start();
  }, []);

  return (
    <AnimatedBlurView
      style={[StyleSheet.absoluteFill, workaroundStyle.current]}
      blurType="light"
      blurAmount={6}
    />
  );


// type
type AnimatedStyleSheet = Animated.AnimatedProps<
  React.ComponentPropsWithRef<typeof View>
>['style'];

For a normal Animated.View I wouldn't be concerned with performance at all but I don't understand the implications for BlurView...

adbl avatar Jan 22 '21 17:01 adbl

This problem isn't limited to ScrollView—I'm also seeing it with FlatList and MapView.

However, I'm using React Navigation and I found that this problem only occurs when the BlurView component and the component it overlays (e.g. ScrollView, FlatList, MapView) are within the same Screen. If you're using React Navigation, you can avoid this problem entirely by adding the BlurView to the navigator. Below is an example.

<Tab.Navigator
  screenOptions={{headerShown: false}}
  tabBar={() => {
    return (
      <View style={styles.tabs}>
        <BlurView
            style={{position: "absolute", top: 0, right: 0, bottom: 0, left: 0}}
            ...
        />
        <Pressable
          style={styles.tab}
          onPress={() => navigation.navigate("Screen 1")}
        />
        <Pressable
          style={styles.tab}
          onPress={() => navigation.navigate("Screen 2")}
        />
      </View>
    )
  }}
>
  <Tab.Screen name="Screen 1" component={Screen1} />
  <Tab.Screen name="Screen 2" component={Screen2} />
</Tab.Navigator>

daveyjones avatar Jan 14 '22 16:01 daveyjones

@daveyjones You're talking about a bottom-tab navigator correct? That doesn't work with a top-tab navigator.

I realised the reason it's not in sync with the underlying view is because of it's position and z-index.

This will not work

<BlurView  style={{position: "absolute", top: 0, right: 0, bottom: 0, left: 0, zIndex: 10}}/>
<ScrollView>
  {content}
</ScrollView>

This works (removing zIndex and placing it under the ScrollView)

<ScrollView>
  {content}
</ScrollView>
<BlurView  style={{position: "absolute", top: 0, right: 0, bottom: 0, left: 0}}/>

This was happening to me in a material-top-tabs navigator, where tabBar={() => <TabBarWithBlur />} had to have z-index because it's rendered before the scroll content inside the tab view. My solution was to set the BlurView inside the tab view.

Could you try that @devmanny ?

Gregoirevda avatar Feb 08 '22 13:02 Gregoirevda

@Gregoirevda answer is correct according to the docs too. this is the working mechanism of this lib.Thanks

beqramo avatar Sep 13 '22 20:09 beqramo

@Gregoirevda I'm having the same problem as yours. Can you share how you solve it? I'm not sure I understand placing the Blur View inside the tab view. Do you mean inside each scrollable item of the tab? Thanks

filippobarcellos avatar Feb 23 '23 08:02 filippobarcellos

Inside each tab component, you can have a wrapper called BlurredTabView which looks like this

function BlurredTabView({children}) { 
return <>
  {children}
  <BlurView  style={{position: "absolute", top: 0, right: 0, bottom: 0, left: 0}}/>
</>

Gregoirevda avatar Feb 23 '23 08:02 Gregoirevda

@Gregoirevda 's solution is what worked for me and works like a charm

arled avatar Jul 10 '23 00:07 arled