react-native-blur
react-native-blur copied to clipboard
BlurView on top of scrollview
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
Anyone? @charpeni?
This is a pretty common use case. @Kureev?
any response?
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
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
I tested your workaround and it is working! I wonder how strong the impact is on the performance though.
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...
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 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 answer is correct according to the docs too. this is the working mechanism of this lib.Thanks
@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
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 's solution is what worked for me and works like a charm