# MPhotoView 图片预览

- 来源：[E10 UI 组件库](https://www.e-cology.com.cn/ui/mobile/photo-view/freepass)
- 平台：移动端
- 维护者：屈正巍
- Issue：http://gitlab.ecology10.cn/FRONTEND/weapp-ui/-/issues?state=all&search=[MPhotoView]

## 代码演示

### 直接使用

采用 showImage 函数，自定义预览方式

```jsx
import React from 'react';
import { MPhotoView, MButton, PhotoViewData } from '@weapp/ui';

const images: PhotoViewData[] = [
  {
    id: '1',
    src: '/build/ui/demo/assets/photo-view/1.jpg',
    // src: '/build/ui/demo/assets/photo-view/10.jpg',
    originSrc: '/build/ui/demo/assets/photo-view/2.jpeg',
    intro: '这是两只小肥羊',
    user: '屈正巍',
    time: '2021-10-22 18:00:00',
    imgSize: 1024,
  },
  {
    id: '2',
    src: '/build/ui/demo/assets/photo-view/2.jpeg',
  },
  {
    id: '3',
    src: '/build/ui/demo/assets/photo-view/3.jpeg',
  },
  {
    id: '4',
    src: '/build/ui/demo/assets/photo-view/4.jpeg',
  },
  {
    id: '5',
    src: "/api/file/preview?fileId=1143900054845579267&type=redirect",
    fileId: '1143900054845579267',
    intro: '测试fileId2',
    user: '屈正巍2',
    time: '2023-03-14 18:00:00'
  },
  {
    id: '6',
    src: "/api/file/preview?fileId=1143922096542244868&type=redirect",
    fileId: '1143922096542244868',
    intro: '测试fileId2',
    user: '屈正巍2',
    time: '2023-03-14 18:00:00'
  },
  {
    id: '7',
    src: "",
  },
]

export default class PhotoViewDemo extends React.PureComponent<{}> {

  preview = (index: number) => MPhotoView.showImage(images, index)
  preview0 = () => MPhotoView.showImage(images, 0)
  preview1 = () => MPhotoView.showImage(images, 1)
  preview2 = () => MPhotoView.showImage(images, 2)

  render() {
    return (
      <div>
        <MButton onClick={this.preview0}>预览第1张</MButton>
        <MButton onClick={this.preview1}>预览第2张</MButton>
        <MButton onClick={this.preview2}>预览第3张</MButton>
      </div>
    );
  }
}
```

### 引入组件

采用 MPhotoView 组件，自定义控制visible

```jsx
import React from 'react';
import { MPhotoView } from '@weapp/ui';
const images = [
  {
    id: '1',
    src: '/build/ui/demo/assets/photo-view/1.jpg',
    // src: '/api/file/preview?type=redirect&imgFormat=image&fileId=1149132338570665986',
    originSrc: '/build/ui/demo/assets/photo-view/2.jpeg',
    // originSrc: '/api/file/preview?type=redirect&imgFormat=image&fileId=1149132338570665986',
    intro: '这是两只小肥羊',
    user: '屈正巍',
    time: '2021-10-22 18:00:00',
    imgSize: 1024,
  },
  {
    id: '2',
    src: '/build/ui/demo/assets/photo-view/2.jpeg',
  },
  {
    id: '3',
    src: '/build/ui/demo/assets/photo-view/3.jpeg',
  },
  {
    id: '4',
    src: '/build/ui/demo/assets/photo-view/4.jpeg',
  },
  {
    id: '5',
    src: "/api/file/preview?fileId=1143900054845579267&type=redirect",
    fileId: '1143900054845579267',
    intro: '测试fileId2',
    user: '屈正巍2',
    time: '2023-03-14 18:00:00'
  },
  {
    id: '6',
    src: "/api/file/preview?fileId=1143922096542244868&type=redirect",
    fileId: '1143922096542244868',
    intro: '测试fileId2',
    user: '屈正巍2',
    time: '2023-03-14 18:00:00'
  },
  {
    id: '7',
    src: "",
  },
]

const Img = (props: any) => {
  const { index, onClick } = props;
  function preview() {
    onClick(index);
  }
  return <img width='100' height='100' src={props.src} alt={props.src} onClick={preview} />
}

export default class PhotoViewDemo extends React.PureComponent<{}> {
  state = {
    visible: false,
    index: 0
  }
  preview = (index: number) => this.setState({ visible: true, index })

  onClose = () => this.setState({ visible: false })

  render() {
    return (
      <div>
        {images.map((item, index) =>
          <Img
           
            key={item.id}
            src={item.src}
            index={index}
            onClick={this.preview}
          />
        )}
        <MPhotoView visible={this.state.visible} photoIndex={this.state.index} data={images} onClose={this.onClose} disableSingleImageTouch />
      </div>
    )
  }
}
```

### 查看原图

可以通过传入originSrc参数，点击原图按钮切换原图

```jsx
import { PureComponent } from 'react';
import { MPhotoView, MInput, Button, constants, PhotoViewData } from '@weapp/ui';

const prefixCls = `${constants.uiPcClsPrefix}-demo-photoview`;

const Img = (props: any) => {
  const { index, onClick } = props;
  function preview() {
    onClick(index);
  }
  return <img style={{ maxHeight: 100 }} alt={props.src} src={props.src} onClick={preview} />
}

export default class PhotoViewDemo extends PureComponent<{}> {

  state = {
    visible: false,
    index: 0,
    src: '/build/ui/demo/assets/photo-view/1.jpg',
    // src: '/api/file/preview?type=redirect&imgFormat=image&fileId=1152830915191767044',
    originSrc: '/build/ui/demo/assets/photo-view/9.jpg',
    // originSrc: '/api/file/preview?type=redirect&imgFormat=image&fileId=1149132338570665986',
    src2: '/build/ui/demo/assets/photo-view/2.jpeg',
    originSrc2: '/build/ui/demo/assets/photo-view/8.jpg'
  }
  preview = (index: number) => { this.setState({ index }, () => { this.setState({ visible: true }) }) }
  onClose = () => this.setState({ visible: false })
  onSrcChange = (value: any) => this.setState({ src: value })
  onOriginSrcChange = (value: any) => this.setState({ originSrc: value })
  onSrc2Change = (value: any) => this.setState({ src2: value })
  onOriginSrc2Change = (value: any) => this.setState({ originSrc2: value })
  getImages = () => {
    const { src, originSrc, src2, originSrc2 } = this.state
    const image: PhotoViewData = {
      id: '1',
      src: src,
      imgSize: 1024,
      intro: '这是两只小肥羊',
      user: '屈正巍',
      time: '2021-10-22 18:00:00'
    }
    if (originSrc) image.originSrc = originSrc
    const image2: PhotoViewData = {
      id: '2',
      src: src2,
      imgSize: 2048,
      intro: '这是两只小肥羊吗，不是吧',
      user: '屈正巍',
      time: '2021-10-22 18:00:00'
    }
    if (originSrc2) image2.originSrc = originSrc2
    return [image, image2];
  }
  showImage1 = () => { MPhotoView.showImage(this.getImages()) }
  showImage2 = () => { MPhotoView.showImage(this.getImages(), 1) }
  render() {
    const { src, originSrc, src2, originSrc2 } = this.state
    const images = this.getImages()
    return (
      <div className={prefixCls}>
        <div style={{ marginRight: 5 }}>图片src地址：</div>
        <MInput
          placeholder="请输入图片地址"
          value={src}
          onChange={this.onSrcChange}
          style={{ border: '1px solid #999999' }}
        />
        <div style={{ marginRight: 5 }}>原图originSrc地址：</div>
        <MInput
          placeholder="请输入原图地址"
          value={originSrc}
          onChange={this.onOriginSrcChange}
          style={{ border: '1px solid #999999' }}
        />
        <div style={{ marginRight: 5 }}>图片2src地址：</div>
        <MInput
          placeholder="请输入图片2地址"
          value={src2}
          onChange={this.onSrc2Change}
          style={{ border: '1px solid #999999' }}
        />
        <div style={{ marginRight: 5 }}>原图2originSrc地址：</div>
        <MInput
          placeholder="请输入原图2地址"
          value={originSrc2}
          onChange={this.onOriginSrc2Change}
          style={{ border: '1px solid #999999' }}
        />
        <h4>通过showImage查看原图</h4>
        <Button onClick={this.showImage1}>点击查看原图</Button>
        <Button onClick={this.showImage2}>点击查看原图2</Button>
        <br />
        <h4>通过PhotoView组件查看原图</h4>
        {images.map((item, index) =>
          <Img
            key={item.id}
            index={index}
            src={item.src}
            onClick={this.preview} />
        )}
        <MPhotoView
          visible={this.state.visible}
          photoIndex={this.state.index}
          data={images}
          onClose={this.onClose}
          showToolBar
        />
      </div>
    )
  }
}
```

### 工具栏

自定义移动端展示工具栏

```jsx
import { PureComponent, RefAttributes, FC, useCallback } from 'react';
import { MPhotoView, PhotoViewData, Icon, Dialog } from '@weapp/ui';

const { message } = Dialog

const images: PhotoViewData[] = [
  {
    id: '1',
    src: '/build/ui/demo/assets/photo-view/1.jpg',
    // src: '/api/file/preview?type=redirect&imgFormat=image&fileId=1149132338570665986',
    originSrc: '/build/ui/demo/assets/photo-view/7.png',
    // originSrc: '/api/file/preview?type=redirect&imgFormat=image&fileId=1149132338570665986',
    intro: '这是两只小肥羊',
    user: '屈正巍',
    time: '2021-10-22 18:00:00',
    imgSize: 1024,
  },
  {
    id: '2',
    src: '/build/ui/demo/assets/photo-view/2.jpeg',
    originSrc: '/build/ui/demo/assets/photo-view/8.jpg',
  },
  {
    id: '3',
    src: '/build/ui/demo/assets/photo-view/3.jpeg',
  },
  {
    id: '4',
    src: '/build/ui/demo/assets/photo-view/4.jpeg',
  },
  {
    id: '5',
    src: "/api/file/preview?fileId=1143900054845579267&type=redirect",
    fileId: '1143900054845579267',
    intro: '测试fileId2',
    user: '屈正巍2',
    time: '2023-03-14 18:00:00'
  },
  {
    id: '6',
    src: "/api/file/preview?fileId=1143922096542244868&type=redirect",
    fileId: '1143922096542244868',
    intro: '测试fileId2',
    user: '屈正巍2',
    time: '2023-03-14 18:00:00'
  },
  {
    id: '7',
    src: "",
  },
]

const Img = (props: any) => {
  const { index, onClick } = props;
  function preview() {
    onClick(index);
  }
  return <img width='100' height='100' src={props.src} alt={props.src} onClick={preview} />
}

const ShareButton: FC<any & RefAttributes<any>> = (props: any) => {
  const { params, image } = props
  const onButtonClick = useCallback(() => {
    console.log('props', props)
    message({
      type: "info",
      content: (
        <div style={{ display: 'flex', flexDirection: 'column' }}>
          <span>文件详情</span>
          <span>{`当前为第${params?.index + 1}张`}</span>
          <span>{`当前地址为${image?.src}`}</span>
        </div>
      )
    })
  }, [image?.src, params?.index, props])
  return <Icon className={`PhotoView-toolBarBtn`} name={'Icon-share02'} onClick={onButtonClick} size={'lg'} title={'转发'} />
}

export default class PhotoViewDemo extends PureComponent<{}> {
  state = {
    visible: false,
    index: 0
  }
  preview = (index: number) => this.setState({ visible: true, index })

  onClose = () => this.setState({ visible: false })

  customRenderShareButton = (_: any, content: any) => {
    return (
      <ShareButton
       
        {..._}
      />
    )
  }

  render() {
    return (
      <div>
        {images.map((item, index) =>
          <Img
            key={item.id}
            src={item.src}
            index={index}
            onClick={this.preview}
          />
        )}
        <MPhotoView
         
          visible={this.state.visible}
          photoIndex={this.state.index}
          data={images}
          onClose={this.onClose}
          showToolBar
          showShareButton
          customRenderShareButton={this.customRenderShareButton}
        />
      </div>
    )
  }
}
```

### 自定义属性

自定义图片顶部信息的展示形式

```jsx
import React from 'react';
import { MPhotoView } from '@weapp/ui';
const images = [
  {
    src: '/build/ui/demo/assets/photo-view/1.jpg',
    id: '1',
    user: <span style={{ border: '1px solid red', color: 'red' }}>作者</span>,
    imgSize: 2048,
    time: <span style={{ border: '1px solid red', color: 'red' }}>时间</span>,
    intro: <div style={{ border: '1px solid red', height: "100%", color: 'red' }}>图片描述</div>
  },
  {
    src: '/build/ui/demo/assets/photo-view/2.jpeg',
    id: '2',
    user: '作者',
    imgSize: 2048,
    time: '时间',
    intro: '图片描述'
  },
  {
    src: '/build/ui/demo/assets/photo-view/3.jpeg',
    id: '3',
  },
]

const Img = (props: any) => {
  const { index, onClick } = props;
  function preview() {
    onClick(index);
  }
  return <img width='100' height='100' src={props.src} alt={props.src} onClick={preview} />
}

export default class PhotoViewDemo extends React.PureComponent<{}> {
  state = {
    visible: false,
    index: 0
  }
  preview = (index: number) => this.setState({ visible: true, index })

  onClose = () => this.setState({ visible: false })

  render() {
    return (
      <div>
        <div>
          {images.map((item, index) => {
            return <Img key={item.id} src={item.src} index={index} onClick={this.preview} />
          })}
        </div>
        <MPhotoView canRotate visible={this.state.visible} photoIndex={this.state.index} data={images} onClose={this.onClose} />
      </div>
    )
  }
}
```

### 加载失败

可自定义渲染加载失败展示

```jsx
import { PureComponent } from 'react';
import { MPhotoView, constants, PhotoViewData, MButton } from '@weapp/ui';

const prefixCls = `${constants.uiPcClsPrefix}-demo-photoview`;

const images: PhotoViewData[] = [
  {
    id: '1',
    src: '',
    hideDownload: true,
    hideRotate: true
  },
]

export default class PhotoViewDemo extends PureComponent<{}> {

  state = {
    visible1: false,
    visible2: false,
    index1: 0,
    index2: 0
  }

  preview1 = () => {
    this.setState({ index1: 0 }, () => {
      this.setState({ visible1: true })
    })
  }
  onClose1 = () => {
    this.setState({ visible1: false })
  }
  preview2 = () => {
    this.setState({ index2: 0 }, () => {
      this.setState({ visible2: true })
    })
  }
  onClose2 = () => {
    this.setState({ visible2: false })
  }
  renderBroken = () => (<div style={{ width: 150, height: 150, background: 'pink' }}>渲染失败</div>)

  render() {
    const { visible1, visible2, index1, index2 } = this.state
    return (
      <div className={prefixCls}>
        <MButton onClick={this.preview1}>默认渲染失败</MButton>
        <MButton onClick={this.preview2}>自定义渲染失败</MButton>
        <MPhotoView
         
          visible={visible1}
          photoIndex={index1}
          data={images}
          onClose={this.onClose1} />
        <MPhotoView
         
          visible={visible2}
          photoIndex={index2}
          data={images}
          brokenElement={this.renderBroken}
          onClose={this.onClose2} />
      </div>
    )
  }
}
```

## API

### 类型说明：（请从@weapp/ui 中导出）

```typescript
type PhotoViewData = {
  id: string; // 数据id
  src: string; // 图片地址
  fileId?: string; // 调用下载接口和开启图文识别传的内部文件fileId
  module?: string; //用于走下载接口的module
  downloadParams?: string; //用于走下载接口的其他参数
  downloadSrc?: string; // 专门用于下载的图片地址
  intro?: React.ReactNode | string; //图片介绍
  user?: React.ReactNode | string; //图片人员名称
  time?: React.ReactNode | string; //图片时间
  imgSize?: number; //图片大小
  hideEnlarge?: boolean; // 隐藏放大按钮
  hideShrink?: boolean; // 隐藏缩小按钮
  hideRotate?: boolean; // 隐藏顺时针旋转
  hideAntiRotate?: boolean; // 隐藏逆时针旋转
  hideDownload?: boolean; // 隐藏下载按钮
  hideInfo?: boolean; // 隐藏信息按钮
};
```

### 参数说明如下：

| 参数 | 说明 | 类型 | 是否必填 | 默认值 |
| --- | --- | --- | --- | --- |
| className | 自定义类名 | string | 否 |  |
| data | 图片数据 | PhotoViewData[] | 是 |  |
| visible | 是否打开预览 | boolean | 否 |  |
| photoIndex | 打开第几图片 | number | 否 |  |
| onClose | 当相册浏览关闭的回调函数 | () => void | 否 |  |
| maskClosable | 是否可点击背景关闭预览 | boolean | 否 | true |
| customRender | 自定义渲染 | (_: any, ele: React.ReactNode) => React.ReactNode | 否 |  |
| options | 自定义配置项 | {string:any} | 否 |  |
| canRotate | 是否可旋转 | boolean | 否 | false |
| canScale | 是否可缩放 | boolean | 否 | true |
| canDoubleScale | 是否可双击缩放 | boolean | 否 | true |
| showToolBar | 是否展示移动端工具栏 | boolean | 否 | true |
| previewMode | 是否开启单页预览模式 | boolean | 否 | false |
| disableSingleImageTouch | 在移动设备只有一张图片时是否禁止触摸事件 | boolean | 否 | false |
| documentClickWhiteList | 全屏点击关闭拦截白名单 | string[] | 否 |  |
| photoViewBase | 照片预览的基础定位元素 | 'window' \| 'document' | 否 | 'window' |
| defaultPhotoFitDialog | 是否默认开启照片适应弹出框 | boolean | 否 | true |
### PhotoView.showImage

通过 function 调用方式预览图片(具体请参照 demo2)

```typescript
//其中otherParams为需要传递的参数
function (items: Array<PhotoViewData>, index: number, otherParams: PhotoViewProps) {
  ...
}
```
