当前位置: 首页 > article >正文

实现可拖拽的 Ant Design Modal 并保持下层 HTML 可操作性

前言

在开发复杂的前端界面时,我们常常需要一个可拖拽的弹窗(Modal),同时又希望用户能够在弹窗打开的情况下操作下层的内容。Ant Design 的 Modal 组件提供了强大的功能,但默认情况下,弹窗会覆盖整个页面,阻止用户与下层内容交互。本文将通过代码示例,展示如何实现一个可拖拽的 Modal,并确保弹窗不会影响下层 HTML 的操作

效果图

在这里插入图片描述

视频演示

可拖拽antd Modal和保持下层 HTML 可操作性

技术栈

• React: 前端框架
• Ant Design: UI 组件库
• react-draggable: 可拖拽组件库

需求分析

  1. 可拖拽的 Modal: 用户可以通过拖拽 Modal 的标题栏来移动弹窗位置。
  2. 下层内容可操作: 弹窗打开时,用户仍然可以操作下层的 HTML 元素。
  3. 响应式拖拽限制: 弹窗的拖拽范围应限制在可视区域内。
    实现思路
  4. 使用 react-draggable 包装 Modal: 利用 react-draggable 提供的拖拽功能,将 Modal 包装为可拖拽组件。
  5. 自定义 Modal 的遮罩层: 通过设置遮罩层的 pointerEventsbackgroundColor,使其完全透明且不阻止鼠标事件。
  6. 动态计算拖拽边界: 根据窗口大小和 Modal 的尺寸,动态计算拖拽的边界,确保 Modal 不会超出可视区域。

代码实现

import { Button, Modal, Input } from 'antd';
import React, { useRef, useState } from 'react';
import type { DraggableData, DraggableEvent } from 'react-draggable';
import Draggable from 'react-draggable';

const text = `...`; // 省略的 Lorem Ipsum 文本

const App: React.FC = () => {
  const [open, setOpen] = useState(false);
  const [disabled, setDisabled] = useState(false);
  const [bounds, setBounds] = useState({ left: 0, top: 0, bottom: 0, right: 0 });
  const draggleRef = useRef<HTMLDivElement>(null);

  const showModal = () => {
    setOpen(true);
  };

  const handleOk = (e: React.MouseEvent<HTMLElement>) => {
    console.log(e);
    setOpen(false);
  };

  const handleCancel = (e: React.MouseEvent<HTMLElement>) => {
    console.log(e);
    setOpen(false);
  };

  const onStart = (_event: DraggableEvent, uiData: DraggableData) => {
    const { clientWidth, clientHeight } = window.document.documentElement;
    const targetRect = draggleRef.current?.getBoundingClientRect();
    if (!targetRect) {
      return;
    }
    setBounds({
      left: -targetRect.left + uiData.x,
      right: clientWidth - (targetRect.right - uiData.x),
      top: -targetRect.top + uiData.y,
      bottom: clientHeight - (targetRect.bottom - uiData.y),
    });
  };

  return (
    <>
      <Button type="primary" onClick={showModal}>Open Draggable Modal</Button>
      <div>
        <Input placeholder="Input here" />
        <p>{text}</p>
        <p>{text}</p>
        <p>{text}</p>
        <p>{text}</p>
      </div>
      <Modal
        title={
          <div
            style={{
              width: '100%',
              cursor: 'move',
            }}
            onMouseOver={() => {
              if (disabled) {
                setDisabled(false);
              }
            }}
            onMouseOut={() => {
              setDisabled(true);
            }}
          >
            Draggable Modal
          </div>
        }
        open={open}
        onOk={handleOk}
        onCancel={handleCancel}
        maskStyle={{
          pointerEvents: 'none', // 禁用遮罩层的交互,允许下层内容被操作
          backgroundColor: 'rgba(0, 0, 0, 0)', // 使遮罩层完全透明
        }}
        modalRender={modal => (
          <Draggable
            disabled={disabled}
            bounds={bounds}
            onStart={(event, uiData) => onStart(event, uiData)}
          >
            <div ref={draggleRef} style={{ position: 'relative', zIndex: 1000 }}>
              {modal}
            </div>
          </Draggable>
        )}
        bodyStyle={{ overflow: 'hidden' }} // 防止 Modal 内容滚动
        style={{ pointerEvents: 'auto' }} // 确保 Modal 可以正常交互
        getContainer={false} // 防止 Modal 被附加到默认的 body 中
        wrapClassName="modal-no-mask" // 自定义样式类
      >
        <p>
          Just don't learn physics at school and your life will be full of magic and miracles.
        </p>
        <br />
        <p>Day before yesterday I saw a rabbit, and yesterday a deer, and today, you.</p>
      </Modal>
      <style>
        {`
          .modal-no-mask {
            pointer-events: none; /* 确保 Modal 不阻止鼠标事件 */
          }
          .modal-no-mask .ant-modal-content {
            pointer-events: auto; /* 确保 Modal 内容可以交互 */
          }
        `}
      </style>
    </>
  );
};

export default App;

关键点解析

  1. react-draggable 的使用
    Draggable 组件通过 bounds 属性限制拖拽范围,确保 Modal 不会超出可视区域。
    onStart 回调函数用于动态计算拖拽边界。
  2. 自定义遮罩层
    • 通过设置 maskStylepointerEventsnonebackgroundColor 为透明,使遮罩层不会阻止鼠标事件,同时保持完全透明。
  3. Modal 的可交互性
    • 设置 style={{ pointerEvents: ‘auto’ }} 确保 Modal 内容可以正常交互。
    • 使用 getContainer={false} 防止 Modal 被附加到默认的 body 中,避免样式冲突。
  4. 动态计算拖拽边界
    • 在 onStart 回调中,根据窗口大小和 Modal 的尺寸动态计算拖拽边界,确保 Modal 的拖拽范围始终在可视区域内。
    效果展示
  5. 可拖拽的 Modal: 用户可以通过拖拽标题栏移动 Modal 的位置。
  6. 下层内容可操作: 弹窗打开时,用户仍然可以操作下层的输入框和其他内容。
  7. 响应式拖拽限制: Modal 的拖拽范围始终在可视区域内,不会超出屏幕边界。

总结

通过结合 react-draggableAnt DesignModal 组件,我们实现了一个可拖拽的弹窗,并确保弹窗不会影响下层 HTML 的操作。这种实现方式在复杂的前端界面中非常实用,例如在地图应用、图表展示或多任务操作场景中,用户可以在弹窗打开的情况下继续操作下层内容。
希望本文的实现思路和代码示例能为你的项目提供帮助!


http://www.kler.cn/a/552299.html

相关文章:

  • 百度智能云—千帆 ModelBuilder API的简单调用(Java)
  • leetcode232-用栈实现队列
  • 百度搜索和文心智能体接入DeepSeek满血版——AI搜索的新纪元
  • 【网络安全 | 漏洞挖掘】我如何通过Cookie Manipulation发现主域上的关键PII?
  • [OD E 100] 服务器广播需要广播的服务器数量
  • Hive Orc表数据导出和导入
  • Param ‘serviceName‘ is illegal, serviceName is blank
  • Rabbitmq的三个端口区分
  • python 的框架 dash 开发TodoList Web 应用
  • Spring Boot自动装配:约定大于配置的魔法解密
  • 组合模式详解(Java)
  • React 与 Vue 对比指南 - 上
  • 备战蓝桥杯 Day4 差分
  • 学习机器视觉halcon3D需要什么基础知识
  • 【工具类】 Hutool 中用于生成随机数的工具类
  • 【USRP】N210,X310,X410 如何优雅得接入 Vmware 虚拟机
  • Vue学习记录19
  • CPP集群聊天服务器开发实践(六):Redis发布订阅消息队列及服务器集群通信
  • JAVA过滤器(学习自用)
  • Vue.js 框架