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

17,555次阅读
没有评论

共计 6000 个字符,预计需要花费 15 分钟才能阅读完成。

前言

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

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

实现原理

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

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

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

如下图所示:

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

这个时候就需要同时用到绝对定位和相对定位以及 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 (
) } 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 (
{content}
); }; 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 实现多个场景下的鼠标跟随框提示框

    正文完
     0
    Yojack
    版权声明:本篇文章由 Yojack 于2024-10-07发表,共计6000字。
    转载说明:
    1 本网站名称:优杰开发笔记
    2 本站永久网址:https://yojack.cn
    3 本网站的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,请联系站长进行删除处理。
    4 本站一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
    5 本站所有内容均可转载及分享, 但请注明出处
    6 我们始终尊重原创作者的版权,所有文章在发布时,均尽可能注明出处与作者。
    7 站长邮箱:laylwenl@gmail.com
    评论(没有评论)