本質上是實現一個矩形內的列表進行數據交換,並配套必要的樣式。
交換類型拖放具體分爲三部分git
/* * 必要參數,可考慮從外部接收 * lineHeight是單格高度 * lineWidth是單格寬度 * lineNumber是一行的最大個數 * proportion 是交換位置的最低比例 */ const lineHeight:number = 40; const lineWidth:number = 102 ; const lineNumber:number = 5; const proportion:number = 0.6;
這個很簡單,只要監聽鼠標點擊事件就好了。github
onMouseDown={e => handleMouseDown(e, index)}
同時須要對拖放過程當中須要用到的數據進行初始化。ui
const handleMouseDown = (e: React.MouseEvent, index: number) => { setDragging(true); setDraggingIndex(index); setStartPage({"x": e.pageX,"y": e.pageY}); }
爲了讓使用者看出來進入了拖放模式,樣式也須要微調(具體想成什麼樣子能夠自定義)code
style={getDraggingStyle(index)}`
須要注意的是,拖放的樣式效果會在這經過藉助transform的偏移實現orm
const getDraggingStyle = (index: number) => { if(index !== draggingIndex) { return; } return { transform:`translate(${offsetPage.x}px,${offsetPage.y}px), opacity:0.5, } }
關鍵點有兩點事件
以前已經提到過了實現方式是transform的偏移,其中的變量則是能夠經過監聽鼠標移動事件。ci
onMouseMove={e => handleMouseMove(e)}
並在移動過程當中計算偏移量,包括X軸(左右)和Y軸(上下)get
const handleMouseMove = (e: React.MouseEvent) => { setOffsetPage({"x": e.pageX - startPage.x, "y": e.pageY - startPage.y}); }
爲方便移動過程當中不受到其餘因素干擾,能夠建立一個覆蓋層,元素在覆蓋層上移動。覆蓋層隨移動而生,隨放下而去。源碼
{dragging && ( <div className="drag-mask" onMouseMove={e => handleMouseMove(e)} onMouseUp={e => handleMouseUp(e)} /> )}
覆蓋層的CSSit
.drag-mask { position: fixed; left: 0; right: 0; top: 0; bottom: 0; background:rgba(0, 0, 0, 0); }
關鍵點有兩點
以前的偏移位置是實時計算出來的,正負表明方向,大小表示距離。
移動的位置在第一步就定義了,還須要的是被交換元素的位置。
這裏用最少移動距離來判斷是哪一個元素須要被交換。舉例來講,若是一個方向上移動的距離沒有超過一個元素的一半距離(proportion,可自定義),則和前一個元素交換,不然和當前元素交換。
let leastX = lineWidth * proportion; //X軸最少移動距離 let leastY = lineHeight * proportion; //Y軸最少移動距離
以前的移動格數用向下取整就能夠得到
let offsetNumberX = Math.floor(Math.abs(lengthX / lineWidth)); //X軸移動格數 let offsetNumberY = Math.floor(Math.abs(lengthY / lineHeight)); //Y軸移動格數
最後的計算
if(offsetLastX >= leastX) { offsetNumberX++; } if(offsetLastY >= leastY){ offsetNumberY++; }
if(currentIndex < list.length && currentIndex >= 0) { [list[startIndex],list[currentIndex]] = [list[currentIndex],list[startIndex]]; }
超出列表範圍。
列表看上去多是一個不規則區域,可是最後一行空位的能夠在第一個限制中計算,因此能夠合併成一個矩形區域。
矩形的範圍經過ref得到Dom元素
const rangeUI = useRef<HTMLUListElement>(null); <ul ref={ rangeUI }>
得到元素的相關數據,計算得到位置數據
const [mainDirection, setMainDirection] = useState<normalType>({"top": 0, "left": 0, "right": 0 ,"bottom": 0}); useEffect(() => { if(rangeUI.current) { setMainDirection({"top": rangeUI.current.offsetTop, "left":rangeUI.current.offsetLeft, "right": rangeUI.current.offsetLeft + rangeUI.current.offsetWidth ,"bottom": rangeUI.current.offsetTop + rangeUI.current.offsetHeight}) } },[])
判斷是否超出範圍
if((flagX === 1 && lengthX > (mainDirection.right - startPage.x)) || (flagX === -1 && -lengthX > (startPage.x - mainDirection.left)) || (flagY === 1 && lengthY > (mainDirection.bottom - startPage.y)) || (flagY === -1 && -lengthY > (startPage.y - mainDirection.top)) )
被交換元素
currentIndex = startIndex+(offsetNumberX * flagX)+(offsetNumberY * flagY)*lineNumber;
交換位置,即交換列表中的元素位置
[list[startIndex],list[currentIndex]] =[list[currentIndex],list[startIndex]];
最後把數據都初始化
setDragging(false); setDraggingIndex(-1); setStartPage({"x": 0, "y": 0}); setOffsetPage({"x": 0, "y": 0})