讓Antd 的Modal 可以拖動


需求:

為了提升交互體驗,滿足用戶“不關彈窗還能看彈窗下的頁面”的需求,需要彈窗可以隨意拖動位置。

原本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手敲完也沒運行,如有問題歡迎指正討論。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM