React实现多个场景下鼠标跟随提示框详解
目录
- 前言
- 实现原理
- 固定定位实现
- MousePositionDemo
- MousePositionModal
- 绝对定位(相对于整个浏览器窗口)
- MousePositionDemo
- MousePositionModal
- 绝对定位和相对定位(相对于鼠标跟随框的父元素)
- MousePositionDemo
- MousePositionModal2
- 最后
前言
鼠标跟随框的作用如下图所示,可以在前端页面上,为我们后续的鼠标操作进行提示说明,提升用户的体验。本文将通过多种方式去实现,从而满足不同场景下的需求。
实现原理
实现鼠标跟随框的原理很简单,就是监听鼠标在页面上的坐标,然后利用相对定位(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; } }
最后
本文结合实例,详细的介绍了鼠标跟随框在三种场景下的三种具体实现的方法
以上就是React实现多个场景下鼠标跟随提示框详解的详细内容,更多关于React鼠标跟随提示框的资料请关注我们其它相关文章!