安装
在 React
项目目录运行以下命令:
pnpm:
pnpm i react-image-previewer
yarn:
yarn add react-image-previewer
或者用 npm:
npm install react-image-previewer
快速开始
在需要使用的地方引入 PhotoProvider
和 PhotoView
组件,基本用法:
import { PhotoProvider, PhotoView } from 'react-image-previewer';
export default function MyComponent() {
return (
<PhotoProvider>
<div>
{images.map((item, index) => (
<PhotoView key={index} src={item}>
<img src={item} alt="" />
</PhotoView>
))}
</div>
</PhotoProvider>
);
}
以 PhotoProvider
为界限,里面所有的 PhotoView
图片会按照运行顺序提取为一组图片预览画廊。当某个 <img />
被点击,则会定位到指定的图片并打开预览:
如果需要整个页面的图片形成一组进行预览,则可以在页面的入口处嵌套 PhotoProvider
。
注意: react-image-previewer
一次只在 DOM
中加载保留三张图片,因为每张图片都是一个合成层,这会消耗相当多的内存。
PhotoProvider 参数
自定义覆盖物
在 <PhotoProvider />
上添加 overlayRender
函数,可以实现自定义节点以及工具栏的渲染。
overlayRender
函数提供的参数有:
loading
- 当前预览图片是否加载中boolean
mode
- 当前模式drag
|slide
images
- 图片列表DataType[]
index
- 当前索引number
onIndexChange
- 索引改变回调(index: number) => void
visible
- 是否可见boolean
onClose
- 关闭事件回调() => void
overlayVisible
- 覆盖物是否可见boolean
overlay
- 图片覆盖物rotate
- 当前旋转角度number
onRotate
- 旋转事件回调(rotate: number) => void
scale
- 当前缩放number
onScale
- 缩放事件回调(scale: number) => void
拖拽模式
默认使用 slide
模式,使用拖拽模式需要在 <PhotoProvider />
上添加 mode="drag"
拖拽模式可以在当前预览视图内自由拖动图片,左右拖动不再自动切换图片。
缩放比例 scale
将按图片实际大小计算,最小缩放比例放开到 0.1
(图片实际大小的 10%),最大缩放比例为 6
倍。
slide
模式。循环预览
在 <PhotoProvider />
上添加参数 loop
可以更改循环预览数量。设为 boolean
启用/关闭,设为 number
类型则超过具体数量以开启循环预览,默认值为 3
张:
<PhotoProvider loop={4} />
蒙层透明度
预设为不透明,上拉过程中会慢慢减少透明度,可在 <PhotoProvider />
添加 maskOpacity
调整默认透明度,可设置为 0-1
之间的数字:
<PhotoProvider maskOpacity={0.5} />
蒙层事件
pullClosable
(默认 true
) 是否可以通过下拉关闭画廊,maskClosable
(默认 true
) 是否可以点击蒙版关闭画廊:
<PhotoProvider pullClosable={false} maskClosable={false} />
过渡效果
react-image-previewer
为保证更流畅体验,使用了相当多的 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
淡入淡出效果。关闭鼠标滚轮缩放
PhotoProvider
提供 enableMouseZoom
属性来控制是否响应鼠标滚轮事件。默认 true
<PhotoProvider enableMouseZoom={false} />
自定义反馈
<PhotoView />
如果加载失败,默认渲染空节点,你很可能需要根据当前 UI 设置属于自己的错误反馈节点。
PhotoProvider
提供以下属性自定义反馈节点渲染:
brokenElement
- 设置加载失败图案React.ReactElement
loadingElement
- 设置加载中图案React.ReactElement
根节点挂载
PhotoProvider
提供 portalContainer
属性设置挂载的根节点,默认挂载到 document.body
上
<PhotoProvider portalContainer={document.body} />
Ref
PhotoProvider
提供 ref
属性获取预览器的 DOM
节点
const ref = useRef()
<PhotoProvider ref={ref} />
裁切缩略图
<PhotoView />
组件会自动识别子节点类型,如果子节点为 <img />
,并且使用 CSS
设置了 object-fit
属性,则在缩放动画中附带裁切效果:
<PhotoView src={imageURL}>
<img src={imageURL} style={{ objectFit: 'cover' }} alt="" />
</PhotoView>
放大/缩小
-
在
mode
为drag
模式下,缩放最小可达0.1
(图片原始尺寸的 10%),1
为图片原始尺寸大小 -
在
mode
为slide
模式下,缩放最小可达1
(当前视图的大小) -
最大缩放比例可达
6
倍
添加对 scale
的控制即可实现放大/缩小按钮:
import { ZoomInIcon, ZoomOutIcon } from 'react-image-previewer/ui'
<PhotoProvider
overlayRender={({ onScale, scale }) => {
return (
<div style={{zIndex: 9999, position: 'absolute', bottom: '100px', left: '50%'}}>
<button onClick={() => onScale(scale + 1)}><ZoomOutIcon /></button>
<button onClick={() => onScale(scale - 1)}><ZoomOutIcon /></button>
</div>
);
}}
/>
旋转
react-image-previewer
默认不内置旋转控制按钮,但提供了旋转 API
。我们只需简单添加一个 SVG
图片即可实现旋转效果:
import { RotateLeftIcon, RotateRightIcon } from 'react-image-previewer/ui'
<PhotoProvider
overlayRender={({ rotate, onRotate }) => {
return (
<div style={{zIndex: 9999, position: 'absolute', bottom: '100px', left: '50%'}}>
<button onClick={() => onRotate(rotate + 90)}><RotateLeftIcon /></button>
<button onClick={() => onRotate(rotate - 90)}><RotateRightIcon /></button>
</div>)
}}
/>
以下示例实现了旋转/全屏/放大/缩小工具栏:
长图模式
移动设备加载长宽比例超过 3
倍的图片,默认会进入长图模式。即开始预览在图片顶部,图片宽度填充移动设备宽度:
触发节点
<PhotoView>
子组件可以是常规 React.HTMLAttributes
类型,如 <img />
、<div />
等。
如果是自定义组件则需要保证 onClick
事件能正常触发,转发 ref
到 HTMLElement
上保证打开/关闭动画源的正确性。
<PhotoView src={imageURL}>
<Button primary>Click</Button>
</PhotoView>
PhotoSlider
一般情况,使用 <PhotoProvider />
配合 <PhotoView />
已经满足大部分需求。如果有更高级的自定义控制,可以使用 <PhotoSlider />
。
<PhotoSlider />
的参数继承自 <PhotoProvider />
。会额外暴露几个 API:
mode
- 当前模式drag
|slide
images
图片列表,DataType[]
visible
是否可见受控属性boolean
onClose
关闭事件回调() => void
afterClose
关闭动画结束后回调() => void
index
当前索引受控属性number
onIndexChange
索引改变回调(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 = attrs.style.width;
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
。
UI
图标
react-image-previewer/ui
提供了一些工具栏图标
import {
ArrowLeftIcon,
ArrowRightIcon,
RotateLeftIcon,
RotateRightIcon,
ZoomInIcon,
ZoomOutIcon,
OneToOneIcon,
DownloadIcon,
LoadingIcon,
CloseIcon,
} from 'react-image-previewer/ui'
关闭按钮
react-image-previewer/ui
提供了一个关闭按钮 CloseButton
,它的默认位置在右上角
import { CloseButton } from 'react-image-previewer/ui'
<PhotoProvider
overlayRender={({ onClose }) => {
return <CloseButton onClick={onClose} />
}}
/>
适合滑动模式的工具栏
react-image-previewer/ui
提供了一个适合滑动模式的工具栏 SlideToolbar
提供了四个按钮,内置了他们的行为和位置,并且优化了移动设备
arrowLeft
左箭头,点击切换到上一张图片arrowRight
右箭头,点击切换到下一张图片countText
用于显示当前图片的索引2/5
download
下载按钮,点击下载当前图片
import { SlideToolbar, CloseButton } from 'react-image-previewer/ui'
<PhotoProvider
overlayRender={props => {
return (
<>
<SlideToolbar {...props} />
<CloseButton onClick={props.onClose} />
</>
)
}}
/>
可选项:
items
设置工具栏按钮,默认为 [ 'arrowLeft', 'countText', 'arrowRight', 'download']
import { SlideToolbar } from 'react-image-previewer/ui'
<SlideToolbar {...props} items={[ 'arrowLeft', 'countText', 'arrowRight', 'download']} />
除了通过 key
来使用他们外,您可以使用单独的按钮。
import { SlideArrowLeft, SlideArrowRight, SlideCountText, SlideDownload } from 'react-image-previewer/ui'
<>
<Tooltip content="prev"><SlideArrowLeft {...props} /></Tooltip>
<Tooltip content="next"><SlideArrowRight {...props} /></Tooltip>
<SlideCountText {...props} className="count-text" />
<SlideDownload {...props} className="download" />
<>
适合拖拽模式的工具栏
react-image-previewer/ui
提供了一个适合滑动模式的工具栏 DragToolbar
,它的默认位置在正下方
提供了11个按钮类型
arrowLeft
左箭头,点击切换到上一张图片countText
用于显示当前图片的索引2/5
arrowRight
右箭头,点击切换到下一张图片zoomOut
放大,点击执行放大操作scaleCount
显示当前缩放比例45%
zoomIn
缩小,点击执行缩小操作oneToOne
缩放比例调整为图片原始大小100%
download
下载按钮,点击下载当前图片rotateLeft
左旋转,点击向左旋转90
度rotateRight
右旋转,点击向右旋转90
度split
分割线,用于分割按钮组合
放大/缩放设置了递增比例
scale
<0.5
时,每次放大0.05
scale
<1.5
时,每次放大0.1
scale
<3
时,每次放大0.25
scale
<6
时,每次放大0.5
import { DragToolbar, CloseButton } from 'react-image-previewer/ui'
<PhotoProvider
overlayRender={props => {
return (
<>
<DragToolbar {...props} />
<CloseButton onClick={props.onClose} />
</>
)
}}
/>
可选项:
- items 设置工具栏按钮,默认为 [ 'arrowLeft', 'countText', 'arrowRight', 'split', 'zoomOut', 'scaleCount', 'zoomIn', 'oneToOne', 'split', 'download', 'split', 'rotateLeft', 'rotateRight']
- children 可以追加子节点到工具栏根节点
- className 设置额外的样式名称
除了使用 key
来设置按钮类型,还可以自由配置工具栏按钮对象,或者覆盖默认内置按钮组件
import { DragStyledItem, DragStyledLabel, DragDownload, DragScaleCount } from 'react-image-previewer/ui'
<DragToolbar {...props} items={
[
'arrowLeft', 'countText', 'arrowRight',
'split',
{
key: 'download',
component: props => {
return <Tooltip><DragDownload {...props} /></Tooltip>
}
},
{
key: 'scaleText',
component: props => {
return <DragScaleCount {...props} />{ 100 * props.scale }</DragScaleCount>
}
},
{
key: 'dog',
component: ({ index }) => {
return (
<DragStyledItem
onClick={() => {
setImages(prev => {
const result = [...prev]
result.splice(index, 1, dog.src)
return result
})
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="1em"
height="1em"
viewBox="0 0 768 768"
fill="currentColor"
>
<path d="M384 559.5c-75 0-138-45-163.5-111h327c-25.5 66-88.5 111-163.5 111zM271.5 352.5c-27 0-48-21-48-48s21-48 48-48 48 21 48 48-21 48-48 48zM496.5 352.5c-27 0-48-21-48-48s21-48 48-48 48 21 48 48-21 48-48 48zM384 640.5c141 0 256.5-115.5 256.5-256.5s-115.5-256.5-256.5-256.5-256.5 115.5-256.5 256.5 115.5 256.5 256.5 256.5zM384 64.5c177 0 319.5 142.5 319.5 319.5s-142.5 319.5-319.5 319.5-319.5-142.5-319.5-319.5 142.5-319.5 319.5-319.5z" />
</svg>
</DragStyledItem>
)
},
},
{
key: 'label',
component: ({ loading }) => {
return (
<DragStyledLabel>
I`m {loading ? 'loading' : 'loaded'}
</DragStyledLabel>
)
}
}
]
} />