山東公路建設(shè)集團(tuán)網(wǎng)站seo sem
前言
鼠標(biāo)跟隨框的作用如下圖所示,可以在前端頁面上,為我們后續(xù)的鼠標(biāo)操作進(jìn)行提示說明,提升用戶的體驗(yàn)。本文將通過多種方式去實(shí)現(xiàn),從而滿足不同場景下的需求。
實(shí)現(xiàn)原理
實(shí)現(xiàn)鼠標(biāo)跟隨框的原理很簡單,就是監(jiān)聽鼠標(biāo)在頁面上的坐標(biāo),然后利用相對定位(position: relative;
)、絕對定位(position: absolute;
)和固定定位(position: fixed;
)等相關(guān)知識。
本文是利用的 React,但只要知道原理,技術(shù)棧什么的問題都不大。具體怎么實(shí)現(xiàn),咱接著往下看。
固定定位實(shí)現(xiàn)
固定定位的好處是,相對于瀏覽器窗口定位,而鼠標(biāo)跟隨框的通用場景就是跟隨鼠標(biāo)移動(dòng)。
MousePositionDemo
我們先寫一個(gè)頁面,用來引入鼠標(biāo)跟隨框:
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)}}>點(diǎn)擊顯示</Button><Button onClick={() => {setVisible(false)}}>點(diǎn)擊關(guān)閉</Button>{/* 鼠標(biāo)跟隨框 */}<MousePositionModalvisible={visible}content="鼠標(biāo)跟隨"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
來返回當(dāng)事件被觸發(fā)時(shí)鼠標(biāo)指針相對于瀏覽器頁面(或客戶區(qū))的水平和垂直坐標(biāo)。
當(dāng)然,僅這樣可能是不夠的,我們會(huì)發(fā)現(xiàn)在鼠標(biāo)靠近瀏覽器頁面最右側(cè)的時(shí)候,鼠標(biāo)跟隨框的部分頁面會(huì)被隱藏掉。為了能夠完整的展示鼠標(biāo)跟隨框中的信息,我們需要進(jìn)行一個(gè)簡單的計(jì)算,當(dāng) 鼠標(biāo)位置的橫坐標(biāo) > 鼠標(biāo)位置橫坐標(biāo) - 鼠標(biāo)選擇框的寬度 時(shí),就讓 鼠標(biāo)跟隨框的橫坐標(biāo) = 鼠標(biāo)位置橫坐標(biāo) - 鼠標(biāo)選擇框的寬度。
鼠標(biāo)跟隨框的具體實(shí)現(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 (<divid="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;
這里有兩個(gè)地點(diǎn)需要注意:一是給鼠標(biāo)跟隨框設(shè)置固定定位,二是要將 z-index
的值設(shè)置的足夠大,不然有可能會(huì)被頁面上的其他元素遮住。
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;}
}
絕對定位(相對于整個(gè)瀏覽器窗口)
利用絕對定位我們可以實(shí)現(xiàn)和上面固定定位相似的效果,但是有個(gè)隱患需要注意,如果鼠標(biāo)跟隨框的某個(gè)相近的父元素用了相對定位,那鼠標(biāo)跟隨框的實(shí)際位置就可能會(huì)亂套了。
絕對定位不僅要考慮可視范圍內(nèi)的位置,還需要考慮瀏覽器頁面滾動(dòng)的距離。
具體實(shí)現(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 (<divid="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;}
}
絕對定位和相對定位(相對于鼠標(biāo)跟隨框的父元素)
有時(shí)候我們可能并不需要在整個(gè)頁面進(jìn)行鼠標(biāo)跟隨框的提示,在某些情況下只需要鼠標(biāo)在進(jìn)入頁面的部分區(qū)域才進(jìn)行提示。
如下圖所示:
這個(gè)時(shí)候就需要同時(shí)用到絕對定位和相對定位以及 offsetX
和 offsetY
。
offsetX: 規(guī)定了事件對象與目標(biāo)節(jié)點(diǎn)的內(nèi)填充邊(padding edge)在 X 軸方向上的偏移量
offsetY: 規(guī)定了事件對象與目標(biāo)節(jié)點(diǎn)的內(nèi)填充邊(padding edge)在 Y 軸方向上的偏移量
具體實(shí)現(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"><MousePositionModal2visible={visible}content="鼠標(biāo)跟隨"defaultPosition={defaultPosition}/></div>)
}export default MousePositionDemo;
注意要將這里 position
設(shè)置為 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 (<divid="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 設(shè)置為 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;}
}
最后
本文結(jié)合實(shí)例,詳細(xì)的介紹了鼠標(biāo)跟隨框在三種場景下的三種具體實(shí)現(xiàn)的方法。如果大家有好的實(shí)現(xiàn)方式或者好的建議,歡迎提出來一起交流~