用 Expo 和 Coreviz SDK 打造 AI 优先的照片应用
在 Expo 中打造原生质感的照片网格:如何用多个虚拟化网格加交叉淡入淡出,复刻 iOS 照片的捏合缩放交互。
中文
复制

本文是 Wassim Gharbi 的客座文章——他目前是 Tesla 的首席架构师,为数万亿个遥测数据点构建数据可视化平台,专注于高性能 UI 与 AI 系统研究。
…
作为一个拍过成千上万张照片的人,我一直很讨厌「专业」修图应用(比如 Lightroom 或 Photomator)和消费级应用(比如 iOS Photos 或 Google Photos)之间的那道鸿沟。
一边是强大的打标签、标记、加星、批量编辑功能,界面却像会计软件。另一边是漂亮、直观、流畅的界面,可一旦图库涨到几千张,你需要的整理工具一个都没有。
所以,在开发 Coreviz 的移动端应用时,我们决定把 Pro 版的 UX 偏好留在网页端,移动端的设计从零开始,给用户一个轻量的应用,让他们能毫不费力地浏览、搜索、打标签和整理庞大的照片库。

我们想要的,是一个看起来不吓人的「专业」媒体库:界面漂亮,把专业级的整理和编辑能力(由 Coreviz SDK 强力加持)带给普通人——他们只是不想让自己的照片库乱成一团。
这个应用(完全基于 Expo 构建,没有写一行原生代码)最终的外观和手感几乎和原生 Photos 应用一模一样,用户喜欢的那些细节一个不少。
但仔细看,你会发现先进的 AI 功能已经无缝嵌入应用的每个角落:「Edit with AI」把 Gemini(Nano Banana)这类模型的编辑能力直接放到照片所在的地方,「Tag with AI」则让你用一句简单的提示词批量给照片打标签,比如「找出所有被拍者闭眼的照片」或「标注每个篮球运动员的球衣号码」。
为什么选 Expo?
在思考如何打造一个「有苹果味」的应用时,React Native 和 Expo 可能不是开发者首先想到的工具。至少在我看来,Expo 一直以来主打的是通用性。当你希望用同一套代码同时发布 iOS 和 Android 应用时,就会选它,而代价是各种妥协。
现在已经不同往日了。
Expo 在跟进最新的 iOS/Android 更新方面做得相当出色,尤其是在暴露 Liquid Glass iOS 重新设计带来的各种最新 UX 交互上一直处于领先:原生 Tabs、Liquid Glass 按钮和标题、共享元素转场等等。
与此同时,React Native 生态本身也在缩小性能和 UX 上的差距,比如 New Architecture、React Native Skia(上面视频里的涟漪效果就是它实现的),以及 LegendList/FlashList——本文要讲的网格视图就由它们驱动。
这让 Expo 成为既能让应用有原生手感、又能覆盖全平台的理想选择,也就顺理成章地成了 Coreviz Studio 移动应用的选择。
挑战:可缩放的网格
iOS 照片里那种顺滑的双指捏合缩放,从密集的小缩略图网格无缝过渡到全屏照片——用起来简直像魔法。
但在 React Native 里复刻它,是真的难。我们最终是这样解决的:
目标看起来很简单:捏合缩放,在不同网格密度之间平滑过渡,保持滚动位置,不掉帧。
我们的第一次尝试是最直觉的那种:根据捏合手势直接改 FlashList 上的 numColumns,对吧?
结果很糟糕。每次列数变化都会触发整个布局重新计算,滚动位置到处乱跳。几百张照片就卡得不行,手感很糟。动画看起来很假,因为确实就是假的。我们本质上是在给一个 React Native 天生就要从头重算的东西做动画。
诀窍:别变形,改用淡入淡出
在对 Claude Code 吼了不知道多少个小时之后,我们想通了:如果我们根本不改网格布局呢?
与其用一个会变形的网格,不如渲染多个完整的网格(每个列数固定)叠在一起。捏合手势过程中,只在它们之间做淡入淡出。
我们手上有:
-
Grid A,1 列
-
Grid B,3 列
-
Grid C,5 列
-
Grid D,9 列
每一个都是独立的 FlashList 或 LegendList,绝对定位,渲染同一批照片,只是 item 尺寸不同。捏合时并没有真的在缩放任何东西,只是在几套预渲染好的布局之间做交叉淡入淡出。
它看起来是一个网格在平滑缩放,实际上是四个网格在玩躲猫猫。
等等,这在性能上是不是非常昂贵?
直觉很准!在 Expo/React Native 的早期,光是想想渲染多个包含数千张照片的完整网格就已经很疯狂了。
不过,我们现在(幸运地)有这个资本了!
多亏了 FlashList 和 LegendList 这类实现了虚拟化的库,我们不必担心渲染这些布局的性能问题。有了虚拟化,只有可见内容才会被渲染!用户当前看不到的部分完全不影响性能!眼不见,心不烦。
下面这张图展示了背后的样子,每个缩放级别都用彩色边框标了出来:
注意这里的诀窍:要让各个缩放级别对齐,使得目标照片(用户捏合放大/缩小时所围绕的那张照片)在每个缩放级别下都处于屏幕上完全相同的绝对位置。
于是,react-native-zoom-grid 诞生了。
基于此,我们决定把这项工作打包成一个独立、可复用的开源组件,它在这套交互之上构建,提供一个高性能、可缩放的网格,可以用在任何 Expo/React Native 应用里。
如果你想在自己的应用里复现同样的效果,去 Github 看代码,或者直接从 npm 安装这个包。
实际效果 – Studio app
如果你想看看这个可缩放网格(以及前面提到的其他交互和 AI 功能)的实际效果。
试试 Studio,一个精心打造的应用,让 AI 离你的照片更近。
你可以在 App Store 上找到 Studio