react 自定义组件 鼠标移入显示/隐藏 组件思路

刚出来时候 做这种类似的效果,都是 通过 css hover 效果,把组件 包在父级中,通过父级的 hover 事件,来控制 里面子级 的显示与隐藏。

这样使用,一: 如果是列表里面做这个功能,就会出现很多这种组件,明明 一个就可以做这件事,就很浪费性能

     二: 组件嵌套在业务代码里面,不好管理 与复用,维护成本高。

添加在body 上这种方式没自己写过,后面一直都是 使用 antd 或者其他组件库,现在写了,记录下思路

这个效果 需要注意 几点

1. 就是 组件显示的位置。

2. 显示隐藏之间如何过渡,

先辨别下 鼠标移入/移出事件

mouseenter 鼠标移入目标触发

mouseleave 鼠标移出目标触发

mouseover 鼠标移入目标触发,其子元素也会触发

mouseout 鼠标移出目标触发,其子元素也会触发

所以应该用 mouseenter 和 mouseleave, 来处理事件,

1. 通过 mouseenter 拿到 event 对象,来获取 元素自身宽高 和 元素距离 文档顶部的距离,和左边的距离, 来定位元素显示的位置。特定的情况还可以判断 距离底部位置,从而适当上移显示组件,或者监听 窗口变化 resize 事件来 实时变化显示组件的位置,不过鼠标移入移出显示应该不用

// 设置组件显示的位置
    setPosition = () => {
        const { event } = this.props;
        let $dom = event.currentTarget;
        let top = $dom.offsetTop + $dom.clientHeight + 5;
        let left = $dom.offsetLeft;
        this.setState({
            top,
            left
        })
    }

2. 就是 显示与隐藏了,

  (1)在通过鼠标移入/移出控制,那么这个按钮可能有个 hover 效果,但是 这里不能用 hover ,因为组件是直接添加到body 根部, 鼠标进入到组件,就没效果了,这里应该用事件判断来控制效果。

  (2)在鼠标移入按钮 可以展示组件,那么 离开按钮之后呢,同上,鼠标在从组件回到按钮上 ,组件怎么显示的问题。

在 鼠标移出按钮的时候 加一个 定时事件,setTimeout 设置时间 0.2s,在日历组件的移入事件中加一个 clearTimeout, 在时间到之前 进入日历组件,那么组件依旧展示,否则,组件关闭,

在鼠标从组件回到按钮 也是一样,设置一个定时器,在 按钮的移入事件中加一个 判断 是否有定时器,有的话则表示是从组件回来的,可进行清空,如果判断没有,则就重新开始,可直接显示组件,并且要在相对的时机,置空定时器的变量(时长可根据实际情况设置,鼠标移动还是很快的)

import React from 'react';
import moment from 'moment';
import Calendar from "./calendar";
import './index.less';

type P = {

}
let timer: any = null;
class Index extends React.Component<P, any> {
    str = moment().format('YYYY-MM');
    constructor(props: P) {
        super(props);
        this.state = {
            flg: false, // 控制 按钮的 hover 效果
        }
    }
    componentDidMount() {
    }
    // 日历显示
    show = (e: any) => {
        this.setState({
            flg: true
        })
        let data = {
            text: this.str,
            // 这里是 给日历组件传了2个回调事件,移出/移入
            mouseenter: this.calendarMouseEnter,
            mouseleave: this.calendarMouseLeave,
            event: e
        }
        Calendar.open(data);
    }
    // 日历隐藏
    hide = () => {
        timer = setTimeout(() => {
            this.setState({
                flg: false
            })
            Calendar.remove('');
            timer = null;
        }, 200);
    }
    // 按钮鼠标移入
    mouseenter = (e: any) => {
        e.stopPropagation();
        if (timer) {
            clearTimeout(timer);
            timer = null;
        } else {
            this.show(e);
        }
    }
    // 按钮鼠标移出
    mouseleave = (e: any) => {
        e.stopPropagation();
        const { flg } = this.state
        if (flg) {
            this.hide();
        }
    }
    // 日历鼠标移入
    calendarMouseEnter = (e: any) => {
        e.stopPropagation();
        clearTimeout(timer);
        timer = null;
    }
    // 日历鼠标移出
    calendarMouseLeave = (e: any) => {
        e.stopPropagation();
        this.hide();
    }
    render() {
        const { flg } = this.state;
        return (
            <div className="oper-calendar">
                <span className={flg ? "title hover" : "title"} onMouseEnter={this.mouseenter} onMouseLeave={this.mouseleave}>{this.str}</span>
            </div>
        )
    }
}

export default Index;