react 响应式图片放大镜 (伸缩盒子)

放大镜效果图

放大镜

非响应式代码 (定宽式点这里)

放大镜代码 (响应式)

import React, { useEffect, useState, useMemo } from "react";

/**
 * 配置项
 * @param {*} scale integer 图片放大倍数
 * @param {*} width integer 组件宽度
 * @param {*} height integer 组件高度
 */
// 根据父级宽度生成 配置项 参数
function fetMainParams (clientWidth = 500) {
    const PARAMS = {
        // 放大倍数
        scale: 2,
        // 组件宽
        width: clientWidth,
        // 组件高
        height: clientWidth + 100,
    }

    // 鼠标悬停小方块 width的半径
    const mouseRadiusW = PARAMS.width / PARAMS.scale / 2;
    // 鼠标悬停小方块 height的半径
    const mouseRadiusH = PARAMS.height / PARAMS.scale / 2;

    const ClassObj = {
        // 图片容器
        imgContainer: {
            width: `${PARAMS.width}px`,
            height: `${PARAMS.height}px`,
            border: "1px solid #ccc",
            cursor: "move",
            position: "relative"
        },
    
        // 遮罩
        maskBlock: {
            position: "absolute",
            top: "0",
            left: "0",
            width: "100%",
            height: "100%",
            // background: "rgba(0,0,0,0)",
            zIndex: 100
        },
    
        // 鼠标悬停小方块样式
        mouseBlock: {
            position: "absolute",
            top: "0",
            left: "0",
            width: `${mouseRadiusW * 2}px`,
            height: `${mouseRadiusH * 2}px`,
            background: "rgba(0,0,0,0.1)",
            zIndex: 99
        },
    
        // 放大镜容器样式
        magnifierContainer: {
            position: "absolute",
            left: `${PARAMS.width}px`,
            top: "0",
            width: `${PARAMS.width}px`,
            height: `${PARAMS.height}px`,
            border: "1px solid #ccc",
            overflow: "hidden",
            zIndex: 98
        },
    
        // 图片放大样式 此处图片宽高不能设置为百分比,在scale的作用下,放大的只是图片初始的宽高 !!!
        imgStyle: {
            width: `${PARAMS.width}px`,
            height: `${PARAMS.height}px`,
            position: "absolute",
            top: 0,
            left: 0,
            transform: `scale(${PARAMS.scale})`,
            transformOrigin: "top left"
        }
    };

    return { PARAMS, mouseRadiusW, mouseRadiusH, ClassObj }
}

/**
 * 参数
 * @param {*} imgUrl string 图片url
 * @param {} position string [left] 放大镜位置 默认位于右边, left左边
 */
export default (props) => {

    // 图片信息
    const [imgUrl, setImgUrl] = useState('');
    // 配置项参数
    const [{ PARAMS, mouseRadiusW, mouseRadiusH, ClassObj }, setMainParams] = useState(({ PARAMS: {}, mouseRadiusW: 0, mouseRadiusH: 0, ClassObj: {} }));
    // 移入移出开关
    const [magnifierOff, setMagnifierOff] = useState(false);
    // 放大镜样式
    const [{ mouseBlock, imgStyle }, setMouseImg] = useState({ mouseBlock: {}, imgStyle: {} });

    // 计算相关参数
    const calculationBlock = (offsetX, offsetY) => {
        const cssStyle = { mouseBlock: { ...mouseBlock }, imgStyle: { ...imgStyle } }
        let offsetW = offsetX;
        let offsetH = offsetY;
        /* 小方块位置 */
        // 防止鼠标移动过快导致计算失误,只要小于或者大于对应值,直接设置偏移量等于最小值或者最大值
        // 判断与左右的边距
        if (offsetX < mouseRadiusW) {
            offsetW = mouseRadiusW;
        }
        else if (offsetX > (PARAMS.width - mouseRadiusW)) {
            offsetW = (PARAMS.width - mouseRadiusW);
        }


        // 判断 鼠标小方块 与上下的边距
        if (offsetY < mouseRadiusH) {
            offsetH = mouseRadiusH;
        }
        else if (offsetY > (PARAMS.height - mouseRadiusH)) {
            offsetH = (PARAMS.height - mouseRadiusH);
        }

        const left = offsetW - mouseRadiusW;
        const top = offsetH - mouseRadiusH;

        // 设置鼠标悬停小方块
        cssStyle.mouseBlock.left = left;
        cssStyle.mouseBlock.top = top;

        /* 计算图片放大位置 */
        cssStyle.imgStyle.left = -left * PARAMS.scale;
        cssStyle.imgStyle.top = -top * PARAMS.scale;

        setMouseImg(cssStyle)
    };

    // 鼠标移入
    const mouseEnter = () => {
        setMagnifierOff(true)
    };

    // 鼠标移除
    const mouseLeave = () => {
        setMagnifierOff(false)
    };

    // 鼠标移动
    const mouseMove = event => {
        const e = event.nativeEvent;
        calculationBlock(e.offsetX, e.offsetY);
    };

    // 放大镜容器样式
    const magnifierMemo = useMemo(() => {
        if (props.position === 'left') {
            return {
                ...ClassObj.magnifierContainer,
                left: `-${PARAMS.width}px`,
            }
        }
        return ClassObj.magnifierContainer;
    }, [props.position, ClassObj]);

    // 根据父级容器生成宽度
    useEffect(() => {
        const clientWidth = document.getElementById('mydiv').clientWidth || 500
        const results = fetMainParams(clientWidth)
        setMainParams(results)
        setMouseImg({ mouseBlock: results.ClassObj.mouseBlock, imgStyle: results.ClassObj.imgStyle })
    }, []);

    useEffect(() => {
        setImgUrl(props.imgUrl)
    }, [props.imgUrl]);

    return (
        <div id="mydiv" style={{ position: 'relative' }}>
            <div style={ClassObj.imgContainer}>
                <img src={imgUrl} width="100%" height="100%" alt="图片加载失败"/>
                <div
                    style={ClassObj.maskBlock}
                    onMouseEnter={mouseEnter}
                    onMouseLeave={mouseLeave}
                    onMouseMove={mouseMove}
                />
                {magnifierOff && <div style={mouseBlock} />}
            </div>

            {magnifierOff && (
                <div style={magnifierMemo}>
                    <img
                        style={imgStyle}
                        src={imgUrl}
                        alt="图片加载失败"
                    />
                </div>
            )}
        </div>
    );
}
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值