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

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

瀏覽器在繪制時,總是以左上角的頂點作為元素的起始位置,也就是說如果你的選區(qū)是從右往左或者從下往上建立,瀏覽器都會以最終圖形的左上角頂點作為元素的起始位置進行繪制。所以確定左上角頂點位置尤為關鍵。
React計算選區(qū)范圍
通過監(jiān)聽瀏覽器的鼠標移動事件來獲取鼠標移動的范圍,在這里我們需要先獲取鼠標初始位置,通過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事件記錄鼠標移動坐標點,如果鼠標未落下則不觸發(fā)移動事件,避免重復渲染,最后在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均小于鼠標移動后坐標,則選區(qū)在第四象限。以起始位置為坐標原點去計算。代碼如下所示:
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;
}
};計算出鼠標最終位置處于哪個象限后,我們就可以計算出選區(qū)的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]);最后我們計算選區(qū)的寬度和高度,通過計算落點位置與鼠標移動后最終位置的差值可獲取寬高。代碼如下:
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);
}到此這篇關于React實現(xiàn)前端選區(qū)的示例代碼的文章就介紹到這了,更多相關React 前端選區(qū)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
react-router-dom 嵌套路由的實現(xiàn)
這篇文章主要介紹了react-router-dom 嵌套路由的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-05-05
React動畫實現(xiàn)方案Framer Motion讓頁面自己動起來
這篇文章主要為大家介紹了React動畫實現(xiàn)方案Framer Motion讓頁面自己動起來,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-10-10
react中使用echarts,并實現(xiàn)tooltip循環(huán)輪播方式
這篇文章主要介紹了react中使用echarts,并實現(xiàn)tooltip循環(huán)輪播方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-01-01
react-native ListView下拉刷新上拉加載實現(xiàn)代碼
本篇文章主要介紹了react-native ListView下拉刷新上拉加載實現(xiàn),具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-08-08

