安装
在 React 项目目录运行以下命令:
pnpm:
pnpm i react-photo-viewyarn:
yarn add react-photo-view或者用 npm:
npm install react-photo-view快速开始
在应用入口处引入 CSS 文件
import 'react-photo-view/dist/react-photo-view.css';在需要使用的地方引入 PhotoProvider 和 PhotoView 组件,基本用法:
import { PhotoProvider, PhotoView } from 'react-photo-view';
export default function MyComponent() {
return (
<PhotoProvider>
<div className="foo">
{images.map((item, index) => (
<PhotoView key={index} src={item}>
<img src={item} alt="" />
</PhotoView>
))}
</div>
</PhotoProvider>
);
}以 PhotoProvider为界限,里面所有的 PhotoView 图片会按照运行顺序提取为一组图片预览画廊。当某个 <img /> 被点击,则会定位到指定的图片并打开预览:



如果需要整个页面的图片形成一组进行预览,则可以在页面的入口处嵌套 PhotoProvider。
注意: react-photo-view 一次只在 DOM 中加载保留三张图片,因为每张图片都是一个合成层,这会消耗相当多的内存。
过渡动画
过渡效果
react-photo-view 为保证更流畅体验,使用了相当多的 CSS3 动画,在滚动时使用弹簧动画贴近原生滚动效果。CSS3 动画默认持续时间为 400ms,使用 cubic-bezier(0.25, 0.8, 0.25, 1) 动画函数。
可通过设置 speed 、easing 来自定义动画时间或函数:
<PhotoProvider
speed={() => 800}
easing={(type) => (type === 2 ? 'cubic-bezier(0.36, 0, 0.66, -0.56)' : 'cubic-bezier(0.34, 1.56, 0.64, 1)')}
/>speed 、easing为函数,可通过参数 type 判断当前动画的类型,type 的值有:
1- 打开中2- 关闭中3- 切换index

img 节点将额外添加 opacity 淡入淡出效果。裁切缩略图
<PhotoView /> 组件会自动识别子节点类型,如果子节点为 <img />,并且使用 CSS 设置了 object-fit 属性,则在缩放动画中附带裁切效果:
<PhotoView src={imageURL}>
<img src={imageURL} style={{ objectFit: 'cover' }} alt="" />
</PhotoView>
自定义工具栏
在 <PhotoProvider /> 上添加参数 toolbarRender 函数,该函数须返回一个 ReactNode 节点。
放大/缩小
添加对 scale 的控制即可实现放大/缩小按钮:
<PhotoProvider
toolbarRender={({ onScale, scale }) => {
return (
<>
<svg className="PhotoView-Slider__toolbarIcon" onClick={() => onScale(scale + 1)} />
<svg className="PhotoView-Slider__toolbarIcon" onClick={() => onScale(scale - 1)} />
</>
);
}}
/>旋转
react-photo-view 默认不内置旋转控制按钮,但提供了旋转 API。我们只需简单添加一个 SVG 图片即可实现旋转效果:
<PhotoProvider
toolbarRender={({ rotate, onRotate }) => {
return <svg className="PhotoView-Slider__toolbarIcon" onClick={() => onRotate(rotate + 90)} />;
}}
/>toolbarRender 函数提供的参数有:
images- 图片列表DataType[]index- 当前索引numberonIndexChange- 索引改变回调(index: number) => voidvisible- 是否可见booleanonClose- 关闭事件回调() => voidoverlayVisible- 覆盖物是否可见booleanoverlay- 图片覆盖物rotate- 当前旋转角度numberonRotate- 旋转事件回调(rotate: number) => voidscale- 当前缩放numberonScale- 缩放事件回调(scale: number) => void
以下示例实现了旋转/全屏/放大/缩小工具栏:






长图模式
移动设备加载长宽比例超过 3 倍的图片,默认会进入长图模式。即开始预览在图片顶部,图片宽度填充移动设备宽度:

触发节点
<PhotoView> 子组件可以是常规 React.HTMLAttributes 类型,如 <img />、<div /> 等。
如果是自定义组件则需要保证 onClick 事件能正常触发,转发 ref 到 HTMLElement 上保证打开/关闭动画源的正确性。
<PhotoView src={imageURL}>
<Button primary>Click</Button>
</PhotoView>PhotoSlider
一般情况,使用 <PhotoProvider /> 配合 <PhotoView /> 已经满足大部分需求。如果有更高级的自定义控制,可以使用 <PhotoSlider />。
<PhotoSlider /> 的参数继承自 <PhotoProvider />。会额外暴露几个 API:
images图片列表,DataType[]visible是否可见受控属性booleanonClose关闭事件回调() => voidafterClose关闭动画结束后回调() => voidindex当前索引受控属性numberonIndexChange索引改变回调(index: number) => void
visible 和 onClose 搭配形成受控,index 搭配 onIndexChange 形成受控。
export default function MyComponent() {
const [visible, setVisible] = useState(false);
const [index, setIndex] = useState(0);
return (
<>
<Button onClick={() => setIndex(2)}>setIndex(2)</Button>
<Button onClick={() => setIndex(4)}>setIndex(4)</Button>
<Button onClick={() => setVisible(true)} primary>
Click
</Button>
<PhotoSlider
images={images.map((item) => ({ src: item, key: item }))}
visible={visible}
onClose={() => setVisible(false)}
index={index}
onIndexChange={setIndex}
/>
</>
);
}预览更多
<PhotoView /> 无子组件的情况下也能加入到预览队列,只是无法直接触发当前图片的预览。通过这个特性,我们可以实现经过一个入口,预览更多图片的功能:
<PhotoProvider>
{images.map((item, index) => (
<PhotoView key={index} src={item}>
{index < 2 ? <img src={item} alt="" /> : undefined}
</PhotoView>
))}
</PhotoProvider>

自定义渲染节点
<PhotoView /> 默认内置支持图片的预览,只需传递 src 即可,组件会自动识别图片的宽高。
我们也可以通过 render 函数实现自定义节点的预览,以支持 video 或 pdf 的需求。
const elementSize = 400;
function MyComponent() {
return (
<PhotoView
width={elementSize}
height={elementSize}
render={({ scale, attrs }) => {
const width = parseFloat((attrs?.style?.width ?? 0) as string);
const offset = (width - elementSize) / elementSize;
const childScale = scale === 1 ? scale + offset : 1 + offset;
return (
<div {...attrs}>
<div style={{ transform: `scale(${childScale})`, width: elementSize, transformOrigin: '0 0' }}>
<div>Hello world</div>
<Button>button</Button>
<input onMouseDown={(e) => e.stopPropagation()} />
</div>
</div>
);
}}
>
<Button primary>Click</Button>
</PhotoView>
);
}注意:画廊在缩放之后,先会用 scale 进行缩放,在动画结束后则调整为当前 scale 所对应的宽高,并重置 scale 为 1。
PhotoProvider 参数
自定义描述
在 <PhotoProvider /> 上添加 overlayRender 函数,可以实现自定义节点,函数参数返回值与 toolbarRender 一致。






循环预览
在 <PhotoProvider /> 上添加参数 loop 可以更改循环预览数量。设为 boolean 启用/关闭,设为 number 类型则超过具体数量以开启循环预览,默认值为 3 张:
<PhotoProvider loop={4} />


蒙层透明度
预设为不透明,上拉过程中会慢慢减少透明度,可在 <PhotoProvider /> 添加 maskOpacity 调整默认透明度,可设置为 0-1 之间的数字:
<PhotoProvider maskOpacity={0.5} />
不显示顶部按钮区域
设置 bannerVisible 为 false 则可以不显示顶部区域:
<PhotoProvider bannerVisible={false} />
蒙层事件
pullClosable(默认 true) 是否可以通过下拉关闭画廊,maskClosable(默认 true) 是否可以点击蒙版关闭画廊:
<PhotoProvider pullClosable={false} maskClosable={false} />
自定义反馈
<PhotoView /> 如果加载失败,默认渲染空节点,你很可能需要根据当前 UI 设置属于自己的错误反馈节点。
brokenElement- 设置加载失败图案React.ReactElementloadingElement- 设置加载中图案React.ReactElement