教你用react实现防京东商品详情的图片放大效果

597 阅读2分钟

效果图:

image.png

// 新建index.js文件,img就是url地址
<ImageMagnifier minImg={img} maxImg={img} width={250} height={250} />
// 新建ImageMagnifier.js文件
import React, { Component } from 'react';
import PropTypes from 'prop-types';

import girdImg from 'public/images/gird.png';
class ImageMagnifier extends Component {
    constructor(props) {
        super(props);
        this.state = {
            params: {
                // 放大倍数
                scale: 2,
                // 组件宽
                width: props.width,
                height: props.height
            },
            // 略缩图
            minImg: '',
            // 大图
            maxImg: '',
            // 开关
            magnifierOff: false,
            // 图片加载情况
            imgLoad: false,
            cssStyle: {
                // 图片容器
                imgContainer: {
                    width: '400px',
                    height: '400px',
                    cursor: 'move',
                    position: 'relative'
                },
                // 鼠标悬停小方块
                mouseBlock: {
                    position: 'absolute',
                    top: '0',
                    left: '0',
                    width: '100px',
                    height: '100px',
                    backgroundImage: `url(${girdImg})`,
                    backgroundAttachment: 'scroll',
                    backgroundRepeat: 'repeat',
                    backgroundPosition: '0 0',
                    zIndex: 99
                },
                // 鼠标悬停遮罩层
                maskBlock: {
                    position: 'absolute',
                    top: '0',
                    left: '0',
                    width: '100%',
                    height: '100%',
                    background: 'rgba(0,0,0,0)',
                    zIndex: 100
                },
                // 放大镜
                magnifierContainer: {
                    position: 'absolute',
                    left: '480px',
                    top: '50px',
                    width: '600px',
                    height: '600px',
                    overflow: 'hidden',
                    zIndex: 98
                },
                // 图片
                imgStyle: {
                    width: '100%',
                    height: '100%'
                },
                // scale 放大的只是图片初始的宽高
                imgStyle2: {
                    width: '400px',
                    height: '400px',
                    position: 'absolute',
                    top: 0,
                    left: 0,
                    transform: 'scale(4)',
                    transformOrigin: 'top left'
                }
            }
        };
    }
    // 初始化
    componentWillMount() {
        this.initParam();
        this.updataImg(this.props);
    }
    // props 变化时更新
    componentWillReceiveProps(nextProps) {
        this.updataImg(nextProps);
    }
    // 鼠标移入
    mouseEnter = () => {
        this.setState({
            magnifierOff: true
        });
    };
    // mouse remove
    mouseLeave = () => {
        this.setState({
            magnifierOff: false
        });
    }
    // mouse move
    mouseMove = event => {
        let e = event.nativeEvent;
        const { offsetX, offsetY } = e;
        this.calculationBlock(offsetX, offsetY);
    }
    // calculation params
    calculationBlock(offsetx, offsety) {
        let cssStyle = JSON.parse(JSON.stringify(this.state.cssStyle));
        let mouseOffSetX = offsetx - 50; // mouseOffSetX = offset - maskWidth / 2 mask 位置
        let mouseOffSetY = offsety - 50; // mouseOffSetX = offset - maskHeight / 2 mask 位置
        // 兼容鼠标移动过快导致计算失误,只要小于或大于对应值,直接设置偏移量等于最小或最大值
        if (mouseOffSetX < 0) {
            mouseOffSetX = 0;
        }
        // mask 移动在图片内 参数可根据 clientWidth、clientHeight 计算
        if (mouseOffSetX > 150) {
            mouseOffSetX = 150;
        }
        if (mouseOffSetY < 0) {
            mouseOffSetY = 0;
        }
        if (mouseOffSetY > 150) {
            mouseOffSetY = 150;
        }
        // 移动 mask 距离
        cssStyle.mouseBlock.left = parseFloat(mouseOffSetX) + 'px';
        cssStyle.mouseBlock.top = parseFloat(mouseOffSetY) + 'px';

        // 右侧大图片,等比例移动
        // 大图片移动的距离 / mask 移动的距离 = 大图片 / 小图片
        let bigImg = document.querySelector('.big img');
        let smallImg = document.querySelector('.small img');
        let ratio = bigImg.offsetWidth / smallImg.offsetWidth;
        cssStyle.imgStyle2.left = -parseFloat((mouseOffSetX) * ratio) + 'px';
        cssStyle.imgStyle2.top = -parseFloat((mouseOffSetY) * ratio) + 'px';

        this.setState({
            cssStyle: cssStyle
        });
    }

    // 初始化参数
    initParam() {
        let cssStyle = JSON.parse(JSON.stringify(this.state.cssStyle));
        let params = JSON.parse(JSON.stringify(this.state.params));

        cssStyle.imgContainer.width = params.width + 'px';
        cssStyle.imgContainer.height = params.height + 'px';

        cssStyle.imgStyle2.width = params.width + 'px';
        cssStyle.imgStyle2.height = params.height + 'px';
        cssStyle.imgStyle2.transform = 'scale(' + params.scale + ')';

        this.setState({
            cssStyle: cssStyle
        });
    }

    // 更新图片
    updataImg(props) {
        this.setState({
            minImg: props.minImg,
            maxImg: props.maxImg
        });
    }

    // 图片加载情况
    handleImageLoaded(e) {
        this.setState({ imgLoad: true });
    }

    // 图片加载中
    handleImageErrored() {
        this.setState({
            imgLoad: false
        });
    }

    render() {
        const { cssStyle, magnifierOff, minImg, maxImg, imgLoad } = this.state;
        return (
            <div className={'box'}>
                <div className={'small'} style={cssStyle.imgContainer}>
                    {/* 加载小图 */}
                    <img
                        className={'small img'}
                        style={cssStyle.imgStyle} src={minImg} alt={''} />
                    <div
                        className={'mask'}
                        style={cssStyle.maskBlock}
                        onMouseEnter={this.mouseEnter}
                        onMouseLeave={this.mouseLeave}
                        onMouseMove={this.mouseMove} />
                    {magnifierOff && <div style={cssStyle.mouseBlock} />}
                </div>
                {magnifierOff && (
                    <div className={'big hide'}
                        style={cssStyle.magnifierContainer}>
                        <img
                            className={'big img'}
                            style={cssStyle.imgStyle2}
                            src={maxImg}
                            onLoad={this.handleImageLoaded.bind(this)}
                            onError={this.handleImageErrored.bind(this)}
                            alt={''} />
                        {!imgLoad && 'failed to load'}
                    </div>
                )}
            </div>
        );
    }
}

export default ImageMagnifier;

ImageMagnifier.propTypes = {
    width: PropTypes.number,
    height: PropTypes.number
};