antd-modal 替代品 可拖动弹窗组件

 

 

1、ModalWin.js 

import React from 'react';
import {Button, Icon} from 'antd';
import PropTypes from 'prop-types';
import styles from './styles.less';

class ModalWin extends React.Component {

  constructor(props) {
    super(props);
    const {visible} = this.props;
    const {clientWidth, clientHeight} = document.documentElement;
    this.state = {
      visible: (visible !== '' && visible !== null) ? visible : false,
      clientWidth,
      clientHeight,
      pageX: clientWidth / 3,
      pageY: '10vh',
      moving: false,
    }
  }

  componentWillReceiveProps({visible}) {
    if (visible !== '' && visible !== null) {
      this.setState({visible});
    }
  }

  componentDidMount() {
    this.resize();
    window.addEventListener('resize', this.resize);
  }

  resize = () => {
    const {clientWidth, clientHeight} = document.documentElement;
    // console.log(`监听到窗口大小变化 宽:${clientWidth} 高:${clientHeight}`)
    const modal = document.getElementById("modal");
    if (modal) {
      const pageY = (clientHeight - modal.offsetHeight) / 2;
      const pageX = (clientWidth - modal.offsetWidth) / 2;
      this.setState({clientWidth, clientHeight, pageX, pageY})
    }
  }

  onCancel = () => {
    const {onCancel} = this.props;
    if (onCancel) {
      onCancel();
    } else {
      this.setState({visible: false})
    }
  }

  open = () => {
    this.setState({visible: true});
  }

  // 获取鼠标点击title时的坐标、title的坐标以及两者的位移
  getPosition = (e) => {
    // 标题DOM元素titleDom
    const titleDom = e.target;
    // titleDom的坐标
    const X = titleDom.getBoundingClientRect().left;
    const Y = titleDom.getBoundingClientRect().top;
    // 鼠标点击的坐标
    let mouseX = 0, mouseY = 0;
    if (e.pageX || e.pageY) {  //ff,chrome等浏览器
      mouseX = e.pageX;
      mouseY = e.pageY;
    } else {
      mouseX = e.clientX + document.body.scrollLeft - document.body.clientLeft;
      mouseY = e.clientY + document.body.scrollTop - document.body.clientTop;
    }
    // 鼠标点击位置与modal的位移
    const diffX = mouseX - X;
    const diffY = mouseY - Y;
    return {X, Y, mouseX, mouseY, diffX, diffY}
  }

  /**
   * 鼠标按下,设置modal状态为可移动,并注册鼠标移动事件
   * 计算鼠标按下时,指针所在位置与modal位置以及两者的差值
   **/
  onMouseDown = (e) => {
    const position = this.getPosition(e);
    window.onmousemove = this.onMouseMove;
    this.setState({moving: true, diffX: position.diffX, diffY: position.diffY});
  }

  // 松开鼠标,设置modal状态为不可移动,
  onMouseUp = (e) => {
    this.setState({moving: false});
  }

  // 鼠标移动重新设置modal的位置
  onMouseMove = (e) => {
    const {moving, diffX, diffY} = this.state;
    if (moving) {
      // 获取鼠标位置数据
      const position = this.getPosition(e);
      // 计算modal应该随鼠标移动到的坐标
      const x = position.mouseX - diffX;
      const y = position.mouseY - diffY;
      // 窗口大小
      const {clientWidth, clientHeight} = document.documentElement;
      const modal = document.getElementById("modal");
      if (modal) {
        // 计算modal坐标的最大值
        const maxHeight = clientHeight - modal.offsetHeight;
        const maxWidth = clientWidth - modal.offsetWidth;
        // 判断得出modal的最终位置,不得超出浏览器可见窗口
        const left = x > 0 ? (x < maxWidth ? x : maxWidth) : 0;
        const top = y > 0 ? (y < maxHeight ? y : maxHeight) : 0;
        this.setState({pageX: left, pageY: top})
      }
    }
  }

  render() {
    const {okText, onOk, cancelText, children, width, height, title} = this.props;
    const {visible, clientWidth, clientHeight, pageX, pageY} = this.state;
    const modal = (
      <div className={styles.custom_modal_mask} style={{width: clientWidth, height: clientHeight}}>
        <div
          id="modal"
          className={styles.custom_modal_win}
          style={{
            width: width ? width : clientWidth / 3,
            height: height ? height : 'unset',
            marginLeft: pageX,
            marginTop: pageY,
          }}>
          <div
            className={styles.custom_modal_header}
            onMouseDown={this.onMouseDown}
            onMouseUp={this.onMouseUp}
          >
            {title ? title : null}
            <div className={styles.custom_modal_header_close} onClick={this.onCancel}>
              <Icon type="close"/>
            </div>
          </div>
          <div className={styles.custom_modal_content}>
            {children}
          </div>
          <div className={styles.custom_modal_footer}>
            <div className={styles.custom_modal_footer_inner}>
              <Button onClick={this.onCancel}>
                {cancelText ? cancelText : '取消'}
              </Button>
              <Button type="primary" onClick={onOk} style={{marginLeft: '10px'}}>
                {okText ? okText : '确定'}
              </Button>
            </div>
          </div>
        </div>
      </div>
    )
    return <div>{!visible ? null : modal}</div>
  }
}

ModalWin.propTypes = {
  visible: PropTypes.bool,
  title: PropTypes.string,
  width: PropTypes.any,
  height: PropTypes.any,
  closable: PropTypes.bool,
  okText: PropTypes.string,
  cancelText: PropTypes.string,
  onCancel: PropTypes.func,
  onOk: PropTypes.func,
  onOkLoading: PropTypes.bool,

}

export default ModalWin;

2.style.less


/************* 自定义modal相关样式 ****************/
.custom_modal_mask {
  top: 0;
  left: 0;
  position: fixed;
  text-align: center;
  background-color: rgba(0, 0, 0, .3);
  z-index: 999;
}

.custom_modal_win {
  background: #fff;
  position: relative;
  border-radius: 5px;
  min-width: 400px;
}

.custom_modal_header {
  width: 100%;
  height: 40px;
  line-height: 30px;
  cursor: move;
  text-align: left;
  padding: 5px 20px;
  border-bottom: 1px solid #e3e3e3;
}

.custom_modal_header_close {
  float: right;
  line-height: 30px;
  cursor: pointer;
}

.custom_modal_content {
  top: 0;
  width: 100%;
  bottom: 50px;
  overflow: auto;
  padding: 10px 20px;
  text-overflow: ellipsis;
  background-color: #fff;
  text-align: left;
  max-height: 80vh;
  min-height: 50px;
}

.custom_modal_footer {
  bottom: 0;
  width: 100%;
  height: 46px;
  background: #fff;
  border-top: 1px solid #e3e3e3;
  border-radius: 0 0 5px 5px;
}

.custom_modal_footer_inner {
  float: right;
  right: 8px;
  bottom: 8px;
  position: absolute;
}

 

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值