当前位置: 首页 > news >正文

利用React实现多个场景下的鼠标跟随框提示框

前言

鼠标跟随框的作用如下图所示,可以在前端页面上,为我们后续的鼠标操作进行提示说明,提升用户的体验。本文将通过多种方式去实现,从而满足不同场景下的需求。

鼠标跟随框1.gif

实现原理

实现鼠标跟随框的原理很简单,就是监听鼠标在页面上的坐标,然后利用相对定位(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>{/* 鼠标跟随框 */}<MousePositionModalvisible={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 (<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;

这里有两个地点需要注意:一是给鼠标跟随框设置固定定位,二是要将 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 (<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;}
}

绝对定位和相对定位(相对于鼠标跟随框的父元素)

有时候我们可能并不需要在整个页面进行鼠标跟随框的提示,在某些情况下只需要鼠标在进入页面的部分区域才进行提示。

如下图所示:

鼠标跟随框2.gif

这个时候就需要同时用到绝对定位和相对定位以及 offsetXoffsetY

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"><MousePositionModal2visible={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 (<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 设置为 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;}
}

最后

本文结合实例,详细的介绍了鼠标跟随框在三种场景下的三种具体实现的方法。如果大家有好的实现方式或者好的建议,欢迎提出来一起交流~

http://www.lryc.cn/news/13325.html

相关文章:

  • 【安全知识】——如何绕过cdn获取真实ip
  • JavaScript内存泄露和垃圾回收机制
  • Kubernetes02:知识图谱
  • nginx-服务器banner泄漏风险
  • GCC 同名符号冲突解决办法
  • 下一代视频编码技术2023
  • 最新最全中小微企业研究数据:海量创业公司信息与获取投资信息(1985-2021年)
  • springboot数据源浅析
  • 2022黑马Redis跟学笔记.实战篇(七)
  • QT mp3音乐播放器实现框架,Qt鼠标事件,网络编程,QSqlite,Json解析,HTTP请求等
  • 硬件学习 软件Cadence day04 PCB 封装绘制
  • 【Java】yield()和join()区别
  • 【MySQL】Java连接MySQL数据库(封装版只需会MySQL)
  • 【java基础】运算符
  • 带噪学习-概述
  • Scratch少儿编程案例-多彩打地鼠
  • 为什么拔掉计算机网线还能ping通127.0.0.1?
  • Android kotlin 内、外部存储根目录及测试(可以实现仿微信未读消息数提示数字)
  • Android 7.0 OTA升级(高通)
  • 工作负载之DeployMent
  • 淘宝tmall页面数据获取,API接口对接程序
  • 基于粒子群优化算法的电动汽车充放电V2G研究(Matlab代码实现)
  • java并发编程原理2 (AQS, ReentrantLock,线程池)
  • 研报精选230219
  • 【PPPoE】PPPoE拨号流程
  • django项目实战(django+bootstrap实现增删改查)
  • Lesson4---Python语言基础(2)
  • NCHW - NHWC - CHWN 排列
  • 2019蓝桥杯真题矩阵切割(填空题) C语言/C++
  • Java线程池的创建以及原理