需求:
為了提升交互體驗,滿足用戶“不關彈窗還能看彈窗下的頁面”的需求,需要彈窗可以隨意拖動位置。
原本ant design的彈窗沒有拖拽功能,但是為了保留原本功能樣式,我們要拓展一下這個Modal組件。
思路:
1、首先需要兩個DIV,一個是和視口一樣大的drag-mask,綁定mouseMove事件和mouseUp事件,用來作為拖拽參照;另一個是和Modal一樣大的drag-target,綁定mouseDown事件。
2、已知Modal的樣式.ant-modal{position:relative;top:100px;left:0px;},可見通過更改top、left我們將可以改變Modal位置。
3、mouseDown事件時,顯示drag-mask,並記錄坐標(preX,preY);
4、mouseMove事件時,Modal的新位置=Modal的原位置+移動距離:
left2 = left1 + (pageX-preX);
top2 = top1 + (pageY-preY);
圖示:由灰色位置移動到藍色位置,箭頭表示拖拽軌跡

5、mouseMove到窗口邊緣時不能繼續拖動,誤差10px,也就是說拖拽時鼠標移動到靠近窗口邊緣10px時就自動mouseUp(防止拖出視口);
代碼:
import React, { Component } from 'react'; import ReactDom from 'react-dom'; import { Modal, Empty } from 'antd'; import './dragable.less'; export default class DragableModal extends Component { constructor(props) { super(props); this.state = { visible:false, dragging: false, preX: 0, preY: 0, styleTop: 100, styleLeft:0, } this.windowH = document.body.clientHeight; this.windowW = document.body.clientWidth; } show = () => { this.setState({ visible: true, dragging: false, preX: 0, preY: 0, styleTop: 100, styleLeft:0, }) } hide = () => { this.setState({ visible:false }) } isOverWindow = (moveX, moveY) => { const er = 10; if (moveX < er) return true; if (moveX > (this.windowW - er)) return true; if (moveY < er) return true; if (moveY > (this.windowH - er)) return true; return false; } handleMoseDown=(evt)=>{ this.setSate({ dragging:true, preX:evt.pageX, preY:evt.pageY, }) } handleMouseMove = (evt) => { if (this.isOverWindow) { this.hanldeMouseUp(); return; } const {preX,preY,styleLeft,styleTop} = this.state; const left = styleLeft + (evt.pageX-preX); const top = styleTop + (evt.pageY-preY); this.setState({ preX:evt.pageX, preY:evt.pageY, styleLeft:left, styleTop:top, }) } hanldeMouseUp = () => { this.setState({dragging:false}) } render() { const { visible, dragging, styleLeft, styleTop } = this.state; const style = { left: styleLeft, top: styleTop } return ( <div> <Modal title='拖拽測試' visible={visible} onCancel={this.hide} style={style} maskClosable={false} > <div className='drag-target' onMouseDown={this.handleMouseDown}></div> <Empty description='沒啥內容' /> { dragging && <div
className='drag-mask'
onMouseMove={this.handleMouseMove}
onMouseUp={this.hanldeMouseUp}
></div> } </Modal> </div> ) } }
.drag-mask{ position: fixed; left: 0; right: 0; top: 0; bottom: 0; z-index: 1001; } .drag-target{ position: absolute; left: 0; right: 0; top: 0; bottom: 0; cursor: move; }
PS:
Modal是可以拖動了,但是我發現Modal中原本可以點擊的a標簽不能點了[○・`Д´・ ○] ~
淡定淡定……原來~drag-target是絕對定位absolute,它的兄弟元素a還是普通文檔流,那么drag-target的層級就在兄弟元素們的上面。
我們知道同輩元素定位方式相同,且無z-index設置時,html靠后者層級居上。所以只要給drag-target后面的兄弟元素設置為relative,那么它們就不會被drag-target遮着了。
給a設置position:relative,a又可以點擊了
pps:因為暫時沒有環境,所以上面的代碼demo手敲完也沒運行,如有問題歡迎指正討論。