共计 6933 个字符,预计需要花费 18 分钟才能阅读完成。
前言
鼠标追随框的作用如下图所示,能够在前端页面上,为咱们后续的鼠标操作进行提醒阐明,晋升用户的体验。本文将通过多种形式去实现,从而满足不同场景下的需要。
实现原理
实现鼠标追随框的原理很简略,就是监听鼠标在页面上的坐标,而后利用绝对定位(position: relative;
)、相对定位(position: absolute;
)和固定定位(position: fixed;
)等相干常识。
本文是利用的 React,但只有晓得原理,技术栈什么的问题都不大。具体怎么实现,咱接着往下看。
固定定位实现
固定定位的益处是,绝对于浏览器窗口定位,而鼠标追随框的通用场景就是追随鼠标挪动。
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
来返回当事件被触发时鼠标指针绝对于浏览器页面(或客户区)的程度和垂直坐标。
当然,仅这样可能是不够的,咱们会发现在鼠标凑近浏览器页面最右侧的时候,鼠标追随框的局部页面会被暗藏掉。为了可能残缺的展现鼠标追随框中的信息,咱们须要进行一个简略的计算,当 鼠标地位的横坐标 > 鼠标地位横坐标 – 鼠标抉择框的宽度 时,就让 鼠标追随框的横坐标 = 鼠标地位横坐标 – 鼠标抉择框的宽度。
鼠标追随框的具体实现如下:
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;
}
}
相对定位(绝对于整个浏览器窗口)
利用相对定位咱们能够实现和下面固定定位类似的成果,然而有个隐患须要留神,如果鼠标追随框的某个相近的父元素用了绝对定位,那鼠标追随框的理论地位就可能会乱套了。
相对定位不仅要思考可视范畴内的地位,还须要思考浏览器页面滚动的间隔。
具体实现如下:
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;
}
}
相对定位和绝对定位(绝对于鼠标追随框的父元素)
有时候咱们可能并不需要在整个页面进行鼠标追随框的提醒,在某些状况下只须要鼠标在进入页面的局部区域才进行提醒。
如下图所示:
这个时候就须要同时用到相对定位和绝对定位以及 offsetX
和 offsetY
。
offsetX:规定了事件对象与指标节点的内填充边(padding edge)在 X 轴方向上的偏移量
offsetY:规定了事件对象与指标节点的内填充边(padding edge)在 Y 轴方向上的偏移量
具体实现如下:
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;
}
}
最初
本文联合实例,具体的介绍了鼠标追随框在三种场景下的三种具体实现的办法。如果大家有好的实现形式或者好的倡议,欢送提出来一起交换~