iOS 上用 UIVisualEffectView,效果漂亮,但 Android 那边要么性能差、要么效果不对——得用不同方案分别适配,写两套代码,维护两套效果。
Android 11 之后原生支持毛玻璃,但低版本系统又得找第三方库兼容。团队小的时候,光一个头部模糊就搞得人仰马翻。
今天介绍的这个开源项目,叫 variable-header-blur。一个组件,iOS 和 Android 原生毛玻璃,代码一样效果一致。
https://github.com/hewad-mubariz/variable-header-blurDemo
iOS
Android
先说它能做什么
variable-header-blur 是一个 Expo 模块,专注于 App 头部导航栏的毛玻璃效果。
它做的事很简单:给你一个可配置的毛玻璃头部组件,iOS 和 Android 共用一套代码,原生实现,效果统一。
import { VariableHeaderBlurView } from"variable-header-blur";<VariableHeaderBlurViewheaderHeight={HEADER_TOTAL_HEIGHT}maxBlurRadius={16}tintOpacityTop={0.3}tintOpacityMiddle={0.1}style={{position: "absolute", top:0, left:0, right:0 }}> {/* Header content */}</VariableHeaderBlurView>;就这么多。把毛玻璃头部当成一个容器,往里塞你的导航栏内容,剩下的它自己搞定。

核心:渐进式模糊
这个组件最大的特点,是渐进式模糊——从顶部到底部,模糊强度逐渐减弱。
这不是普通的"整块模糊",而是模拟真实毛玻璃材质的光学特性:越靠上、越靠近光源,模糊越强;越往下,内容越清晰。
这个效果在 iOS 原生支持——用 UIVisualEffectView 配合私有的 variableBlur 滤镜实现。Android 则用 chrisbanes/haze 库,在 Compose 里跑同样的渐进式模糊管线。
两个平台最终呈现的视觉效果基本一致——这对 UI 一致性要求高的 App 来说,是实打实的减负。
iOS 和 Android,各用什么方案
项目在两个平台用了不同的底层实现,但都达到了相近的视觉目标:
iOS 方案
基于 nikstar/VariableBlur,用 UIVisualEffectView 承载苹果私有的 variableBlur 滤镜。梯度遮罩(gradient mask)驱动每个像素的模糊强度,实现真正的渐进式模糊。SwiftUI 组件包装在 Expo ExpoView 里,暴露给 React Native 使用。
Android 方案
用 chrisbanes/haze 的管线,在 Compose 原生视图里运行:
hazeSource(state)标记内容源图层 hazeEffect(state)应用模糊 + 色调 HazeProgressive.verticalGradient(...)控制渐进式模糊衰减
外加一层 Canvas 渐变,让色调过渡和模糊强度对齐。整体效果接近 iOS 的原生表现。

几个关键配置
组件提供了一组 Props,精准控制模糊效果:
headerHeight | |||
maxBlurRadius | |||
tintOpacityTop | |||
tintOpacityMiddle | |||
tintColor | |||
progressiveStartY | |||
progressiveEndY |
可以看到,Android 多了几个高级调参项——tintColor、progressiveStartY、progressiveEndY——给需要精细控制效果的开发者留了口子。

局限性和门槛
这个模块包含自定义原生代码,不支持 Expo Go,必须用 development build:
npx expo prebuildnpx expo run:iosnpx expo run:android换句话说:适合正经做 App 项目的团队,不适合快速原型预览。
但如果你本来就用 Expo 做生产级 App,这个门槛就不是门槛——只是顺手的事。
技术细节值得单独说
整个架构设计很干净:
src/VariableHeaderBlurView.tsx → 暴露原生视图管理器ios/VariableHeaderBlurView.swift → iOS 毛玻璃 + 渐变遮罩android/VariableHeaderBlurView.kt → Android Haze 渐进模糊 + 色调渐变原生 Props 通过 VariableHeaderBlurModule 在双平台映射,React Native 这侧只管用 Props 调用,不用关心底层实现。
这种架构的好处:视图逻辑和平台实现完全分离,后续如果要加新平台,或者调整某平台的模糊算法,不影响另一侧,也不影响 React Native 调用侧。

你可以从哪里开始
如果你现在正在做 Expo App 的头部导航,或者正好在调研毛玻璃实现方案:
去 GitHub 页面看一下 Demo 视频(iOS 和 Android 各有一个) Clone 项目,看 src/VariableHeaderBlurView.tsx的 Props 定义按文档步骤在自己的 Expo 项目里引入
# 1. 把模块放进项目modules/variable-header-blur# 2. 加到 package.json"variable-header-blur": "file:./modules/variable-header-blur"# 3. yarn install 然后 prebuildnpx expo prebuild原生毛玻璃这件事,iOS 和 Android 一直有两套实现思路。
variable-header-blur 的价值,不是造了个新轮子——是把这两套思路在 Expo 体系里接起来了,让你好歹用一套代码。
如果你正被双端毛玻璃效果折磨,这个项目值得一试。
项目地址:https://github.com/hewad-mubariz/variable-header-blur
如果身边有做 Expo 开发的朋友,这篇可以转给他看看。
夜雨聆风