原生写法

// 封装组件
import React, { useState, useRef } from 'react';

const DraggableModal = ({ children }) => {
  const [position, setPosition] = useState({ x: 0, y: 0 });
  const modalRef = useRef(null);

  const handleMouseDown = (e) => {
    const modal = modalRef.current;
    const startX = e.clientX - modal.offsetLeft;
    const startY = e.clientY - modal.offsetTop;

    const handleMouseMove = (e) => {
      setPosition({
        x: e.clientX - startX,
        y: e.clientY - startY
      });
    };

    const handleMouseUp = () => {
      document.removeEventListener('mousemove', handleMouseMove);
      document.removeEventListener('mouseup', handleMouseUp);
    };

    document.addEventListener('mousemove', handleMouseMove);
    document.addEventListener('mouseup', handleMouseUp);
  };

  return (
    <div
      ref={modalRef}
      style={{ position: 'absolute', left: position.x, top: position.y }}
    >
      <div style={{ cursor: 'move' }} onMouseDown={handleMouseDown}>
        Drag Me
      </div>
      {children}
    </div>
  );
};

export default DraggableModal;




// 使用
<DraggableModal>
  <ModalContent />
</DraggableModal>






使用库并处理拖动和点击事件重叠问题

// 使用库实现功能
你可以使用react-draggable库来实现这个功能。首先安装react-draggable库:

```
npm install react-draggable
```

然后在你的组件中引入react-draggable库,并使用它来包裹你想要添加可拖动功能的按钮:

```jsx
import React from 'react';
import Draggable from 'react-draggable';

const DraggableButton = () => {
  let isDragging = false;

  const handleDrag = () => {
    isDragging = true;
  };
  const handleStop = () => {
    console.log('handleStop');

    // 触发 onStop 后会触发 onClick,加个 setTimeout 是为了让修改不“马上”发生,导致 handleClick 拿到的不是预期的结果
    setTimeout(() => (isDragging = false), 0);
  };

  const handleClick = () => {
    console.log('handleClick');

    if (isDragging) {
      return;
    }

    console.log('啊,我被 click 了');
  };

  return (
    <Draggable onDrag={handleDrag} onStop={handleStop}>
      <button onClick={handleClick} style={{ position: 'fixed', bottom: 0, right: 0 }}>可拖动按钮</button>
    </Draggable>
  );
}

export default DraggableButton;


解决库和Antd Tooltip配置使用无法拖动

可以尝试使用`react-draggable`的`handle`属性来指定拖动的句柄,避免事件冲突。
示例代码如下:


<Draggable handle=".drag-handle">
  <div>
    <Button className="drag-handle">
      Hover me
      <Tooltip title="Tooltip text">
        <QuestionCircleOutlined />
      </Tooltip>
    </Button>
  </div>
</Draggable>



移动端或PC端如何判断react-draggable包裹的组件时点击还是移动(含移动位置判断),用reack hook代码实现

// 判断是否点击还是移动

import React, { useState } from 'react';
import Draggable from 'react-draggable';

const DraggableComponent = () => {
  const [isDragging, setIsDragging] = useState(false);

  const handleTouchStart = (e) => {
    setIsDragging(false);
  };

  const handleTouchMove = (e) => {
      setIsDragging(true);
  };

  const handleTouchStop = () => {
    if (!isDragging) {
        // 没在拖动
    }
    settimeout(() => {
        setIsDragging(false)
    }, 0)
  }

  return (
    <Draggable
      onStart={handleTouchStart}
      onDrag={handleTouchMove}
      onStop={handleTouchStop}
    >
      <div style={{ width: '100px', height: '100px', backgroundColor: 'lightblue' }}>
        Drag me
      </div>
    </Draggable>
  );
};

export default DraggableComponent;






// 下方是判断移动位置

import React, { useState } from 'react';
import Draggable from 'react-draggable';

const DraggableComponent = () => {
  const [isDragging, setIsDragging] = useState(false);
  const [startX, setStartX] = useState(null);
  const [startY, setStartY] = useState(null);

  const handleTouchStart = (e) => {
    setStartX(e.touches[0].clientX);
    setStartY(e.touches[0].clientY);
    setIsDragging(false);
  };

  const handleTouchMove = (e) => {
    const moveX = e.touches[0].clientX;
    const moveY = e.touches[0].clientY;

    if (Math.abs(moveX - startX) > 5 || Math.abs(moveY - startY) > 5) {
      setIsDragging(true);
    }
  };

  const handleTouchStop = () => {
    setIsDragging(false)
  }

  return (
    <Draggable
      onStart={handleTouchStart}
      onDrag={handleTouchMove}
      onStop={handleTouchStop}
    >
      <div style={{ width: '100px', height: '100px', backgroundColor: 'lightblue' }}>
        Drag me
      </div>
    </Draggable>
  );
};

export default DraggableComponent;

拖动范围不超过浏览器窗口

// 拖动范围不超过浏览器窗口
<Draggable
      bounds="body"
    >
      <div style={{ width: '100px', height: '100px', backgroundColor: 'lightblue' }}>
        Drag me
      </div>
</Draggable>



拖动范围不超过某个div盒子内部

import Draggable from 'react-draggable';

const App = () => {
  const boundsRef = useRef(null);

  return (
    <div ref={boundsRef} style={{ width: '400px', height: '400px', border: '1px solid black', position: 'relative' }}>
      <Draggable bounds={boundsRef}>
        <div className="draggable-box" style={{ width: '100px', height: '100px', background: 'red' }}>
          Drag Me
        </div>
      </Draggable>
    </div>
  );
};

export default App;


使用react-draggable包裹react-select组件时,点击react-select显示的下拉框位置不在下方,位置错乱了如何解决

import React from 'react';
import Draggable from 'react-draggable';
import Select from 'react-select';

const customStyles = {
  menuPortal: base => ({ ...base, zIndex: 9999 }), // 设置z-index确保下拉框在最上层显示
};

const App = () => {
  return (
    <Draggable>
      <Select
        options={options}
        styles={customStyles}
        menuPortalTarget={document.body}
      />
    </Draggable>
  );
};
```

通过设置menuPortalTarget为document.body,可以将下拉框渲染到body节点中,确保其显示在正确的位置。同时,通过自定义样式customStyles,可以设置下拉框的z-index等样式属性。


Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐