React Native中实现仿iOS的液态玻璃效果

0 阅读7分钟

前言

在React Native近期的更新中,比较大的就是GlassEffect,就是iOS的液态玻璃效果。其实具体啥样我也没具体关注,主要是没有iOS设备,我的音乐播放器应用也一直是在Android上调试。而且Android上的blur效果和GlassEffect效果是有明显差异的。那么提到blur,前端er应该立马能想到CSS中的滤镜,blur毛玻璃效果在web端实现非常轻松,而在android则是实验性功能,它还很年轻,在开发过程中我尝试了很多依赖库,下面会简单介绍在使用过程中踩过的坑。下面先放图,使用的位置是页面顶部和底部tab,看一下大致效果:

微信图片_20260214174329_233_23.jpg 微信图片_20260214174330_234_23.jpg

微信图片_20260214174331_235_23.jpg

使用过的依赖库

@sbaiahmed1/react-native-blur

效果非常哇塞的一个依赖库!amazing!但是它有些大坑,而且难以逾越,在github仓库中有很多issue,而且我发现有些并没有明确解决,但是关闭了。最近也是尝试了挺多依赖库,也提了一些issue,有些依赖库作者会回复,但是总体感受就是不耐烦,而且大部分是急不可耐的关闭,比如:我们测没有问题,你的问题我从来没遇到过,关闭。这个问题不多说。具体说我发现的问题:

  1. 当页面中使用了该依赖库创建blur效果后当前页面内触发的页面跳转:比如模态页,新页面是动画划出,在新打开的页面中会蒙上一层白雾,不影响页面操作
  2. 当一个页面使用了blur效果,在该页面中跳转一个从页面侧面划出的模态页(抽屉效果)时没有问题,但是当页面收起时会再次闪烁一下这个模态页

3.自带overflow:'hidden'且无法使用自定义样式覆盖,这使得你想让容器内的一个元素以定位的方式'超出容器'变的很困难,你需要改布局和样式来实现,这在我的音乐播放器的应用中

其它问题没有具体测试,但是已经让我够崩溃的了,因为我用的expo搭建的rn项目,并且没有本地构建环境,只能使用EAS build,而expo的每个月免费构建次数是有上限的,Android和iOS各是15次。

@danielsaraldi/react-native-blur-view

这个依赖库也是个很amazing的依赖库,但是amazing的不是效果,是它的使用方式比较奇特,readme文档中写道:

import {
  BlurView,
  BlurTarget,
  VibrancyView,
} from '@danielsaraldi/react-native-blur-view';
// ...

export default function App() {
  // ...

  return (
    <>
      <BlurView targetId="target" style={styles.blurView}>
        <Text style={styles.title}>BlurView</Text>
      </BlurView>

      <VibrancyView style={styles.vibrancyView}>
        <Text style={styles.title}>VibrancyView</Text>
      </VibrancyView>

      <BlurTarget id="target" style={styles.main}>
        <ScrollView
          style={styles.main}
          contentContainerStyle={styles.content}
          showsVerticalScrollIndicator={false}
        >
          {/* ... */}
        </ScrollView>
      </BlurTarget>
    </>
  );
}

它有一个比较奇怪的targetId,而且它的使用不太符合直觉,而且目前大部分blur依赖库的使用方式就是BlurView容器组件内部children使我们真正想要渲染的元素。由于我没有深入研究该依赖库,而且做出了半透明背景色效果,就和我当初初用expo-blur一样,实现了rgba半透明背景色。。。

  1. 自带overflow:'hidden',无法覆盖
  2. 使用方式复杂,不够直观
  3. 接收的style样式奇奇怪怪各种无效

expo-blur

这个依赖库很早我就安装过,,当时只是做了简单的效果,没有仔细看文档,其实它的文档非常简单,配置项很少,使用极其简单,效果其实也还不错,没有像第一个依赖库那样的硬伤bug,但是最近我要在应用中明确实现图中的效果,但是,但是由于我忽略了最重要的配置,导致我只能实现rgba效果!,后面我会重点分析,但是并不是说它就没有问题:

  1. 容器中的文字,icon图标外围可能会出现模糊阴影:仔细看第三张图的'我的'文字,它周围产生了阴影!而且不是总会产生!就是容器内的元素可能因为BlurView的影响产生了朦胧美!当然你觉得它美也行,但是它时有时无,可以确定是bug

其它目前我没有发现异常,而且就像expo-audio兜兜转转又回到原点一样,我千辛万苦尝试了这么多依赖库,因为这些依赖库都是需要原生代码,因此想要调试不能使用expo go,必须执行开发构建才能调试,这个坑还能接受,但是你还要知道:不同的blur依赖库内部可能都使用了同样的依赖库,调用了同样的原生功能,如果你把所有功能相似的依赖库都安装并开发构建,你大概率会失败,所以在尝试一些依赖库时就提心吊胆:构建次数有限,每次同类库只能安装一个,比较耗时,大概30分钟以内,如果排队的话,可能光排队就要30分钟!

expo-blur的使用

刚才也提到了expo-blur使用非常简单,配置项很少,非常符合直觉,文档的介绍也比较简单:它继承扩展了ViewProps,就是它具有View组件的属性,除此之外还有以下属性:

  1. intensity,强度值 1~100,模糊强度,值越大越模糊
  2. tint 模糊效果类型,它内置了很多通用的模糊效果类型,比如systemMaterialLight和systemMaterialDark,有一些是成对的dark和light,这在做亮暗模式切换时很有用
  3. blurReductionFactor区分模糊强度的数值,主要用于模拟调整效果,使模糊效果接近iOS,文档没有提到具体值的范围,默认值是4,我测试超过100就没有变化了
  4. experimentalBlurMethod,在安卓上实验性的模糊方法也就是底层到底使用何种方式让UI模糊,它有两个可选值,默认none,另一个是dimezisBlurView

就这么点配置项,我天真的以为核心是intensity强度值,结果发现设置为100效果也只是rgba,这让我很恼火,以为依赖库效果不行,就换换换,其实呢是没有设置experimentalBlurMethod:'dimezisBlurView',如果你看了我上一篇文章,里面的半透明效果使用的是@sbaiahmed1/react-native-blur,效果不正确应该还是有些配置或者使用方式不对,也不研究了,因为expo-blur满足我想要的效果。除此之外,你是不是想过做一个半透明毛玻璃效果的图片作为背景图充当blurView容器呢? 我要告诉你,这是行不通的,明明在ps中调试的不错,但是真正使用就会发现效果约等于rgba!

为了让应用中效果比较统一,我做了简单封装:

import type { FC, PropsWithChildren } from "react";
import { BlurView } from "expo-blur";
import type { StyleProp, ViewStyle } from "react-native";
import { useThemeConfig } from "@/hooks/useTheme";
interface Props extends PropsWithChildren {
    style?: StyleProp<ViewStyle>;
}

/**
 * @param children 子组件,显示在模糊层上方的实际内容
 * @returns jsx 组件
 */
const BlurContainer: FC<Props> = ({
    children, style,
}) => {
    const { tabbarBlurType } = useThemeConfig();
    return (
        <BlurView
            intensity={60}
            style={[{ flex: 1 }, style]}
            blurReductionFactor={4}
            tint={tabbarBlurType}
            experimentalBlurMethod="dimezisBlurView"
        >
            {children}
        </BlurView>
    );
};
export default BlurContainer;

让tint跟随主题切换,而且expo-blur没有强制overflow:'hidden',这在tabbar上的播放器控制栏上露头的图片布局非常有利:

Snipaste_2026-02-14_19-11-25.png

下面是没有开启experimentalBlurMethod="dimezisBlurView"的效果:

Snipaste_2026-02-14_19-14-54.png

下图是在亮模式下blur导致的异常阴影效果问题:

Snipaste_2026-02-14_19-28-56.png

这个问题应该调整intensity的值低一点应该会改善,在暗模式下不是很明显。[项目地址](expo rn: expo创建的react native的音乐播放器应用,专注视频转歌和本地歌曲播放)欢迎讨论交流!