前端react简单封装 ---《图片预览插件》

效果图


组件

js代码



    import React from 'react';

    import ReactDOM from 'react-dom';

    import PropTypes from 'prop-types';

    import { ZoomInOutlined, ZoomOutOutlined,RedoOutlined,UndoOutlined,ArrowLeftOutlined,ArrowRightOutlined,CloseOutlined} from '@ant-design/icons'

    import './index.css';


    class PhotoPreview extends React.Component {

    constructor(props) {

        super(props);

        this.state = {

            bigUrl: props.bigUrl === '' ? props.url : props.bigUrl,

            tool: Object.assign(PhotoPreview.defaultProps.tool, props.tool),

            imgIndex: props.imgIndex,

            imgs: props.imgs,


            loadEl: true, // loading元素显示隐藏

            figureEl: false, // 生成图片预览元素

            imgOriginalWidth: 0, // 当前大图默认宽度值

            imgOriginalHeight: 0, // 当前大图默认高度值

            imgAttr: {

                // 大图的地址及描述

                src: '',

                alt: '',

            },

            imgParentStyle: {

                // 大图父级div元素样式

                width: '0px',

                height: '0px',

            },

            rotateDeg: 0, // 图片旋转角度

            increaseNum: 20, // 图片放大时距离空隙

        };


        // 获取相关元素

        this.bigImgRef = React.createRef();

        this.ppiRef = React.createRef();

    }


    // 预览图片超出window宽或高的处理

    beyondWindow = () => {

        const { imgParentStyle, rotateDeg, increaseNum } = this.state;

        const iWidth = parseFloat(imgParentStyle.width) + increaseNum * 2;

        const iHeight = parseFloat(imgParentStyle.height) + increaseNum * 2;

        const ppiEl = this.ppiRef.current;


        let ips = imgParentStyle;

        if (rotateDeg % 360 === 90 || rotateDeg % 360 === 270) {

            if (iHeight > window.innerWidth) {

                ips = { ...ips, left: `${(iHeight - iWidth) / 2 + increaseNum}px` };

            } else {

                ips = { ...ips, left: 'auto' };

            }

            if (iWidth > window.innerHeight) {

                ips = { ...ips, top: `${(iWidth - iHeight) / 2 + increaseNum}px` };

            } else {

                ips = { ...ips, top: 'auto' };

            }

        } else if (

            (rotateDeg % 360 === -90 && iWidth > iHeight) ||

            (rotateDeg % 360 === -270 && iWidth > iHeight)

        ) {

            // 如果是-90或-270,并且图片宽大于高的话,那么则需要做兼容处理

            let left = 'auto';

            let top = 'auto';

            if (iHeight > ppiEl.clientWidth) {

                left = `${-(iHeight / 2) + increaseNum * 2}px`;

            }

            if (iWidth > ppiEl.clientHeight) {

                top = `${iHeight / 2 + increaseNum / 2}px`;

            }

            ips = { ...ips, left: `${left}`, top: `${top}` };

        } else if (

            (rotateDeg % 360 === -90 && iHeight > iWidth) ||

            (rotateDeg % 360 === -270 && iHeight > iWidth)

        ) {

            // 如果是-90或-270,并且图片高大于宽的话,那么则需要做兼容处理

            let left = 'auto';

            let top = 'auto';

            if (iHeight > ppiEl.clientWidth) {

                left = `${iWidth / 2}px`;

            }

            if (iWidth > ppiEl.clientHeight) {

                top = `${-(iWidth / 2) + increaseNum * 2}px`;

            }


            ips = { ...ips, left: `${left}`, top: `${top}` };

        } else {

            if (iWidth > window.innerWidth) {

                ips = { ...ips, left: `${increaseNum}px` };

            } else {

                ips = { ...ips, left: 'auto' };

            }

            if (iHeight > window.innerHeight) {

                ips = { ...ips, top: `${increaseNum}px` };

            } else {

                ips = { ...ips, top: 'auto' };

            }

        }

        this.setState({

            imgParentStyle: ips,

        });

    };


    // 图片缩小事件

    toSmallEvent = () => {

        const { tool, imgParentStyle, imgOriginalWidth, imgOriginalHeight } = this.state;

        if (tool.toSmall === false) {

            return;

        }

        let width = parseFloat(imgParentStyle.width) / 1.5;

        let height = parseFloat(imgParentStyle.height) / 1.5;

        // 图片缩小不能超过5倍

        if (width < imgOriginalWidth / 5) {

            width = imgOriginalWidth / 5;

            height = imgOriginalHeight / 5;

        }

        this.setState(

            {

                imgParentStyle: Object.assign(imgParentStyle, {

                    width: `${width}px`,

                    height: `${height}px`,

                }),

            },

            () => {

                this.beyondWindow();

            }

        );

    };


    // 图片放大事件

    toBigEvent = () => {

        const { tool, imgParentStyle, imgOriginalWidth, imgOriginalHeight } = this.state;

        if (tool.toBig === false) {

            return;

        }

        let width = parseFloat(imgParentStyle.width) * 1.5;

        let height = parseFloat(imgParentStyle.height) * 1.5;

        // 图片放大不能超过5倍

        if (width > imgOriginalWidth * 5) {

            width = imgOriginalWidth * 5;

            height = imgOriginalHeight * 5;

        }

        this.setState(

            {

                imgParentStyle: Object.assign(imgParentStyle, {

                    width: `${width}px`,

                    height: `${height}px`,

                }),

            },

            () => {

                this.beyondWindow();

            }

        );

    };


    // 向左旋转事件

    turnLeftEvent = () => {

        const { tool, rotateDeg, imgParentStyle } = this.state;

        if (tool.turnLeft === false) {

            return;

        }

        const iRotateDeg = rotateDeg - 90;

        this.setState(

            {

                imgParentStyle: Object.assign(imgParentStyle, {

                    transform: `rotate(${iRotateDeg}deg)`,

                }),

                rotateDeg: iRotateDeg,

            },

            () => {

                this.beyondWindow();

            }

        );

    };


    // 向右旋转事件

    turnRightEvent = () => {

        const { tool, rotateDeg, imgParentStyle } = this.state;

        if (tool.turnRight === false) {

            return;

        }

        const iRotateDeg = rotateDeg + 90;

        this.setState(

            {

                imgParentStyle: Object.assign(imgParentStyle, {

                    transform: `rotate(${iRotateDeg}deg)`,

                }),

                rotateDeg: iRotateDeg,

            },

            () => {

                this.beyondWindow();

            }

        );

    };


    // 上一张图片

    goLeftEvent = () => {

        const { imgIndex, imgs, loadEl } = this.state;

        // 如果还在loading加载中,不予许上一张下一张操作

        if (loadEl) {

            return;

        }

        const nImgIndex = imgIndex - 1;

        // console.log(nImgIndex);

        if (nImgIndex < 0) {

            return;

        }

        this.setState(

            {

                imgIndex: nImgIndex,

                rotateDeg: 0,

                imgParentStyle: {

                    width: '0px',

                    height: '0px',

                },

            },

            () => {

                this.photoShow(imgs[nImgIndex].bigUrl, imgs[nImgIndex].alt, false);

            }

        );

    };


    // 下一张图片

    goRightEvent = () => {

        const { imgIndex, imgs, loadEl } = this.state;

        // 如果还在loading加载中,不予许上一张下一张操作

        if (loadEl) {

            return;

        }

        const nImgIndex = imgIndex + 1;

        // console.log(nImgIndex);

        if (nImgIndex > imgs.length - 1) {

            return;

        }

        this.setState(

            {

                imgIndex: nImgIndex,

                rotateDeg: 0,

                imgParentStyle: {

                    width: '0px',

                    height: '0px',

                },

            },

            () => {

                // 如果不存在大图,那么直接拿小图代替。

                const bigUrl = imgs[nImgIndex].bigUrl || imgs[nImgIndex].url;

                this.photoShow(bigUrl, imgs[nImgIndex].alt);

            }

        );

    };


    // 关闭事件

    closeEvent = () => {

        // 恢复到默认值

        const { imgIndex, imgs } = this.props;

        this.setState({

            imgIndex,

            imgs,

            figureEl: false,

            rotateDeg: 0,

            imgParentStyle: {

                width: '0px',

                height: '0px',

            },

        });

        window.removeEventListener('mousewheel', this._psMousewheelEvent);

        window.removeEventListener('keydown', this._psKeydownEvent);

        window.removeEventListener('resize', this._psWindowResize);

        document.body.removeAttribute('photo-preview-show');

    };


    // 大图被执行拖拽操作

    bigImgMouseDown = (event) => {

        event.preventDefault();

        const ppiEl = this.ppiRef.current;

        const bigImgEl = this.bigImgRef.current;

        const diffX = event.clientX - bigImgEl.offsetLeft;

        const diffY = event.clientY - bigImgEl.offsetTop;

        // 鼠标移动的时候

        bigImgEl.onmousemove = (ev) => {

            const moveX = parseFloat(ev.clientX - diffX);

            const moveY = parseFloat(ev.clientY - diffY);

            const mx = moveX > 0 ? -moveX : Math.abs(moveX);

            const my = moveY > 0 ? -moveY : Math.abs(moveY);

            let sl = ppiEl.scrollLeft + mx * 0.1;

            let sr = ppiEl.scrollTop + my * 0.1;

            if (sl <= 0) {

                sl = 0;

            } else if (sl >= ppiEl.scrollWidth - ppiEl.clientWidth) {

                sl = ppiEl.scrollWidth - ppiEl.clientWidth;

            }

            if (sr <= 0) {

                sr = 0;

            } else if (sr >= ppiEl.scrollHeight - ppiEl.clientHeight) {

                sr = ppiEl.scrollHeight - ppiEl.clientHeight;

            }

            ppiEl.scrollTo(sl, sr);

        };

        // 鼠标抬起的时候

        bigImgEl.onmouseup = () => {

            bigImgEl.onmousemove = null;

            bigImgEl.onmouseup = null;

        };

        // 鼠标离开的时候

        bigImgEl.onmouseout = () => {

            bigImgEl.onmousemove = null;

            bigImgEl.onmouseup = null;

        };

    };


    // 鼠标滚轮事件

    _psMousewheelEvent = (event) => {

        // event.preventDefault();

        const { figureEl, tool } = this.state;

        if (figureEl && tool.mousewheel) {

            if (event.wheelDelta > 0) {

                this.toBigEvent();

            } else {

                this.toSmallEvent();

            }

        }

    };


    // 键盘按下事

    _psKeydownEvent = (event) => {

        const { figureEl, tool } = this.state;

        if (event.keyCode === 27 && tool.esc && figureEl) {

            this.closeEvent();

        }

    };


    // 窗口发生改变的时候

    _psWindowResize = () => {

        const { figureEl } = this.state;

        if (figureEl) {

            this.beyondWindow();

        }

    };


    // 图片展示

    photoShow = (url, alt, winEventToggle) => {

        // 图片加载并处理

        this.setState({

            loadEl: true,

            figureEl: true,

        });

        const img = new Image();

        img.src = url;

        img.onload = async () => {

            this.setState(

                {

                    loadEl: false,

                    imgOriginalWidth: img.width,

                    imgOriginalHeight: img.height,

                    imgAttr: {

                        src: url,

                        alt,

                    },

                    imgParentStyle: {

                        width: `${img.width}px`,

                        height: `${img.height}px`,

                    },

                },

                () => {

                    this.beyondWindow();

                }

            );

        };


        // 是否需再次执行window事件

        const wev = winEventToggle || true;

        if (wev) {

            // console.log('wev');

            // window触发事件

            window.addEventListener('mousewheel', this._psMousewheelEvent);

            window.addEventListener('keydown', this._psKeydownEvent);

            window.addEventListener('resize', this._psWindowResize);

            document.body.setAttribute('photo-preview-show', 'true');

        }

    };


    UNSAFE_componentWillReceiveProps(newProps) {

        console.log(`new-props:${newProps.nImgIndex}`);

    }


    render() {

        const { alt, url } = this.props;

        const {

            bigUrl,

            tool,

            figureEl,

            loadEl,

            imgAttr,

            imgParentStyle,

            imgIndex,

            imgs,

            increaseNum,

        } = this.state;

        const iParentStyle = { ...imgParentStyle };

        const iSpanStyle = {

            width: `${parseFloat(imgParentStyle.width) + increaseNum * 2}px`,

            height: `${parseFloat(imgParentStyle.height) + increaseNum * 2}px`,

        };

        return (

            <>

                <img

                    onClick={this.photoShow.bind(this, bigUrl, alt)}

                    src={url}

                    alt={alt}

                    className="photo-preview__thumb-img"

                />

                {figureEl

                    ? ReactDOM.createPortal(

                          <>

                              <figure className="photo-preview">

                                  <div className="photo-preview__in" ref={this.ppiRef}>

                                      {loadEl ? (

                                          <div className="photo-preview__loading"></div>

                                      ) : (

                                          <div

                                              className="photo-preview__img-wrap"

                                              style={iParentStyle}

                                          >

                                              <span

                                                  className="photo-preview__img-placeholder"

                                                  style={{

                                                      ...iSpanStyle,

                                                      marginLeft: `-${increaseNum}px`,

                                                      marginTop: `-${increaseNum}px`,

                                                  }}

                                              ></span>

                                              <img

                                                  src={imgAttr.src}

                                                  alt={imgAttr.alt}

                                                  onMouseDown={this.bigImgMouseDown}

                                                  ref={this.bigImgRef}

                                              />

                                          </div>

                                      )}

                                      <div className="photo-preview__tool">

                                          {tool.toSmall ? (

                                              <ZoomOutOutlined onClick={this.toSmallEvent} className='iconfont'/>

                                          ) : null}

                                          {tool.toBig ? (

                                              <ZoomInOutlined onClick={this.toBigEvent} className='iconfont'/>

                                          ) : null}

                                          {tool.turnLeft ? (

                                              <RedoOutlined onClick={this.turnLeftEvent} className='iconfont'/>

                                          ) : null}

                                          {tool.turnRight ? (

                                              <UndoOutlined onClick={this.turnRightEvent} className='iconfont'/>

                                          ) : null}


                                          {imgIndex !== '' && imgs.length > 1 ? (

                                              <>

                                              <ArrowLeftOutlined className='iconfont' onClick={this.goLeftEvent} data-disable={loadEl ? 'true' : 'false'}/>

                                              <ArrowRightOutlined className='iconfont' onClick={this.goRightEvent} data-disable={loadEl ? 'true' : 'false'}/>

                                              </>

                                          ) : null}


                                          {tool.close ? (

                                              <CloseOutlined onClick={this.closeEvent} className='iconfont'/>

                                          ) : null}

                                      </div>

                                  </div>

                              </figure>

                          </>,

                          document.body

                      )

                    : null}

            </>

        );

    }

    }


    PhotoPreview.defaultProps = {

    bigUrl: '',

    alt: '',

    tool: {

        toSmall: true, // 缩小按钮

        toBig: true, // 放大按钮

        turnLeft: true, // 左转按钮

        turnRight: true, // 右转按钮

        close: true, // 关闭按钮

        esc: true, // esc键触发

        mousewheel: true, // 鼠标滚轮事件是否触发

    },

    imgIndex: '',

    imgs: [],

    };

    PhotoPreview.propTypes = {

    bigUrl: PropTypes.string,

    url: PropTypes.string.isRequired,

    alt: PropTypes.string,

    tool: PropTypes.object,

    imgIndex: PropTypes.number,

    imgs: PropTypes.array,

    };

    export default PhotoPreview;



css代码

.photo-preview__thumb-img {

    cursor: pointer;

  }

  .photo-preview {

    margin: 0;

    position: fixed;

    left: 0;

    top: 0;

    bottom: 0;

    right: 0;

    z-index: 999999;

    background-color: rgba(0, 0, 0, 0.5);

    animation: fadeIn 0.4s;

  }

  .photo-preview .photo-preview__in {

    position: absolute;

    left: 0;

    top: 0;

    right: 0;

    bottom: 0;

    overflow: auto;

    user-select: none;

    display: flex;

    justify-content: center;

    align-items: center;

  }

  .photo-preview .photo-preview__in::-webkit-scrollbar {

    width: 15px;

    height: 15px;

  }

  .photo-preview .photo-preview__in::-webkit-scrollbar-track {

    border-radius: 0;

  }

  .photo-preview .photo-preview__in::-webkit-scrollbar-thumb {

    border-radius: 0;

    background-color: silver;

  }

  .photo-preview .photo-preview__in .photo-preview__img-wrap {

    transition-duration: 0.2s;

    position: absolute;

  }

  .photo-preview .photo-preview__in .photo-preview__img-wrap .photo-preview__img-placeholder {

    display: block;

    width: 100%;

    height: 100%;

    position: absolute;

    pointer-events: none;

  }

  .photo-preview .photo-preview__in .photo-preview__img-wrap img {

    position: absolute;

    width: 100%;

    height: 100%;

  }

  .photo-preview .photo-preview__loading {

    position: relative;

  }

  .photo-preview .photo-preview__loading::before {

    content: ' ';

    display: block;

    border-top: 5px solid #999999;

    border-right: 5px solid #999999;

    border-bottom: 5px solid #999999;

    border-left: 5px solid #ffffff;

    width: 50px;

    height: 50px;

    border-radius: 50%;

    animation: rotating 0.8s linear 0s infinite;

  }

  .photo-preview .photo-preview__tool {

    border-radius: 45px;

    padding: 5px 10px;

    height: 45px;

    background-color: #ffffff;

    opacity: 0.3;

    position: fixed;

    top: 20px;

    right: 20px;

    user-select: none;

    transition-duration: 0.5s;

    display: flex;

  }

  .photo-preview .photo-preview__tool:hover {

    opacity: 0.9;

  }

  .photo-preview .photo-preview__tool .iconfont {

    font-size: 25px;

    text-align: center;

    width: 35px;

    height: 35px;

    line-height: 35px;

    color: #444444;

    transition-duration: 0.4s;

    margin: 0 2px;

    cursor: pointer;

  }

  .photo-preview .photo-preview__tool .icon-close:hover {

    transform: scale(1.15);

  }

  .photo-preview .photo-preview__tool .icon-turn-left {

    transform: rotate(50deg);

  }

  .photo-preview .photo-preview__tool .icon-turn-left:hover {

    transform: rotate(0deg);

  }

  .photo-preview .photo-preview__tool .icon-turn-right {

    transform: rotate(-50deg);

  }

  .photo-preview .photo-preview__tool .icon-turn-right:hover {

    transform: rotate(0deg);

  }

  .photo-preview .photo-preview__tool .icon-go-left[data-disable='true'],

  .photo-preview .photo-preview__tool .icon-go-right[data-disable='true'] {

    cursor: wait;

  }

  body[photo-preview-show='true'] {

    overflow: hidden;

  }

  @keyframes fadeIn {

    0% {

      opacity: 0;

    }

    100% {

      opacity: 1;

    }

  }

  @keyframes rotating {

    0% {

      transform: rotate(0deg);

    }

    100% {

      transform: rotate(360deg);

    }

  }

引用代码

import PhotoPreview from '@src/components/Common/PhotoPreview'

html

<PhotoPreview 

                                                            key={index}

                                                            imgIndex={index}

                                                            tool={{ turnLeft: false, turnRight: false}}

                                                            imgs={detailList} 

                                                            url={item.saveFilePath}

                                                            bigUrl={item.saveFilePath} 

                                                            className='images'/>



配置项列表
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350