Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(blur): Migrate web to a function component and fix reanimated err…
…ors (#27721) # Why - [`setNativeProps` was removed](necolas/react-native-web@e68c327) so we need to set the style props directly. This can be reproduced by opening NCL and going to the blur route. - Tested the change in both safari and chrome. - This PR also drops support for older browsers in favor of the unsupported style warning. --------- Co-authored-by: Expo Bot <34669131+expo-bot@users.noreply.github.com>
- Loading branch information
Showing
7 changed files
with
96 additions
and
119 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,65 +1,64 @@ | ||
// Copyright © 2024 650 Industries. | ||
|
||
'use client'; | ||
|
||
import * as React from 'react'; | ||
import { forwardRef, useImperativeHandle, useRef } from 'react'; | ||
import { View } from 'react-native'; | ||
|
||
import { BlurViewProps } from './BlurView.types'; | ||
import getBackgroundColor from './getBackgroundColor'; | ||
|
||
// TODO: Class components are not supported with React Server Components. | ||
export default class BlurView extends React.Component<BlurViewProps> { | ||
private blurViewRef = React.createRef<View>(); | ||
const BlurView = forwardRef<{ setNativeProps: (props: BlurViewProps) => void }, BlurViewProps>( | ||
({ tint = 'default', intensity = 50, style, ...props }, ref) => { | ||
const blurViewRef = useRef<HTMLDivElement>(null); | ||
const blurStyle = getBlurStyle({ tint, intensity }); | ||
|
||
/** | ||
* Reanimated will detect and call this function with animated styles passed as props on every | ||
* animation frame. We want to extract intensity from the props, then create and apply new styles, | ||
* which create the blur based on the intensity and current tint. | ||
*/ | ||
setNativeProps(nativeProps) { | ||
const { style, tint, intensity: standardIntensity } = this.props; | ||
const intensity = nativeProps.style.intensity ?? standardIntensity; | ||
const blurStyle = getBlurStyle({ intensity, tint }); | ||
this.blurViewRef?.current?.setNativeProps({ | ||
...nativeProps, | ||
style: [style, blurStyle, nativeProps.style], | ||
}); | ||
} | ||
useImperativeHandle( | ||
ref, | ||
() => ({ | ||
setNativeProps: (nativeProps: BlurViewProps) => { | ||
if (!blurViewRef.current?.style) { | ||
return; | ||
} | ||
|
||
render() { | ||
const { tint = 'default', intensity = 50, style, ...props } = this.props; | ||
const blurStyle = getBlurStyle({ tint, intensity }); | ||
return <View {...props} style={[style, blurStyle]} ref={this.blurViewRef} />; | ||
} | ||
} | ||
// @ts-expect-error: `style.intensity` is not defined in the types | ||
const nextIntensity = nativeProps.style?.intensity ?? intensity; | ||
const blurStyle = getBlurStyle({ intensity: nextIntensity, tint: tint ?? 'default' }); | ||
if (nativeProps.style) { | ||
for (const key in nativeProps.style) { | ||
if (key !== 'intensity') { | ||
blurViewRef.current.style[key] = nativeProps.style[key]; | ||
} | ||
} | ||
} | ||
|
||
function isBlurSupported(): boolean { | ||
// TODO: Replace with CSS or static extraction to ensure hydration errors cannot happen. | ||
// Enable by default in Node.js | ||
if (typeof window === 'undefined') { | ||
return true; | ||
blurViewRef.current.style.backgroundColor = blurStyle.backgroundColor; | ||
blurViewRef.current.style.backdropFilter = blurStyle.backdropFilter; | ||
blurViewRef.current.style['webkitBackdropFilter'] = blurStyle.WebkitBackdropFilter; | ||
}, | ||
}), | ||
[intensity, tint] | ||
); | ||
|
||
return ( | ||
<View | ||
{...props} | ||
style={[style, blurStyle]} | ||
/** @ts-expect-error: mismatch in ref type to support manually setting style props. */ | ||
ref={blurViewRef} | ||
/> | ||
); | ||
} | ||
// https://developer.mozilla.org/en-US/docs/Web/API/CSS/supports | ||
// https://developer.mozilla.org/en-US/docs/Web/CSS/backdrop-filter#Browser_compatibility | ||
return ( | ||
typeof CSS !== 'undefined' && | ||
(CSS.supports('-webkit-backdrop-filter', 'blur(1px)') || | ||
CSS.supports('backdrop-filter', 'blur(1px)')) | ||
); | ||
} | ||
); | ||
|
||
function getBlurStyle({ intensity, tint }): Record<string, string> { | ||
const style: Record<string, string> = { | ||
function getBlurStyle({ | ||
intensity, | ||
tint, | ||
}: Required<Pick<BlurViewProps, 'intensity' | 'tint'>>): Record<string, string> { | ||
const blur = `saturate(180%) blur(${Math.min(intensity, 100) * 0.2}px)`; | ||
return { | ||
backgroundColor: getBackgroundColor(Math.min(intensity, 100), tint), | ||
backdropFilter: blur, | ||
WebkitBackdropFilter: blur, | ||
}; | ||
|
||
if (isBlurSupported()) { | ||
const blur = `saturate(180%) blur(${Math.min(intensity, 100) * 0.2}px)`; | ||
style.backdropFilter = blur; | ||
// Safari support | ||
style.WebkitBackdropFilter = blur; | ||
} | ||
|
||
return style; | ||
} | ||
|
||
export default BlurView; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters