刚出来时候 做这种类似的效果,都是 通过 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;