React實現(xiàn)多個場景下鼠標跟隨提示框詳解
前言
鼠標跟隨框的作用如下圖所示,可以在前端頁面上,為我們后續(xù)的鼠標操作進行提示說明,提升用戶的體驗。本文將通過多種方式去實現(xiàn),從而滿足不同場景下的需求。

實現(xiàn)原理
實現(xiàn)鼠標跟隨框的原理很簡單,就是監(jiān)聽鼠標在頁面上的坐標,然后利用相對定位(position: relative;)、絕對定位(position: absolute;)和固定定位(position: fixed;)等相關知識。
本文是利用的 React,但只要知道原理,技術棧什么的問題都不大。具體怎么實現(xiàn),咱接著往下看。
固定定位實現(xiàn)
固定定位的好處是,相對于瀏覽器窗口定位,而鼠標跟隨框的通用場景就是跟隨鼠標移動。
MousePositionDemo
我們先寫一個頁面,用來引入鼠標跟隨框:
index.tsx
import React, { useEffect, useState } from 'react';
import './index.less';
import { Button } from 'antd';
import MousePositionModal from './MousePositionModal';
const MousePositionDemo = () => {
const [visible, setVisible] = useState(false);
return (
<div id="mouse-position-demo" className="mouse-position-demo">
<Button onClick={() => {
setVisible(true)
}}>點擊顯示</Button>
<Button onClick={() => {
setVisible(false)
}}>點擊關閉</Button>
{/* 鼠標跟隨框 */}
<MousePositionModal
visible={visible}
content="鼠標跟隨"
defaultPosition={{
x: 32,
y: 32
}}
/>
</div>
)
}
export default MousePositionDemo;
index.less
.mouse-position-demo {
margin: 0 auto;
height: 500px;
width: 500px;
background-color: #fff;
padding: 24px 24px;
}
MousePositionModal
這里我們首先通過 clientX, clientY 來返回當事件被觸發(fā)時鼠標指針相對于瀏覽器頁面(或客戶區(qū))的水平和垂直坐標。
當然,僅這樣可能是不夠的,我們會發(fā)現(xiàn)在鼠標靠近瀏覽器頁面最右側的時候,鼠標跟隨框的部分頁面會被隱藏掉。為了能夠完整的展示鼠標跟隨框中的信息,我們需要進行一個簡單的計算,當 鼠標位置的橫坐標 > 鼠標位置橫坐標 - 鼠標選擇框的寬度 時,就讓 鼠標跟隨框的橫坐標 = 鼠標位置橫坐標 - 鼠標選擇框的寬度。
鼠標跟隨框的具體實現(xiàn)如下:
index.tsx
import React, { useState, useEffect } from 'react';
import './index.less';
interface IMousePositionModal {
visible: boolean;
content: string;
defaultPosition: {
x: number,
y: number
}
}
const MousePositionModal = (props: IMousePositionModal) => {
const { visible, content, defaultPosition } = props;
const [left, setLeft] = useState(defaultPosition.x);
const [top, setTop] = useState(defaultPosition.y);
useEffect(() => {
if (visible) {
show();
}
}, [visible]);
const show = () => {
const modal = document.getElementById('mouse-position-modal');
if (modal) {
document.onmousemove = (event) => {
const { clientX, clientY } = event || window.event;
const clientWidth = document.body.clientWidth || document.documentElement.clientWidth;
const { offsetWidth } = modal;
let x = clientX + 18;
const y = clientY + 18;
if (x >= clientWidth - offsetWidth) {
x = clientWidth - offsetWidth;
}
setLeft(x);
setTop(y);
};
}
};
return (
<div
id="mouse-position-modal"
className="mouse-position-modal"
style={{ left: `${left}px`, top: `${top}px`, visibility: `${visible ? 'visible' : 'hidden'}`}}
>
<div className="mouse-position-modal-content">{content}</div>
</div>
);
};
export default MousePositionModal;
這里有兩個地點需要注意:一是給鼠標跟隨框設置固定定位,二是要將 z-index 的值設置的足夠大,不然有可能會被頁面上的其他元素遮住。
index.less
.mouse-position-modal {
min-width: 240px;
height: 57px;
background: #fff;
box-shadow: 0 4px 12px 0 rgba(0, 0, 0, 0.15);
border-radius: 4px;
position: fixed;
z-index: 2000;
padding: 8px 12px;
.mouse-position-modal-content {
font-size: 16px;
color: #262626;
}
}
絕對定位(相對于整個瀏覽器窗口)
利用絕對定位我們可以實現(xiàn)和上面固定定位相似的效果,但是有個隱患需要注意,如果鼠標跟隨框的某個相近的父元素用了相對定位,那鼠標跟隨框的實際位置就可能會亂套了。
絕對定位不僅要考慮可視范圍內的位置,還需要考慮瀏覽器頁面滾動的距離。
具體實現(xiàn)如下:
MousePositionDemo
和固定定位一樣
MousePositionModal
index.tsx
import React, { useState, useEffect } from 'react';
import './index.less';
interface IMousePositionModal {
visible: boolean;
content: string;
defaultPosition: {
x: number,
y: number
}
}
const MousePositionModal = (props: IMousePositionModal) => {
const { visible, content, defaultPosition } = props;
const [left, setLeft] = useState(defaultPosition.x);
const [top, setTop] = useState(defaultPosition.y);
useEffect(() => {
if (visible) {
show();
}
}, [visible]);
const show = () => {
const modal = document.getElementById('mouse-position-modal');
if (modal) {
document.onmousemove = (event) => {
const { clientX, clientY, pageX, pageY } = event || window.event;
const sl = document.body.scrollLeft || document.documentElement.scrollLeft;
const st = document.body.scrollTop || document.documentElement.scrollTop;
const clientWidth = document.body.clientWidth || document.documentElement.clientWidth;
const { offsetWidth } = modal;
let x = (pageX || clientX + sl) + 18;
const y = (pageY || clientY + st) + 18;
if (x >= clientWidth - offsetWidth) {
x = clientWidth - offsetWidth;
}
setLeft(x);
setTop(y);
};
}
};
return (
<div
id="mouse-position-modal"
className="mouse-position-modal"
style={{ left: `${left}px`, top: `${top}px`, visibility: `${visible ? 'visible' : 'hidden'}`}}
>
<div className="mouse-position-modal-content">{content}</div>
</div>
);
};
export default MousePositionModal;
index.less
.mouse-position-modal {
min-width: 240px;
height: 57px;
background: #fff;
box-shadow: 0 4px 12px 0 rgba(0, 0, 0, 0.15);
border-radius: 4px;
position: absolute;
z-index: 2000;
padding: 8px 12px;
.mouse-position-modal-content {
font-size: 16px;
color: #262626;
}
}
絕對定位和相對定位(相對于鼠標跟隨框的父元素)
有時候我們可能并不需要在整個頁面進行鼠標跟隨框的提示,在某些情況下只需要鼠標在進入頁面的部分區(qū)域才進行提示。
如下圖所示:

這個時候就需要同時用到絕對定位和相對定位以及 offsetX 和 offsetY。
offsetX: 規(guī)定了事件對象與目標節(jié)點的內填充邊(padding edge)在 X 軸方向上的偏移量 offsetY: 規(guī)定了事件對象與目標節(jié)點的內填充邊(padding edge)在 Y 軸方向上的偏移量
具體實現(xiàn)如下:
MousePositionDemo
index.tsx
import React, { useEffect, useState } from 'react';
import './index.less';
import { Button } from 'antd';
import MousePositionModal2 from './MousePositionModal2';
// 兼容offsetX
const getOffsetX = (e: any) =>{
const event = e || window.event;
const srcObj = e.target || e.srcElement;
if (event.offsetX){
return event.offsetX;
}else{
const rect = srcObj.getBoundingClientRect();
const clientx = event.clientX;
return clientx - rect.left;
}
}
// 兼容offsetY
const getOffsetY = (e: any) => {
const event = e || window.event;
const srcObj = e.target || e.srcElement;
if (event.offsetY){
return event.offsetY;
}else{
const rect = srcObj.getBoundingClientRect();
const clientx = event.clientY;
return clientx - rect.top;
}
}
const MousePositionDemo = () => {
const [visible, setVisible] = useState(false);
const [defaultPosition, setDefaultPosition] = useState({
x: 32,
y: 32
})
useEffect(() => {
const ele = document.getElementById('mouse-position-demo') as HTMLElement;
ele.addEventListener('mouseenter', show)
ele.addEventListener('mousemove', mouseMove)
ele.addEventListener('mouseleave', hide)
return () => {
ele.removeEventListener('mouseenter', show)
ele.removeEventListener('mousemove', mouseMove)
ele.removeEventListener('mouseleave', hide)
}
}, [])
const show = () => {
setVisible(true)
}
const hide = () => {
setVisible(false)
}
const mouseMove = (e: MouseEvent) => {
let x = getOffsetX(e) + 18;
const y = getOffsetY(e) + 18;
setDefaultPosition({ x, y });
}
return (
<div id="mouse-position-demo" className="mouse-position-demo">
<MousePositionModal2
visible={visible}
content="鼠標跟隨"
defaultPosition={defaultPosition}
/>
</div>
)
}
export default MousePositionDemo;
注意要將這里 position 設置為 relative 。
index.less
.mouse-position-demo {
margin: 0 auto;
height: 500px;
width: 500px;
background-color: #fff;
padding: 24px 24px;
position: relative;
}
MousePositionModal2
index.tsx
import React, { useState, useEffect } from 'react';
import './index.less';
interface IMousePositionModal {
visible: boolean;
content: string;
defaultPosition: {
x: number,
y: number
}
}
const MousePositionModal2 = (props: IMousePositionModal) => {
const { visible, content, defaultPosition } = props;
const { x, y } = defaultPosition;
return (
<div
id="mouse-position-modal"
className="mouse-position-modal"
style={{ left: `${x}px`, top: `${y}px`, visibility: `${visible ? 'visible' : 'hidden'}` }}
>
<div className="mouse-position-modal-content">{content}</div>
</div>
);
};
export default MousePositionModal2;
注意要將這里 position 設置為 absolute 。
index.less
.mouse-position-modal {
min-width: 240px;
height: 57px;
background: #fff;
box-shadow: 0 4px 12px 0 rgba(0, 0, 0, 0.15);
border-radius: 4px;
position: absolute;
z-index: 2000;
padding: 8px 12px;
.mouse-position-modal-content {
font-size: 16px;
color: #262626;
}
}
最后
本文結合實例,詳細的介紹了鼠標跟隨框在三種場景下的三種具體實現(xiàn)的方法
以上就是React實現(xiàn)多個場景下鼠標跟隨提示框詳解的詳細內容,更多關于React鼠標跟隨提示框的資料請關注腳本之家其它相關文章!
相關文章
react hook使用useState更新數(shù)組,無法更新問題及解決
這篇文章主要介紹了react hook使用useState更新數(shù)組,無法更新問題及解決,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-03-03

