localeCode: zh-CN order: 54 category: 展示类 title: Image 图片 icon: doc-image
Image, ImagePreview 从 v2.20.0 版本开始支持
import { Image, ImagePreview } from '@douyinfe/semi-ui';
通过 src 指定图片路径即可获取一个具有预览功能的图片,通过 width,height 指定图片的宽高
import React from 'react';
import { Image } from '@douyinfe/semi-ui';
() => (
<Image
width={360}
height={200}
src="https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/abstract.jpg"
/>
);
可通过 fallback 自定义加载失败的占位图,该参数类型支持 string 和 ReactNode
import React from 'react';
import { Image } from '@douyinfe/semi-ui';
() => (
<div style={{ display: 'flex', alignItem: 'center', flexDirection: 'column' }}>
<span>加载失败默认样式</span>
<Image
width={200}
height={200}
src="https://load-error.jpeg"
/>
<br />
<span>自定义加载失败占位图</span>
<Image
width={200}
height={200}
src="https://load-error.jpeg"
fallback={<IconUploadError style={{ fontSize: 50 }} />}
/>
</div>
);
大图可通过placeholder实现渐进加载
import React from 'react';
import { Image, Button } from '@douyinfe/semi-ui';
() => {
const [timestamp, setTimestamp] = React.useState('');
return (
<>
<Image
width={300}
height={200}
src={`https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/abstract-big.png?${timestamp}`}
placeholder={<Image
src='https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/abstract-small.jpeg'
width={300}
height={200}
preview={false}
/>}
/>
<br />
<Button
theme={'solid'}
onClick={() => {
setTimestamp(Date.now());
}}
style={{ marginTop: 10 }}
>Reload</Button>
</>
);
};
可以通过设置 Image 组件的 src 和 preview 参数中的 src 不同来自定义预览图片
import React from 'react';
import { Image } from '@douyinfe/semi-ui';
() => {
return (
<Image
width={300}
height={200}
src={'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/abstract-small.jpeg'}
preview={{
src: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/abstract-big.png'
}}
/>
);
};
使用 ImagePreview 包裹 Image 即可实现多图片预览
import React, { useMemo } from 'react';
import { Image, ImagePreview } from '@douyinfe/semi-ui';
() => {
const srcList = useMemo(() => ([
"https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/abstract.jpg",
"https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/sky.jpg",
"https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/greenleaf.jpg",
"https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/colorful.jpg",
]), []);
return (
<ImagePreview>
{srcList.map((src, index) => {
return (
<Image
key={index}
src={src}
width={200}
alt={`lamp${index + 1}`}
style={{ marginRight: 5 }}
/>
);
})}
</ImagePreview>
);
};
预览组件 ImagePreview 可以单独使用,通过 visible 和 onVisibleChange 控制是否预览,通过 src 传入可以预览的图片
import React, { useMemo, useCallback } from 'react';
import { ImagePreview, Button } from '@douyinfe/semi-ui';
() => {
const srcList = useMemo(() => ([
"https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/abstract.jpg",
"https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/sky.jpg",
"https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/greenleaf.jpg",
"https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/colorful.jpg",
]), []);
const [visible1, setVisible1] = useState(false);
const [visible2, setVisible2] = useState(false);
const visibleChange1 = useCallback((v) => {
setVisible1(v);
}, []);
const visibleChange2 = useCallback((v) => {
setVisible2(v);
}, []);
const onButton1Click = useCallback((v) => {
setVisible1(true);
}, []);
const onButton2Click = useCallback((v) => {
setVisible2(true);
}, []);
return (
<>
<Button onClick={onButton1Click}>Preview single Image</Button>
<ImagePreview
src={"https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/sky.jpg"}
visible={visible1}
onVisibleChange={visibleChange1}
/>
<br />
<Button onClick={onButton2Click} style={{ marginTop: 20 }}>Preview multiple Images</Button>
<ImagePreview
src={srcList}
visible={visible2}
onVisibleChange={visibleChange2}
/>
</>
);
};
可以通过 getPopupContainer 指定预览组件的父级 DOM(需要指定 position: relative),图片预览将会渲染至该 DOM 中
import React, { useMemo } from 'react';
import { Image, ImagePreview } from '@douyinfe/semi-ui';
() => {
const srcList = useMemo(() => ([
"https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/abstract.jpg",
"https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/sky.jpg",
"https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/greenleaf.jpg",
]), []);
return (
<>
<div
id="container"
style={{
height: 400,
position: "relative"
}}
>
<ImagePreview
getPopupContainer={() => {
const node = document.getElementById("container");
return node;
}}
style={{
height: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
{srcList.map((src, index) => {
return (
<Image
key={index}
src={src}
width={200}
alt={`lamp${index + 1}`}
style={{ marginRight: 5 }}
/>
);
})}
</ImagePreview>
</div>
</>
);
};
可以使用 renderPreviewMenu 自定义预览底部操作区域
import React, { useMemo, useCallback } from 'react';
import { Image, ImagePreview, Button } from '@douyinfe/semi-ui';
import { IconChevronLeft, IconChevronRight, IconMinus, IconPlus, IconRotate, IconDownload, IconRealSizeStroked, IconWindowAdaptionStroked } from "@douyinfe/semi-icons";
() => {
const srcList = useMemo(() => ([
"https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/abstract.jpg",
"https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/sky.jpg",
"https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/greenleaf.jpg",
]), []);
const renderPreviewMenu = useCallback((props) => {
const {
ratio,
disabledPrev,
disabledNext,
disableZoomIn,
disableZoomOut,
disableDownload,
onDownload,
onNext,
onPrev,
onRotateLeft,
onRatioClick,
onZoomIn,
onZoomOut,
} = props;
return (
<div
style={{
background: "grey",
height: 40,
width: 280,
display: "flex",
alignItems: "center",
justifyContent: "space-around",
borderRadius: 3,
}}
>
<Button
icon={<IconChevronLeft size="large" />}
type="tertiary"
onClick={!disabledPrev ? onPrev : undefined}
disabled={disabledPrev}
/>
<Button
icon={<IconChevronRight size="large" />}
type="tertiary"
onClick={!disabledNext ? onNext : undefined}
disabled={disabledNext}
/>
<Button
icon={<IconMinus size="large" />}
type="tertiary"
onClick={!disableZoomOut ? onZoomOut : undefined}
disabled={disableZoomOut}
/>
<Button
icon={<IconPlus size="large" />}
type="tertiary"
onClick={!disableZoomIn ? onZoomIn : undefined}
disabled={disableZoomIn}
/>
<Button
icon={ratio === "adaptation" ? <IconRealSizeStroked size="large" /> : <IconWindowAdaptionStroked size="large" />}
type="tertiary"
onClick={onRatioClick}
/>
<Button
icon={<IconRotate size="large" />}
type="tertiary"
onClick={onRotateLeft}
/>
<Button
icon={<IconDownload size="large" />}
type="tertiary"
onClick={!disableDownload ? onDownload : undefined}
disabled={disableDownload}
/>
</div>);
}, []);
return (
<ImagePreview renderPreviewMenu={renderPreviewMenu}>
{srcList.map((src, index) => {
return (
<Image
key={index}
src={src}
width={200}
alt={`lamp${index + 1}`}
style={{ marginRight: 5 }}
/>
);
})}
</ImagePreview>
);
};
通过 renderHeader 可以自定义预览顶部展示区
import React, { useMemo } from 'react';
import { Image, ImagePreview } from '@douyinfe/semi-ui';
() => {
const srcList = useMemo(() => ([
"https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/abstract.jpg",
"https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/sky.jpg",
]), []);
return (
<>
<ImagePreview
renderHeader={(title) => (
<div
style={{ width: "100%", height: "100%", display: "flex", alignItems: "center", justifyContent: "center" }}>
<span style={{ background: "black", padding: '0 10px' }}>自定义标题:{title}</span>
</div>
)}
>
<div >
{srcList.map((src, index) => {
return (
<Image
key={index}
src={src}
width={200}
alt={`lamp${index + 1}`}
preview={{
previewTitle: `lamp${index + 1}`,
}}
style={{ marginRight: 5 }}
/>
);
})}
</div>
</ImagePreview>
</>
);
};
| 属性 | 说明 | 类型 | 默认值 | 版本 |
|---|---|---|---|---|
| alt | 图像描述 | string | - | |
| className | 自定义样式类名 | string | - | |
| crossOrigin | 透传给原生 img 标签的 crossorigin | 'anonymous'|'use-credentials' | - | |
| fallback | 加载失败容错地址或者自定义加载失败时的显示内容 | ReactNode | - | |
| height | 图片显示高度 | number | - | |
| onError | 加载错误回调 | (event: Event) => void | - | |
| onLoad | 加载成功回调 | (event: Event) => void | - | |
| placeholder | 图片未加载时候的占位内容 | ReactNode | - | |
| preview | 预览参数,为 false 时候禁用预览 | boolean | ImagePreview | - | |
| src | 图片获取地址 | string | - | |
| style | 自定义样式 | CSSProperties | - | |
| width | 图片显示宽度 | number | - |
| 属性 | 说明 | 类型 | 默认值 | 版本 |
|---|---|---|---|---|
| adaptiveTip | 适应页面操作按钮提示 | string | "适应页面" | |
| className | 自定义样式类名 | string | - | |
| closable | 是否显示关闭按钮 | boolean | true | |
| closeOnEsc | 点击 esc 关闭预览 | boolean | true | |
| currentIndex | 受控属性,当前预览图片下标 | number | - | |
| defaultCurrentIndex | 首次展示图片下标 | number | - | |
| defaultVisible | 首次是否开启预览 | boolean | - | |
| disableDownload | 禁用下载 | boolean | false | |
| downloadTip | 下载操作按钮提示 | string | "下载" | |
| getPopupContainer | 指定父级 DOM,弹层将会渲染至该 DOM 中,自定义需要设置 container position: relative |
() => HTMLElement | () => document.body | |
| infinite | 是否无限循环 | boolean | false | |
| lazyLoad | 是否开启懒加载 | boolean | true | |
| lazyLoadMargin | 传给 options 中的rootMargin 参数,参考 Intersection Observer API | string | "0px 100px 100px 0px" | |
| maskClosable | 点击遮罩是否可关闭 | boolean | true | |
| nextTip | 下一步操作按钮提示 | string | "下一步" | |
| originTip | 原始尺寸操作按钮提示 | string | "原始尺寸" | |
| onChange | 切换图片触发的事件 | (index: number) => void | - | |
| onClose | 点击关闭按钮时的回调函数 | () => void | - | |
| onDownload | 图片下载回调函数 | (src: string, index: number) => void | - | |
| onRotateLeft | 旋转图片的回调 | (angle: number) => void | - | |
| onNext | 向后切换图片的回调 | (index: number) => void | - | |
| onPrev | 向前切换图片的回调 | (index: number) => void | - | |
| onZoomIn | 图片放大时的回调函数 | (zoom: number) => void | - | |
| onZoomOut | 图片缩小时的回调函数 | (zoom: number) => void | - | |
| onVisibleChange | 切换可见状态触发的回调 | (visible: boolean) => void | - | |
| preLoad | 是否开启预加载 | boolean | true | |
| preLoadGap | 预加载的步长 | number | 2 | |
| previewTitle | 自定义预览 title | ReactNode | - | |
| prevTip | 上一步操作按钮提示 | string | "上一步" | |
| renderHeader | 自定义渲染预览顶部信息 | (info: ReactNode) => ReactNode | - | |
| renderPreviewMenu | 自定义渲染预览底部菜单信息 | (props: MenuProps) => ReactNode; | - | |
| rotateTip | 旋转操作按钮提示 | string | "旋转" | |
| showTooltip | 是否展示底部操作区提示 | boolean | false | |
| src | 图片列表信息 | string | string[] | - | |
| style | 自定义样式 | CSSProperties | - | |
| viewerVisibleDelay | 隐藏预览操作按钮前的无操作时长 | number | 10000 | |
| visible | 受控属性,是否预览 | boolean | - | |
| zIndex | 预览层层级 | number | 1070 | |
| zoomInTip | 放大操作按钮提示 | string | "放大" | |
| zoomOutTip | 缩小操作按钮提示 | string | "缩小" | |
| zoomStep | 图片每次缩小/放大比例 | number | 0.1 |
| 属性 | 说明 | 类型 |
|---|---|---|
| curPage | 当前图片页下标 | number |
| disabledPrev | 是否禁用向左切换按钮 | boolean |
| disabledNext | 是否禁用向右切换按钮 | boolean |
| disableDownload | 是否禁用下载按钮 | boolean |
| max | 图片缩放最大比例 | number |
| min | 图片缩放最小比例 | number |
| onDownload | 图片下载的调用函数 | () => void |
| onZoomIn | 图片放大时的调用函数 | () => void |
| onZoomOut | 图片缩小时的调用函数 | () => void |
| onPrev | 向前切换图片的调用函数 | () => void |
| onNext | 向后切换图片的调用函数 | () => void |
| onRotateLeft | 逆时针旋转图片的调用函数 | () => void |
| onRotateRight | 顺时针旋转图片的调用函数 | () => void |
| ratio | 原始尺寸或适应页面按钮状态 | "adaptation" | "realSize" |
| step | 缩放的比例步长 | number |
| totalNum | 可预览的总图片数 | number |
| zoom | 当前图片缩放比例 | number |