React 在react中实现鼠标拖拽移动盒子和图片(基于Ant-Design-Pro 4实现)

主要用到函数

名称归类
useStatereact状态
useRefreact获取ref
useEffectreact初始化函数
onmousedownjs鼠标按下
onmousemovejs鼠标移动
onmouseupjs鼠标抬起

这个基于 Ant-Design-Pro 4.1.0, 里面用到了一些 Ant Design 的UI 组件

效果图

逻辑代码

import React, { useState, useRef, useEffect } from 'react';
import { Col } from 'antd';
import { MinusCircleOutlined, PlusCircleOutlined } from "@ant-design/icons";
import "./PictureModule.less";

const drag = (obj, set) => {
    // 鼠标被按下
    obj.onmousedown = (event) => {
        event = event || window.event;
        // 阻止默认事件
        event.preventDefault();

        // 鼠标手
        obj.style.cursor = "grabbing";

        // 最大移动距离
        var maxMoveX = obj.clientWidth - 100;
        var maxMoveY = obj.clientHeight - 100;

        // 计算鼠标当前坐标 = 鼠标按下坐标 - 元素当前坐标(距离父元素距离)
        // div的水平偏移量  鼠标.clentX - 元素.offsetLeft
        // div的垂直偏移量  鼠标.clentY - 元素.offsetTop
        var ol = event.clientX - obj.offsetLeft;
        var ot = event.clientY - obj.offsetTop;
        
        // 绑定鼠标移动事件
        document.onmousemove = (event2) => {
            event2 = event2 || window.event;
            // 计算移动距离 = 当前鼠标坐标 - 鼠标按下坐标
            var left = event2.clientX - ol;
            var top = event2.clientY - ot;

            // 判断左右移动距离
            if (left >= maxMoveX) {
                left = maxMoveX;
            } else if (left <= (-maxMoveX)) {
                left = -maxMoveX;
            }
            // 判断上下移动距离
            if (top >= maxMoveY) {
                top = maxMoveY;
            } else if (top <= (-maxMoveY)) {
                top = -maxMoveY;
            }

            set({ left, top });
        }
        
        // 绑定一个鼠标松开事件
        document.onmouseup = () => {
            // 取消鼠标移动事件
            document.onmousemove = null;
            document.onmouseup = null;
            // 还原鼠标手
            obj.style.cursor = "grab";
        }
    }
}

export default ({ memoize, titleInfo, coords }) => {
    // 缩放
    const [scale, setScale] = useState(1);
    const [scaleTwo, setScaleTwo] = useState(1);
    // 拖拽
    const [xyz, setXyz] = useState({ left: 0, top: 0 });
    const [xyzTwo, setXyzTwo] = useState({ left: 0, top: 0 });

    // 盒子元素
    const container = useRef(null);
    const containerTwo = useRef(null);

    // 放大
    const handleZoomIn = (val) => {
        if (val === 1) {
            setScale(scale + 0.5);
        } else if (val === 2) {
            setScaleTwo(scaleTwo + 0.5)
        }
        
    };

    // 缩小
    const handleZoomOut = (val) => {
        if (val === 1) {
            if (scale <= 1) {
                setScale(1);
                setXyz({ left: 0, top: 0 });
            } else {
                setScale(scale - 0.5);
            }
        } else if (val === 2) {
            if (scaleTwo <= 1) {
                setScaleTwo(1);
                setXyzTwo({ left: 0, top: 0 });
            } else {
                setScaleTwo(scaleTwo - 0.5);
            }
        }
        
    };

    useEffect(() => {
        if (container) drag(container.current, setXyz);
        if (containerTwo) drag(containerTwo.current, setXyzTwo);
    }, []);

    

    return (
        <>
            <Col span={9}>
                <div className="picture_title_box">
                    <div className="title_text" title={titleInfo.srcName}>标准文档:{titleInfo.srcName}</div>
                    <div className="title_btn_icon">
                        <MinusCircleOutlined onClick={() => handleZoomOut(1)} />
                        <PlusCircleOutlined onClick={() => handleZoomIn(1)} />
                    </div>
                </div>
                <div id="LeftXyzContainer" className="container_box">
                    <div
                        ref={container}
                        style={{
                            position: "relative",
                            left: xyz.left, top: xyz.top,
                            transform: `scale3d(${scale}, ${scale}, 1) rotate(0deg)`,
                        }}
                    >
                        <img src={memoize.src} width="100%" alt="图片加载失败" />
                        <div style={{ position: 'absolute', backgroundColor: 'rgb(82, 196, 26, 0.5)', width: 0, height: 0, ...coords.left }} />
                    </div>
                </div>
            </Col>
            <Col span={9}>
                <div className="picture_title_box">
                    <div className="title_text" title={titleInfo.dstName}>比对文档:{titleInfo.dstName}</div>
                    <div className="title_btn_icon">
                        <MinusCircleOutlined onClick={() => handleZoomOut(2)} />
                        <PlusCircleOutlined onClick={() => handleZoomIn(2)} />
                    </div>
                </div>
                <div id="RightXyzContainer" className="container_box">
                    <div
                        ref={containerTwo}
                        style={{
                            position: "relative",
                            cursor: "grab",
                            left: xyzTwo.left, top: xyzTwo.top,
                            transform: `scale3d(${scaleTwo}, ${scaleTwo}, 1) rotate(0deg)`,
                        }}
                    >
                        <img src={memoize.dst} width="100%" alt="图片加载失败" />
                        <div style={{ position: 'absolute', backgroundColor: 'rgb(82, 196, 26, 0.5)', width: 0, height: 0, ...coords.right }} />
                    </div>
                </div>
            </Col>
        </>
    );
};

样式

@import '~antd/es/style/themes/default.less';


// 标题盒子
.picture_title_box {
    height: 40px;
    padding: 0 20px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    background-color: #fafafa;
}

// 标题文字
.title_text {
    flex: 1;
    font-size: 16px;
    font-weight: bold;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

// 标题按钮
.title_btn_icon {
    width: 50px;
    font-size: 18px;
    margin-left: 24px;
    cursor: pointer;
    display: flex;
    justify-content: space-between;
}

// 容器盒子
.container_box {
    overflow: hidden;
    position: relative;
    border: 1px solid #a5b1b8;
    background-color: #f3f3f3;
}
  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
根据引用,在安装@ant-design/react-native时,不再需要手动链接(@ant-design/icons-react-native),而是可以使用自动链接功能。只需要使用命令`npm install @ant-design/icons-react-native --save-dev`来安装@ant-design/icons-react-native即可。此外,可以在`node_modules/@ant-design`文件夹下找到安装的文件。 根据引用,可以使用命令`react-native -v`来检查是否成功安装了react-native。 根据引用,示例代码展示了如何全局使用ant-design-mobile组件。官方文档的示例是按需引入的,如果要全局使用,需要手动引入相应的组件文件。示例代码引入了Button和InputItem组件,并在App组件使用了这些组件。 所以,ant-design-mobile是一个基于React Native的UI组件库,可以通过自动链接或手动引入组件文件的方式来使用。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [react native 0.70版本使用ant-design-mobile-rn及icons字体图标库](https://blog.csdn.net/weixin_43233914/article/details/126849915)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] - *2* [react native 使用ant-design-mobile-rn的icon字体库](https://blog.csdn.net/weixin_43233914/article/details/119450451)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值