查看: 121|回复: 3

鸿蒙 React Native 图片缩放组件接入与踩坑

[复制链接]
发表于 1 小时前 | 显示全部楼层 |阅读模式
在鸿蒙环境下做 React Native 应用,图片预览是最常见的高频场景之一。真正麻烦的是手势缩放、多图切换和下滑关闭这些交互,如果组件依赖原生代码,就得针对鸿蒙重新适配。react-native-image-zoom-viewer 是一个纯 JS 实现的图片缩放浏览组件,不依赖任何原生链接,在鸿蒙上可以直接运行。它内部依赖的 react-native-image-pan-zoom 也是纯 JS 手势计算库,所以整个方案可以做到开箱即用。

安装很简单,直接使用 yarn 添加依赖即可,3.0.1 版本就是纯 JS 库,不需要配置原生链接。
  1. yarn add react-native-image-zoom-viewer
复制代码

基础用法是把 ImageViewer 放在 Modal 中,通过 imageUrls 传入图片数组,然后设置 enableSwipeDown 和 onSwipeDown 来实现下滑关闭。onClick 也可以用来在轻点时退出预览。下面是一个最简单的实现。
  1. import React, { useState } from 'react';
  2. import { Modal, View, Text, TouchableOpacity } from 'react-native';
  3. import ImageViewer from 'react-native-image-zoom-viewer';
  4. const images = [
  5.   { url: 'https://picsum.photos/id/1/800/600', width: 800, height: 600 },
  6.   { url: 'https://picsum.photos/id/10/1200/800', width: 1200, height: 800 },
  7. ];
  8. export default function Gallery() {
  9.   const [visible, setVisible] = useState(false);
  10.   return (
  11.     <Modal visible={visible} transparent onRequestClose={() => setVisible(false)}>
  12.       <ImageViewer
  13.         imageUrls={images}
  14.         index={0}
  15.         onClick={() => setVisible(false)}
  16.         enableSwipeDown
  17.         onSwipeDown={() => setVisible(false)}
  18.       />
  19.     </Modal>
  20.   );
  21. }
复制代码

实际项目里通常不会只用默认样式,还需要自定义头部信息、加载状态,以及监听当前预览到第几张。ImageViewer 提供了 renderHeader 和 loadingRender 这类接口,配合 onChange 可以拿到当前索引。下面这个完整示例展示了如何实现一个带关闭按钮、页码显示和加载动画的图片浏览器。
  1. import React, { useState } from 'react';
  2. import { Modal, View, Text, TouchableOpacity, ActivityIndicator } from 'react-native';
  3. import ImageViewer from 'react-native-image-zoom-viewer';
  4. const images = [
  5.   { url: 'https://picsum.photos/id/1/800/600' },
  6.   { url: 'https://picsum.photos/id/10/1200/800' },
  7.   { url: 'https://picsum.photos/id/100/600/900' },
  8. ];
  9. export default function ImageViewerDemo() {
  10.   const [visible, setVisible] = useState(false);
  11.   const [currentIndex, setCurrentIndex] = useState(0);
  12.   return (
  13.     <Modal visible={visible} transparent animationType="fade">
  14.       <ImageViewer
  15.         imageUrls={images}
  16.         index={currentIndex}
  17.         enableSwipeDown
  18.         enablePreload
  19.         saveToLocalByLongPress
  20.         backgroundColor="#000"
  21.         onClick={() => setVisible(false)}
  22.         onSwipeDown={() => setVisible(false)}
  23.         onChange={(index) => console.log('切换到第', index! + 1, '张')}
  24.         renderHeader={(currentIndex) => (
  25.           <View style={{
  26.             position: 'absolute', top: 0, left: 0, right: 0,
  27.             paddingTop: 50, paddingHorizontal: 16, flexDirection: 'row',
  28.             justifyContent: 'space-between', zIndex: 100,
  29.           }}>
  30.             <TouchableOpacity onPress={() => setVisible(false)}
  31.               style={{ backgroundColor: 'rgba(0,0,0,0.5)', borderRadius: 20, padding: 8 }}>
  32.               <Text style={{ color: '#fff' }}>✕ 关闭</Text>
  33.             </TouchableOpacity>
  34.             <Text style={{ color: '#fff' }}>
  35.               {(currentIndex || 0) + 1} / {images.length}
  36.             </Text>
  37.           </View>
  38.         )}
  39.         loadingRender={() => (
  40.           <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
  41.             <ActivityIndicator size="large" color="#fff" />
  42.           </View>
  43.         )}
  44.       />
  45.     </Modal>
  46.   );
  47. }
复制代码

接入过程中有几个容易踩的坑,这里整理一下。

第一个是图片数组格式。imageUrls 不接受 string[],必须使用对象数组。很多新手直接传入 url 字符串列表,结果图片不显示。
  1. // 错误:imageUrls 不接受 string[]
  2. const images = ['url1', 'url2'];
  3. // 正确:必须用对象数组
  4. const images = [
  5.   { url: 'https://example.com/image.jpg' },
  6.   { url: '', props: { source: require('./local.png') } },
  7. ];
复制代码

第二个是本地图片的传入方式。如果要展示本地图片,需要通过 props.source 来传,而不能直接塞一个 path 进去。url 可以留空,props.source 里用 require 加载资源。
  1. const images = [{
  2.   url: '',
  3.   props: {
  4.     source: require('./assets/photo.png'),
  5.   },
  6. }];
复制代码

第三个是 index 变化不触发重渲染。ImageViewer 内部使用 getDerivedStateFromProps 检测 index prop 的变化,但在某些场景下父组件直接修改 index 可能不会让视图跳转到对应图片。一个可靠的解决方法是给 ImageViewer 加 key={index},强制重建组件。
  1. // 可能不生效
  2. export default function Gallery() {
  3.   const [index, setIndex] = useState(0);
  4.   return <ImageViewer imageUrls={images} index={index} />;
  5. }
  6. // 使用 key 强制重建
  7. const [index, setIndex] = useState(0);
  8. <ImageViewer key={index} imageUrls={images} index={index} />
复制代码

第四个是 onClick 和 onSwipeDown 可能冲突。同时开启 enableSwipeDown 并设置 onClick 后,轻点屏幕时可能两个回调都会被触发。尽量避免同时使用,或者在 onClick 里加防抖或手势判断。

第五个是长按保存菜单。saveToLocalByLongPress 在鸿蒙上长按图片时会弹出一个内置的 JS 菜单,包含“保存图片”和“取消”。这个菜单本身可以正常工作,但保存动作依赖 CameraRoll 模块,如果工程里没有安装这个模块,保存会静默失败,不会报错也不会弹提示。

第六个是图片宽高信息。imageUrls 里的对象可以带 width 和 height,建议尽量提供。提供后组件可以立即按尺寸渲染,不用等图片加载完再自动测量;不提供的话组件会自己去拉取图片尺寸,首次渲染会有明显延迟。
  1. const images = [
  2.   { url: '...' }, // 自动获取尺寸,稍慢
  3.   { url: '...', width: 800, height: 600 }, // 指定尺寸,立即渲染
  4. ];
复制代码

性能方面,可以交给几个配置项来处理。开启 enablePreload 后,库会预加载当前图片前后各一张,减少切换时的等待。对于图片数量较多的场景,建议只在 Modal 打开时才渲染 ImageViewer,关闭时直接销毁,避免在后台做无谓的视图计算。网络图片也应尽量先压缩到合适尺寸,最大宽度控制在 2048px 左右,既能保证清晰度,又不会让内存和带宽压力太大。

整体来看,react-native-image-zoom-viewer 是目前鸿蒙 RN 项目里比较值得推荐的图片预览方案。它不依赖原生代码,功能覆盖了双指缩放、双击放大、多图切换、下滑关闭和长按保存等核心交互,接入成本低。真正需要留神的只有数据格式、状态同步和手势冲突这几个点,处理好了就能稳定用起来。
回复

使用道具 举报

发表于 1 小时前 | 显示全部楼层

Re: 鸿蒙 React Native 图片缩放组件接入与踩坑

看到楼主分享鸿蒙上接入 React Native 图片缩放组件的经验,很实用。特别是这种纯 JS 实现的方案,在鸿蒙这种需要重新适配原生模块的环境下,确实能省掉不少麻烦。我之前也折腾过类似的图片预览,native 依赖在鸿蒙上经常要各种改配置,像这种开箱即用的库就很舒服。 想请教一下楼主,在鸿蒙上实际运行的时候,图片数量多或者单图分辨率比较大的情况下,手势缩放和滑动的流畅度怎么样?有没有遇到过内存占用偏高或者图片加载白屏的问题?另外,`renderHeader` 里的 `currentIndex` 这个参数看起来有点绕,楼主实际用的时候是怎么处理的?
回复 支持 反对

使用道具 举报

发表于 1 小时前 | 显示全部楼层

Re: 鸿蒙 React Native 图片缩放组件接入与踩坑

感谢楼主分享,这个纯 JS 方案对鸿蒙适配确实省心不少。我最近也在折腾 RN 的图片预览,之前用原生组件在鸿蒙上各种兼容问题,看到这个库直接跑通有点惊喜。下滑关闭和预加载这两个点很实用,回头我把长按保存也加上试试。另外想问下楼主,多图快速滑动时内存表现怎么样?有没有做缩略图或懒加载的优化?
回复 支持 反对

使用道具 举报

发表于 1 小时前 | 显示全部楼层

Re: 鸿蒙 React Native 图片缩放组件接入与踩坑

这个组件确实省心,纯 JS 在鸿蒙上不用折腾原生适配,直接就能跑。下滑关闭和手势缩放这些交互在真机上体验很顺滑,renderHeader 自定义也灵活,我们项目里就是用它做的图片预览,踩坑不多。感谢分享!
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-8-10 19:09 , Processed in 0.022431 second(s), 17 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部