React實現(xiàn)前端選區(qū)的示例代碼
什么是選區(qū)
前端選區(qū)非常常見,通過鼠標(biāo)的點擊和移動來創(chuàng)建一個選中頁面,在多個元素需要被選中的情況下,一個個點擊顯的非常拖沓,所以需要通過建立選區(qū)來應(yīng)對多個元素的操作。以下是簡單的建立選區(qū)圖片例子:

如何建立選區(qū)
瀏覽器繪制選區(qū)方式
在瀏覽器中繪制一個矩形,通常是通過其元素的top和left來決定,瀏覽器通過確定元素的top和left位置,并監(jiān)聽鼠標(biāo)在移動過程中的偏移量offsetX和offsetY來計算元素的總寬高,最后繪制成元素的總體形狀,固定好元素的top和left極為重要。如下是瀏覽器繪制的示意圖:

瀏覽器在繪制時,總是以左上角的頂點作為元素的起始位置,也就是說如果你的選區(qū)是從右往左或者從下往上建立,瀏覽器都會以最終圖形的左上角頂點作為元素的起始位置進行繪制。所以確定左上角頂點位置尤為關(guān)鍵。
React計算選區(qū)范圍
通過監(jiān)聽瀏覽器的鼠標(biāo)移動事件來獲取鼠標(biāo)移動的范圍,在這里我們需要先獲取鼠標(biāo)初始位置,通過movedown事件確定鼠標(biāo)起始位置,通過isMove來判斷是否鼠標(biāo)落下并開始移動,記錄鼠標(biāo)落點位置。代碼如下圖所示:
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);
};記錄鼠標(biāo)落點后,通過mousemove事件記錄鼠標(biāo)移動坐標(biāo)點,如果鼠標(biāo)未落下則不觸發(fā)移動事件,避免重復(fù)渲染,最后在moveup事件中取消移動標(biāo)記即可:
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);
};得到鼠標(biāo)落點位置和鼠標(biāo)移動坐標(biāo)后,我們需要去計算鼠標(biāo)移動坐標(biāo)與起始位置的坐標(biāo)象限,如果起始位置的left與top均小于鼠標(biāo)移動后坐標(biāo),則選區(qū)在第四象限。以起始位置為坐標(biāo)原點去計算。代碼如下所示:
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;
}
};計算出鼠標(biāo)最終位置處于哪個象限后,我們就可以計算出選區(qū)的top和left。如果為第一象限則鼠標(biāo)移動的最終位置就是元素偏移量,如果為第二象限則鼠標(biāo)移動最終位置的top為元素偏移top,鼠標(biāo)落點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]);最后我們計算選區(qū)的寬度和高度,通過計算落點位置與鼠標(biāo)移動后最終位置的差值可獲取寬高。代碼如下:
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]);這樣就能夠創(chuàng)建一個選區(qū),完整代碼如下圖:
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);
}到此這篇關(guān)于React實現(xiàn)前端選區(qū)的示例代碼的文章就介紹到這了,更多相關(guān)React 前端選區(qū)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Taro?React自定義TabBar使用useContext解決底部選中異常
這篇文章主要為大家介紹了Taro?React底部自定義TabBar使用React?useContext解決底部選中異常(需要點兩次才能選中的問題)示例詳解,有需要的朋友可以借鑒參考下2023-08-08
React-Router如何進行頁面權(quán)限管理的方法
本篇文章主要介紹了React-Router如何進行頁面權(quán)限管理的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-12-12
React如何使用refresh_token實現(xiàn)無感刷新頁面
本文主要介紹了React如何使用refresh_token實現(xiàn)無感刷新頁面,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-04-04
react中實現(xiàn)將一個視頻流為m3u8格式的轉(zhuǎn)換
這篇文章主要介紹了react中實現(xiàn)將一個視頻流為m3u8格式的轉(zhuǎn)換方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-07-07
更強大的React 狀態(tài)管理庫Zustand使用詳解
這篇文章主要為大家介紹了更強大的React 狀態(tài)管理庫Zustand使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-10-10

