把拖动div功能用react封装成class,在页面直接引入该class即可使用。
title为可拖动区域。panel为要实现拖动的容器。
优化了拖动框超出页面范围的情况,也优化了拖动太快时鼠标超出可拖动区域的情况,优化了拖动会卡顿的情况。
页面中添加引入方法:
<Draggable panelId="要拖动容器的id" titleId="容器内标题的id" contentId="容器内除标题外的其他部分id" setPanelPosition={this.setPanelPosition.bind(this)}/>
页面中添加拖拽回调函数
//推拽回调函数 setPanelPosition(left,top){ this.setState({pageX: left, pageY: top}) }
要拖动的div如下:
<div id="要拖动的id" style={{left:this.state.pageX,top:this.state.pageY}}></div>
封装的class代码:
import React from 'react'; class Draggable extends React.Component { constructor(props) { super(props); this.state = { }; } //拖拽 initDrag(){ let {panelId,titleId,contentId} = this.props; this.panelDom = document.getElementById(panelId); this.titleDom = document.getElementById(titleId); this.contentDom = document.getElementById(contentId); this.backgroundDom = document.body; this.bindEvent(); } //region event componentDidMount() { this.initDrag(); } bindEvent(){ this.titleDom.onmousedown = this.onMouseDown.bind(this); this.titleDom.onmouseup = this.onMouseUp.bind(this); this.titleDom.onmousemove = this.onMouseMove.bind(this); this.contentDom.onmouseup = this.onContentMouseUp.bind(this); this.contentDom.onmousemove = this.onContentMouseMove.bind(this); this.backgroundDom.onmouseup = this.onBackgroundMouseUp.bind(this); this.backgroundDom.onmousemove = this.onBackgroundMouseMove.bind(this); let step = ()=>{ this.activeAnimation = true; window.requestAnimationFrame(step); }; window.requestAnimationFrame(step); } /** * 鼠标按下,设置modal状态为可移动,并注册鼠标移动事件 * 计算鼠标按下时,指针所在位置与modal位置以及两者的差值 **/ onm ouseDown (e) { const position = this.getPosition(e) this.setState({moving: true, diffX: position.diffX, diffY: position.diffY}) } // 松开鼠标,设置modal状态为不可移动 onm ouseUp (e) { const { moving } = this.state moving && this.setState({moving: false}); } // 鼠标移动重新设置modal的位置 onm ouseMove (e) { const {moving, diffX, diffY} = this.state if (moving) { if(this.activeAnimation){ // 获取鼠标位置数据 const position = this.getPosition(e) // 计算modal应该随鼠标移动到的坐标 const x = position.mouseX - diffX const y = position.mouseY - diffY // 窗口大小,结构限制,需要做调整,减去侧边栏宽度 const { clientWidth, clientHeight } = document.documentElement const modal = this.panelDom 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 if(this.props.setPanelPosition){ this.props.setPanelPosition(left,top); } } this.activeAnimation = false; } } } onContentMouseMove(e){ let obj = {}; obj.target = this.titleDom; obj.pageX = e.pageX; obj.screenY = e.screenY; this.onMouseMove(obj); } onContentMouseUp(){ this.onMouseUp(); } onBackgroundMouseMove(e){ let obj = {}; obj.target = this.titleDom; obj.pageX = e.pageX; obj.screenY = e.screenY; this.onMouseMove(obj); } onBackgroundMouseUp(){ this.onMouseUp(); } //endregion //region request // 获取鼠标点击title时的坐标、title的坐标以及两者的位移 getPosition (e) { // 标题DOM元素titleDom const titleDom = e.target // titleDom的坐标(视窗) const X = titleDom.getBoundingClientRect().left // 由于Y轴出现滚动条,需要与鼠标保持一致,存储页面相对位置 const Y = this.panelDom.offsetTop // 鼠标点击的坐标(页面) let mouseX = e.pageX let mouseY = e.screenY // 鼠标点击位置与modal的位移 const diffX = mouseX - X const diffY = mouseY - Y return {X, Y, mouseX, mouseY, diffX, diffY} } //endregion //region render //endregion //region clear //endregion render() { return ( <> </> ); } } export default Draggable;