@douyinfe/semi-ui
Version:
A modern, comprehensive, flexible design system and UI library. Connect DesignOps & DevOps. Quickly build beautiful React apps. Maintained by Douyin-fe team.
570 lines (501 loc) • 30.6 kB
Markdown
---
localeCode: zh-CN
order: 73
category: 展示类
title: Image 图片
icon: doc-image
brief: 用于展示和预览图片。
---
## 代码演示
### 如何引入
Image, ImagePreview 从 v2.20.0 版本开始支持
```jsx import
import { Image, ImagePreview } from '@douyinfe/semi-ui';
```
### 基本用法
通过 `src` 指定图片路径即可获取一个具有预览功能的图片,通过 `width`,`height` 指定图片的宽高
```jsx live=true dir="column"
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
```jsx live=true
import React from 'react';
import { Image } from '@douyinfe/semi-ui';
import { IconUploadError } from '@douyinfe/semi-icons';
() => (
<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`实现渐进加载
```jsx live=true
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` 不同来自定义预览图片
```jsx live=true
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 即可实现多图片预览
```jsx live=true dir="column"
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` 传入可以预览的图片
```jsx live=true
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 中。这会改变浮层 DOM 树位置,但不会改变视图渲染位置。
```jsx live=true dir="column"
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` 自定义预览底部操作区域
```jsx live=true dir="column"
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>
);
};
```
如果想基于默认底部操作区域自定义预览底部操作区域, 可以通过 renderPreviewMenu 的 menuItems 获取默认的 ReactNode, menuItems 是一个 ReactNode 数组,顺序和默认底部操作栏功能区域内容顺序一致,menuItems 参数从 v2.40.0 开始支持
```jsx live=true dir="column"
import React, { useMemo, useCallback } from 'react';
import { Image, ImagePreview, Divider, Tooltip } from '@douyinfe/semi-ui';
import { IconInfoCircle } 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 { menuItems } = props;
const customNode = <Tooltip content='我是一个自定义操作'><IconInfoCircle size="large" /></Tooltip>;
return (
<div style={{ display: 'flex', backgroundColor: 'rgba(0, 0, 0, 0.75)', alignItems: 'center', padding: '5px 16px', borderRadius: 4 }}>
{menuItems.slice(0, 3)}
<Divider layout="vertical" />
{menuItems.slice(3, 7)}
<Divider layout="vertical" />
{menuItems.slice(7)}
<Divider layout="vertical" />
{customNode}
</div>
);
}, []);
return (
<>
<ImagePreview
renderPreviewMenu={renderPreviewMenu}
>
{srcList.map((src, index) => (<Image key={index} src={src} width={200} alt={`lamp${index + 1}`} />))}
</ImagePreview>
</>
);
};
```
### 自定义预览顶部展示区
通过 `renderHeader` 可以自定义预览顶部展示区
```jsx live=true dir="column"
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>
</>
);
};
```
## API 参考
### Image
| 属性 | 说明 | 类型 | 默认值 | 版本 |
|-------------------|---------------------------------------|-------------------|------|------|
| alt | 图像描述 | string | - | |
| className | 自定义样式类名 | string | - | |
| crossOrigin | 透传给原生 img 标签的 crossorigin | 'anonymous'|'use-credentials'| - | |
| fallback | 加载失败容错地址或者自定义加载失败时的显示内容 | ReactNode | - | |
| height | 图片显示高度 | number | - | |
| imgCls | 自定义样式类名,透传给 img 节点 | string | - | |
| imgStyle | 自定义样式,透传给 img 节点 | CSSProperties | - | |
| onClick | 点击图片的回调 | (event: any) => void | - | |
| onError | 加载错误回调 | (event: Event) => void | - | |
| onLoad | 加载成功回调 | (event: Event) => void | - | |
| placeholder | 图片未加载时候的占位内容 | ReactNode | - | |
| preview | 预览参数,为 false 时候禁用预览 | boolean \| ImagePreview | - | |
| src | 图片获取地址 | string | - | |
| style | 自定义样式 | CSSProperties | - | |
| width | 图片显示宽度 | number | - | |
| setDownloadName | 设置图片下载名称 | (src: string) => string | - | 2.40.0 |
其他支持的属性同 [img](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#attributes)。其他属性将透传至底层的 img 节点。
### ImagePreview
| 属性 | 说明 | 类型 | 默认值 | 版本 |
|-------------------|--------------------------------------------------------------------------------------------------------------------------------------------------|------------------|-------|-----|
| adaptiveTip | 适应页面操作按钮提示 | string | "适应页面" | |
| className | 自定义样式类名 | string | - | |
| closable | 是否显示关闭按钮 | boolean | true | |
| closeOnEsc | 点击 esc 关闭预览 | boolean | true | |
| crossOrigin | 透传给预览图片的原生 img 标签的 crossorigin | 'anonymous'|'use-credentials'| - | |
| currentIndex | 受控属性,当前预览图片下标 | number | - | |
| defaultCurrentIndex | 首次展示图片下标 | number | - | |
| defaultVisible | 首次是否开启预览 | boolean | - | |
| disableDownload | 禁用下载 | boolean | false | |
| downloadTip | 下载操作按钮提示 | string | "下载" | |
| getPopupContainer | 指定父级 DOM,弹层将会渲染至该 DOM 中,自定义需要设置 container `position: relative` 这会改变浮层 DOM 树位置,但不会改变视图渲染位置。 | () => HTMLElement | () => document.body | |
| infinite | 是否无限循环 | boolean | false | |
| initialZoom | 预览图片初始缩放比例,仅在首次打开或切换到该图片时生效一次,会被 minZoom/maxZoom 限制 | number | - | 2.97.0 |
| maxZoom | 预览图片最大缩放比例 | number | 5 | 2.97.0 |
| minZoom | 预览图片最小缩放比例 | number | 0.1 | 2.97.0 |
| lazyLoad | 是否开启懒加载 | boolean | true | |
| lazyLoadMargin | 传给 options 中的rootMargin 参数,参考 [Intersection Observer API](https://developer.mozilla.org/zh-CN/docs/Web/API/Intersection_Observer_API#interfaces) | string | "0px 100px 100px 0px" | |
| maskClosable | 点击遮罩是否可关闭 | boolean | true | |
| nextTip | 下一步操作按钮提示 | string | "下一步" | |
| originTip | 原始尺寸操作按钮提示 | string | "原始尺寸" | |
| onChange | 切换图片触发的事件 | (index: number) => void | - | |
| onClose | 点击关闭按钮时的回调函数 | () => void | - | |
| onDownload | 图片下载回调函数 | (src: string, index: number) => void | - | |
| onDownloadError | 图片下载错误回调函数 | (src: string) => void | - | v2.54.0 |
| 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 | - | |
| previewCls | 自定义预览样式类名 | string | - | |
| previewStyle | 自定义预览样式 | object | - | |
| prevTip | 上一步操作按钮提示 | string | "上一步" | |
| renderCloseIcon | 自定义关闭icon | ReactElement | () => ReactElement | - | 2.85.0 |
| renderLeftIcon | 自定义向左icon | ReactElement | (index) => ReactElement | - | 2.85.0 |
| renderRightIcon | 自定义向右icon | ReactElement | (index) => ReactElement | - | 2.85.0 |
| 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 | |
| setDownloadName | 设置图片下载名称 | (src: string) => string | - | 2.40.0 |
### MenuProps
| 属性 | 说明 | 类型 | 版本 |
|-------------------|--------------------------|--------|-----|
| 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 | |
| menuItems | 默认底部预览操作区域功能按钮 ReactNode 数组 | ReactNode[] | 2.40.0 |
## 设计变量
<DesignToken/>