把拖動div功能用react封裝成class,在頁面直接引入該class即可使用。 title為可拖動區域。panel為要實現拖動的容器。 優化了拖動框超出頁面範圍的情況,也優化了拖動太快時滑鼠超出可拖動區域的情況,優化了拖動會卡頓的情況。 頁面中添加引入方法: <Draggable panelId ...
把拖動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位置以及兩者的差值 **/ onMouseDown (e) { const position = this.getPosition(e) this.setState({moving: true, diffX: position.diffX, diffY: position.diffY}) } // 鬆開滑鼠,設置modal狀態為不可移動 onMouseUp (e) { const { moving } = this.state moving && this.setState({moving: false}); } // 滑鼠移動重新設置modal的位置 onMouseMove (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;