Ant design Modal 拖拽、缩放功能

2,020 阅读4分钟

主要介绍了如何给 Ant design Modal 弹框添加拖拽、缩放功能。例如结合 draggable、react-draggable 使用,或者直接使用 ant-design-draggable-modal 等

1. 视频预览弹框组件

上一篇文章介绍了 PlyrVideo 和 DPlayerVideo 视频播放器,感兴趣的可以去看看。

在 React 中,forwardRefuseImperativeHandle 是两个重要的 Hooks,它们主要用于处理子组件被父组件控制的情况。

  • forwardRef:允许父组件通过 ref 访问到子组件的实例。通常用于需要直接操作 DOM 或者访问子组件内部方法的场景。
  • useImperativeHandle :当一个父组件需要通过 ref 来控制子组件时,可以使用 useImperativeHandle 来定义 ref 能够访问到的 API。它允许你自定义暴露给父组件的 ref 对象的属性和方法。
import { forwardRef, useImperativeHandle, useRef, useState } from "react";
import { Modal } from "antd";
import PlyrVideo from "@/components/PlyrVideo";
import DPlayerVideo from "@components/DPlayerVideo";

interface iProps {
  onCancel?: () => void;
}

const MModal = (props: iProps, ref: any) => {
  const { onCancel } = props;
  const [isShow, toggleShow] = useState(false); // 控制模态框的显示状态
  const [loading, setLoading] = useState<boolean>(true); // 控制模态框的加载状态
  const [detail, setDetail] = useState<any>(null); // 存储视频详情数据
  const videoRef = useRef<any>(null); // 添加对视频播放器的引用

  useImperativeHandle(ref, () => ({
    // 打开模态框的方法
    openModal: (info: any) => {
      setDetail(info);
      toggleShow(true);
      setLoading(true);
      // 延迟3s
      setTimeout(() => {
        setLoading(false);
      }, 3000);
    },
    // 关闭模态框的方法
    closeModal: () => {
      toggleShow(false); // 隐藏模态框
    },
  }));

  // 处理模态框取消事件的方法
  const handleCancel = () => {
    toggleShow(false); // 隐藏模态框
    if (onCancel) onCancel(); // 回调事件
  };

  return (
    <Modal
      width="700px" // 设置模态框宽度
      title={
         {/* 自定义标题内容 */}
        <div className="video_title">
          <img src={detail?.iconUrl} alt={detail?.resourceName} />
          {detail?.resourceName}
        </div>
      }
      open={isShow} // 控制模态框的显示
      onCancel={handleCancel} // 取消按钮点击事件
      footer={null} // 移除模态框底部的操作栏
      wrapClassName="preview_modal" // 设置模态框容器类名
      mask={false} // 不显示背景遮罩
      maskClosable={false} // 禁止点击遮罩关闭模态框
      destroyOnClose={true} // 关闭时,销毁子元素
      loading={loading} // 控制模态框加载状态
    >
      {/* 根据视频源的格式选择播放器 */}
      {detail?.resourceUrl?.includes(".m3u8") ? (
        <DPlayerVideo
          videoSrc={detail?.resourceUrl}
          autoPlay={true}
          videoRef={videoRef}
        />
      ) : (
        <PlyrVideo
          videoSrc={detail?.resourceUrl}
          autoPlay={true}
          videoRef={videoRef}
        />
      )}
    </Modal>
  );
};

export default forwardRef(MModal);

2. Modal 拖拽功能

2.1 react-draggable

// "react-draggable": "^4.4.6",
// 1. npm i react-draggable 安装
import Draggable from "react-draggable"; // 2. 引入 react-draggable

<Modal
  modalRender={(node: any) => <Draggable>{node}</Draggable>} // 使用自定义渲染函数
></Modal>;

使用上述自定义方式后,整个ant-modal-content都是可以进行拖拽处理的。

要想只有头部能进行拖拽处理,需要配置handle属性

<Modal
  modalRender={(node: any) => (
    <Draggable handle=".ant-modal-header">{node}</Draggable>
  )}
></Modal>

antd-modal-1.png

2.2 draggable

// 安装 "draggable": "^4.2.0",
// @ts-ignore
import Draggable from "draggable";

useImperativeHandle(ref, () => ({
  openModal: (info: any) => {
    setDetail(info);
    toggleShow(true);
    setTimeout(() => {
      setLoading(false);
      // 目标为:Modal组件
      const ele = document.querySelector(".preview_modal");
      new Draggable(ele, {
        // 拖拽handle设置为Modal头部,不设置此参数表示整个Modal都可拖拽
        handle: document.querySelector(".ant-modal-header"),
      });
    }, 1000);
  },
}));

本质上,也是通过transform实现的

antd-modal-2.png

2.3 ant-design-draggable-modal

Ant Design 官方 Issue 中推荐了 ant-design-draggable-modal 这个工具,集拖拽和缩放于一体

// 安装
// yarn add ant-design-draggable-modal
// 引入
import {
  DraggableModal,
  DraggableModalProvider,
} from "ant-design-draggable-modal";
import "ant-design-draggable-modal/dist/index.css";

<DraggableModal
  width="700px"
  title={
    <div className="video_title">
      <img src={detail?.iconUrl} alt={detail?.resourceName} />
      {detail?.resourceName}
    </div>
  }
  open={isShow}
  onCancel={handleCancel}
  footer={null}
  wrapClassName="preview_modal"
  mask={false}
  maskClosable={false}
  destroyOnClose={true} // 关闭时,销毁子元素
>
  {detail?.resourceUrl && (
    <HlsVideo
      videoSrc={detail?.resourceUrl}
      autoPlay={true}
      videoRef={videoRef}
    />
  )}
</DraggableModal>;

使用效果:拖拽和缩放功不太好使,缩放功能没反应;拖拽功能卡卡的,不顺畅,而且只能在部分范围内拖拽。整体感觉不好用,放弃了。。。

但是官方 demo 示例还是很流畅的,很奇怪。

antd-modal-4.png

3. Modal 缩放功能

使用了 CSS 属性来处理

  • resize: both;
    • resize 属性用于指定一个元素是否可以被用户调整大小,以及如何调整大小。
    • 当设置为 both 时,表示元素可以在水平和垂直方向上被调整大小。
  • overflow: auto;
    • overflow 属性用于指定当元素的内容超出其边界时的行为。
    • 当设置为 auto 时,表示如果内容超出容器的尺寸,则显示滚动条以查看所有内容。
  • 当同时使用 resize: both;overflow: auto; 时,可以创建一个可以被用户调整大小的容器,并且当内容超出容器时,会出现滚动条。
// 视频播放器
.preview_modal {
  // 缩放功能
  .ant-modal-content {
    resize: both;
    overflow: auto;
    padding-top: 0;
  }
  // 拖拽功能-鼠标样式
  .ant-modal-header {
    cursor: move;
    padding-top: 20px;
  }
}

antd-modal-3.png