React實現前端選區的示例代碼
什麼是選區
前端選區非常常見,通過鼠標的點擊和移動來創建一個選中頁面,在多個元素需要被選中的情況下,一個個點擊顯的非常拖沓,所以需要通過建立選區來應對多個元素的操作。以下是簡單的建立選區圖片例子:
如何建立選區
瀏覽器繪制選區方式
在瀏覽器中繪制一個矩形,通常是通過其元素的top和left來決定,瀏覽器通過確定元素的top和left位置,並監聽鼠標在移動過程中的偏移量offsetX和offsetY來計算元素的總寬高,最後繪制成元素的總體形狀,固定好元素的top和left極為重要。如下是瀏覽器繪制的示意圖:
瀏覽器在繪制時,總是以左上角的頂點作為元素的起始位置,也就是說如果你的選區是從右往左或者從下往上建立,瀏覽器都會以最終圖形的左上角頂點作為元素的起始位置進行繪制。所以確定左上角頂點位置尤為關鍵。
React計算選區范圍
通過監聽瀏覽器的鼠標移動事件來獲取鼠標移動的范圍,在這裡我們需要先獲取鼠標初始位置,通過movedown事件確定鼠標起始位置,通過isMove來判斷是否鼠標落下並開始移動,記錄鼠標落點位置。代碼如下圖所示:
const [isMove, setIsMove] = useState<boolean>(false); const [downAndUpPosition, setDownAndUpPosition] = useState<Position>(); const handleMouseDown = (event: React.MouseEvent) => { setIsMove(true); let mouseDownPosition: Position = { offsetX: event.pageX - left, offsetY: event.pageY, }; setDownAndUpPosition(mouseDownPosition); };
記錄鼠標落點後,通過mousemove事件記錄鼠標移動坐標點,如果鼠標未落下則不觸發移動事件,避免重復渲染,最後在moveup事件中取消移動標記即可:
const [movePosition, setMovePosition] = useState<Position>(); const handleMouseMove = (event: React.MouseEvent) => { if (!isMove) return; let movePosition: Position = { offsetX: event.pageX - left, offsetY: event.pageY, }; setMovePosition(movePosition); }; const handleMouseUp = () => { setIsMove(false); };
得到鼠標落點位置和鼠標移動坐標後,我們需要去計算鼠標移動坐標與起始位置的坐標象限,如果起始位置的left與top均小於鼠標移動後坐標,則選區在第四象限。以起始位置為坐標原點去計算。代碼如下所示:
const returnDivPosition = ( downAndUpPosition: Position, movePosition: Position ) => { const downPageX = downAndUpPosition.offsetX; const downPageY = downAndUpPosition.offsetY; const movePageX = movePosition.offsetX; const movePageY = movePosition.offsetY; if (downPageX >= movePageX && downPageY >= movePageY) { return 1; } if (downPageX <= movePageX && downPageY >= movePageY) { return 2; } if (downPageX >= movePageX && downPageY <= movePageY) { return 3; } if (downPageX <= movePageX && downPageY <= movePageY) { return 4; } };
計算出鼠標最終位置處於哪個象限後,我們就可以計算出選區的top和left。如果為第一象限則鼠標移動的最終位置就是元素偏移量,如果為第二象限則鼠標移動最終位置的top為元素偏移top,鼠標落點left為元素偏移left,以此類推即可。代碼如下圖:
const offsetPosition = useMemo(() => { if (downAndUpPosition && movePosition) { const quadrant = returnDivPosition(downAndUpPosition, movePosition); switch (quadrant) { case 1: return { top: movePosition.offsetY, left: movePosition.offsetX, }; case 2: return { top: movePosition.offsetY, left: downAndUpPosition.offsetX, }; case 3: return { top: downAndUpPosition.offsetY, left: movePosition.offsetX, }; case 4: return { top: downAndUpPosition.offsetY, left: downAndUpPosition.offsetX, }; } } return { top: 0, left: 0, }; }, [downAndUpPosition, movePosition]);
最後我們計算選區的寬度和高度,通過計算落點位置與鼠標移動後最終位置的差值可獲取寬高。代碼如下:
const offsetSize = useMemo(() => { if (downAndUpPosition && movePosition) { return { width: Math.abs(downAndUpPosition.offsetX - movePosition.offsetX), height: Math.abs(downAndUpPosition.offsetY - movePosition.offsetY), }; } return { width: 0, height: 0, }; }, [downAndUpPosition, movePosition]);
這樣就能夠創建一個選區,完整代碼如下圖:
type Position = { offsetX: number; offsetY: number; }; const boardStyle: CSSProperties = { width: "100%", height: "calc(100vh - 10px)", position: "relative", }; const SelectArea = ()=>{ const [isMove, setIsMove] = useState<boolean>(false); const [downAndUpPosition, setDownAndUpPosition] = useState<Position>(); const [movePosition, setMovePosition] = useState<Position>(); const handleMouseDown = (event: React.MouseEvent) => { setIsMove(true); let mouseDownPosition: Position = { offsetX: event.pageX - left, offsetY: event.pageY, }; setDownAndUpPosition(mouseDownPosition); }; const handleMouseMove = (event: React.MouseEvent) => { if (!isMove) return; let movePosition: Position = { offsetX: event.pageX - left, offsetY: event.pageY, }; setMovePosition(movePosition); }; const handleMouseUp = () => { setIsMove(false); }; const returnDivPosition = ( downAndUpPosition: Position, movePosition: Position ) => { const downPageX = downAndUpPosition.offsetX; const downPageY = downAndUpPosition.offsetY; const movePageX = movePosition.offsetX; const movePageY = movePosition.offsetY; if (downPageX >= movePageX && downPageY >= movePageY) { return 1; } if (downPageX <= movePageX && downPageY >= movePageY) { return 2; } if (downPageX >= movePageX && downPageY <= movePageY) { return 3; } if (downPageX <= movePageX && downPageY <= movePageY) { return 4; } }; const offsetSize = useMemo(() => { if (downAndUpPosition && movePosition) { return { width: Math.abs(downAndUpPosition.offsetX - movePosition.offsetX), height: Math.abs(downAndUpPosition.offsetY - movePosition.offsetY), }; } return { width: 0, height: 0, }; }, [downAndUpPosition, movePosition]); const offsetPosition = useMemo(() => { if (downAndUpPosition && movePosition) { const quadrant = returnDivPosition(downAndUpPosition, movePosition); switch (quadrant) { case 1: return { top: movePosition.offsetY, left: movePosition.offsetX, }; case 2: return { top: movePosition.offsetY, left: downAndUpPosition.offsetX, }; case 3: return { top: downAndUpPosition.offsetY, left: movePosition.offsetX, }; case 4: return { top: downAndUpPosition.offsetY, left: downAndUpPosition.offsetX, }; } } return { top: 0, left: 0, }; }, [downAndUpPosition, movePosition]); return ( <div onMouseDown={handleMouseDown} onMouseMove={handleMouseMove} onMouseUp={handleMouseUp} style={boardStyle} > <Electorate top={offsetPosition.top} left={offsetPosition.left} width={offsetSize.width} height={offsetSize.height} /> </div> ); } type Props = { top: number; left: number; width: number; height: number; }; const Electorate: FC<Props> = ({ top, left, width, height }) => { return ( <div style={{ top: `${top}px`, left: `${left}px`, width: `${Math.abs(width)}px`, height: `${Math.abs(height)}px`, }} className="electorate" /> ); }; //electorate樣式 .electorate { position: absolute; border: 1px solid rgba(33, 127, 235, 0.534); background-color: rgba(33, 127, 235, 0.3); }
到此這篇關於React實現前端選區的示例代碼的文章就介紹到這瞭,更多相關React 前端選區內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- React Native Popup實現示例
- jQuery實現小球點擊發射動畫
- JavaScript canvas實現刮刮樂案例
- React Hook實現對話框組件
- 示例詳解react中useState的用法