react 可拖拽改变位置和大小的弹窗
一 目标
最近,项目上需要一个可以弹出一个可以移动位置和改变大小的窗口,来显示一下对当前页面的一个辅助内容
二 思路
1.之前写过一个antd modal的可移动弹窗但是毕竟不如自己写的更定制化,比如不需要显示遮罩层,但是移动的思想一致都是鼠标的按下和抬起事件以及坐标位置来计算改变弹窗位置.
2.窗口的的小调节可以用css的resize属性,当然也可以自己写一个指定边框拖拽调整大小的功能,后期可能会更新
三 代码内容
index.js
1 import React, { Component } from 'react' 2 import { Icon } from 'antd' 3 import style from './index.less' 4 class PopContainer extends Component { 5 constructor(props) { 6 super(props); 7 this.windowH = document.body.clientHeight; 8 this.windowW = document.body.clientWidth; 9 this.state = { 10 styleTop: 20, 11 styleLeft: 10, 12 styleHeight: props.height || this.windowH * 0.85, 13 styleWidth: props.width || this.windowW * 0.45, 14 } 15 } 16 //计算是否超出屏幕 17 InWindow = (left, top, startPosX, startPosY) => { 18 let H = document.body.clientHeight; 19 let W = document.body.clientWidth; 20 if ((left < 20 && startPosX > left) || (left > W - 20 && startPosX < left) || 21 (top < 20 && startPosY > top) || ((top > H - 20 && startPosY < top))) { 22 return false 23 } 24 return true 25 } 26 onMouseDown = e => { 27 e.preventDefault(); 28 let startPosX = e.clientX; 29 let startPosY = e.clientY; 30 const { styleLeft, styleTop } = this.state; 31 document.body.onmousemove = e => { 32 let left = e.clientX - startPosX + styleLeft; 33 let top = e.clientY - startPosY + styleTop; 34 if (this.InWindow(e.clientX, e.clientY, startPosX, startPosY)) { 35 this.setState({ 36 styleLeft: left, 37 styleTop: top, 38 }) 39 } else { 40 document.body.onmousemove = null; 41 document.body.onmouseup = null; 42 } 43 }; 44 document.body.onmouseup = function () { 45 document.body.onmousemove = null; 46 document.body.onmouseup = null; 47 }; 48 }; 49 render() { 50 const { styleLeft, styleTop, styleHeight, styleWidth } = this.state 51 const { visible, onClose, children, bodyStyle, title } = this.props 52 return <div className={style.popContainer} style={{ 53 display: visible ? "block" : "none", 54 left: styleLeft + 'px', top: styleTop + 'px', 55 }}> 56 <div className={style.header}> 57 <div className={style.title} onMouseDown={this.onMouseDown} >{title}</div> 58 <span className={style.close} onClick={onClose}><Icon type="close" /></span> 59 </div> 60 <div className={style.content} style={{ ...bodyStyle }}> 61 {children} 62 </div> 63 <div className={style.footer}> 64 </div> 65 </div> 66 67 } 68 } 69 70 export default PopContainer;
index.less
.popContainer { position : fixed; width : 50vw; height : 80vh; background-color: white; z-index : 200; box-shadow : 0px 4px 12px 0px rgba(0, 0, 0, 0.45); border-radius : 4px; resize : both; overflow : auto; min-width : 200px; min-height : 60px; .header { padding : 16px 24px; color : rgba(0, 0, 0, 0.65); border-bottom: 1px solid #e8e8e8; border-radius: 4px 4px 0 0; .close { position : absolute; top : 0; right : 0; cursor : pointer; width : 56px; height : 56px; font-size : 16px; line-height: 56px; text-align : center; color : rgba(0, 0, 0, 0.45); &:hover { color: black; } } .title { cursor : move; color : rgba(0, 0, 0, 0.85); font-weight: 500; font-size : 16px; line-height: 22px; } } .content { height : calc(100% - 70px); overflow-y: scroll; padding : 24px; } }
引用的文件
import PopContainer from './PopContainer/index.js' ... onShow = ()=>{ this.setState({visible:true}) } onClose=()=>{ this.setState({visible:false}) } render(){ <Button onClick={this.onShow}>打开弹窗</Button> <PopContainer visible={this.state.visible} onClose={this.onClose} title="标题" > 内容 <PopContainer /> }