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;
  }
}

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

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

如下图所示:

这个时候就需要同时用到绝对定位和相对定位以及 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">
            <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鼠标跟随提示框的资料请关注我们其它相关文章!

(0)

相关推荐

  • react优雅处理多条件鼠标拖拽位移

    本文实例为大家分享了react优雅处理多条件鼠标拖拽位移的具体代码,供大家参考,具体内容如下 场景 三种拖拽条件 可纵轴 横轴 和全部方向 如果加3个监听重复代码太多因为状态更改组件会重新渲染 所以写的时候要多注意避免有大量代码的函数多次创建销毁 state const [position, setPosition] = useState(axisPosition); jsx <Container       style={{         top: position.top + "px

  • React鼠标多选功能的配置方法

    一般列表都有选择功能,单选复选多选都很常见.在自定义循环的列表,图像中,实现鼠标单选,多选,反选功能. # React mousemultiples # React 鼠标多选组件 React 鼠标多选组件 局限性 > 主要实现鼠标多选的效果, 在不破坏原有的列表情况下,嵌入组件拥有鼠标多选功能. npm包地址 [链接](https://www.npmjs.com/package/mousemultiples) 安装 npm i mousemultiples 使用配置项 /**  * wrappe

  • JS中使用react-tooltip插件实现鼠标悬浮显示框

    前段时间遇到的一个需求,要求鼠标悬停显示使用描述, 用到了react-tooltip插件,今天写一个总结 先看效果(为了方便参考,用的是原始样式): 文档参考地址: https://www.npmjs.com/package/react-tooltip 1, 首先在配置文件加上引用 2, 然后在页面内引入: 3, react-tooltip的使用非常方便, 在需要鼠标悬停显示的标签上加上data-tip和data-html属性 比如我希望鼠标悬停在某个图标时展示提示框,代码如下: data-ti

  • React实现多个场景下鼠标跟随提示框详解

    目录 前言 实现原理 固定定位实现 MousePositionDemo MousePositionModal 绝对定位(相对于整个浏览器窗口) MousePositionDemo MousePositionModal 绝对定位和相对定位(相对于鼠标跟随框的父元素) MousePositionDemo MousePositionModal2 最后 前言 鼠标跟随框的作用如下图所示,可以在前端页面上,为我们后续的鼠标操作进行提示说明,提升用户的体验.本文将通过多种方式去实现,从而满足不同场景下的需求

  • jQuery实现鼠标跟随提示层效果代码(可显示文本,Div,Table,Html等)

    本文实例讲述了jQuery实现鼠标跟随提示层效果代码.分享给大家供大家参考,具体如下: 运行效果截图如下: Web网站有不少需要用到tip提示层的地方,结合jquery的jquery.cluetip.js ,可以实现本地字符或ajax异步调用显示提示层.如以上购评分明细提示.(优势:宽度需要调用页加载时定义,高度auto , 且三角指定图标能根据显示层的大小位置来调用它的位置,达到醒目直观的效果) jquery.cluetip.css /* global */ #cluetip-close im

  • Python+Selenium键盘鼠标模拟事件操作详解

    目录 元素的基本操作 鼠标键盘模拟事件操作 利用 Keys 模块模拟键盘操作事件 利用 Action 类模拟鼠标操作事件 当我们定位到具体的一个元素的时候就可以对这个元素进行具体的操作,比如之前章节所执行的 click 操作.这是最简单的操作,webdriver 还有其他的操作.比如元素的基本操作(点击.输入.清除),还有一些高级操作如鼠标键盘模拟事件.弹出框处理.多页面切换等… 这些都是需要我们了解的内容,也是在做自动化测试的时候经常遇到的一些基本场景.今天这一章节,我们就先来学习一下元素的基

  • React源码分析之useCallback与useMemo及useContext详解

    目录 热身准备 初始化mount mountCallback 更新 update 使用场景 总结 热身准备 createContext Provider Consumer useContext 初始化mount&更新update 总结 热身准备 useCallback和useMemo是一样的东西,只是入参有所不同. useCallback缓存的是回调函数,如果依赖项没有更新,就会使用缓存的回调函数: useMemo缓存的是回调函数的return,如果依赖项没有更新,就会使用缓存的return:

  • Windows下MySQL安装教程图文详解

    MySQL安装说明MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,目前属于Oracle旗下产品. MySQL 是最流行的关系型数据库管理系统之一,在 WEB 应用方面,MySQL是最好的RDBMS(Relational Database Management System,关系数据库管理系统) 应用软件. MySQL是一种关系数据库管理系统,关系数据库将数据保存在不同的表中,而不是将所有数据放在一个大仓库内,这样就增加了速度并提高了灵活性. MySQL所使用的 SQL 语

  • Android Studio下的APP目录结构详解

    Project Name:工程项目名称 Application Name:当前应用发布以后的名字,例如QQ图标下面的名字是"QQ",就是Application Name. Android Studio工程目录 1..gradle和.idea 这两个目录下放置的都是Android Studio自动生成的一些文件,我们无须关心,也不要去手动编辑. 2.app 项目中的代码.资源等内容几乎都是放置在这个目录下的,我们后面的开发工作也基本都是在这个目录下进行的,待会儿还会对这个目录单独展开进行

  • React如何利用Antd的Form组件实现表单功能详解

    一.构造组件 1.表单一定会包含表单域,表单域可以是输入控件,标准表单域,标签,下拉菜单,文本域等. 这里先引用了封装的表单域 <Form.Item /> 2.使用Form.create处理后的表单具有自动收集数据并校验的功能,但如果不需要这个功能,或者默认的行为无法满足业务需求,可以选择不使用Form.create并自行处理数据 经过Form.create()包装过的组件会自带this.props.form属性,this.props.form提供了很多API来处理数据,如getFieldDe

  • 使用 React 和 Threejs 创建一个VR全景项目的过程详解

    最近我在学习使用 React 配合 Three.js 来搭建一个可以浏览720全景图片的项目 实现的是加载一张 2:1 的720全景 分享一下我的创建过程 一.搭建框架并安装需要的插件 npx create-react-app parano // 创建一个 React 项目 npm install -S typescript // 安装 typescript,这个是类型辅助插件,与全景项目关系不大 npm install -S @types/three // 安装 typescript 支持的

  • React实现控制减少useContext导致非必要的渲染详解

    目录 前言 1.拆分context 2.使用useMemo包裹函数 前言 在我们使用useContext来进行数据流管理时,每当context更新时,所有使用到该context的组件都会重新渲染.如果我们的context的数据是由多个部分组成的,但只有其中一两个字段会频繁更新,但其他的数据都比较稳定时,这时,即使组件值使用到了比较稳定的那部分数据,但它依然会频繁渲染,这就很容易会导致性能问题.我们一般会使用拆分context或者结合useMemo来减少组件渲染的次数: 1.拆分context 我

  • vant/vue实现小程序下拉刷新功能方法详解

    首先先把van-list控件放入列表项中 <van-list v-model="loading" :finished="finished" finished-text="没有更多了" @load="onLoad" > <list-nav :list="list"></list-nav> </van-list> data(){ return{ list: [],

随机推荐