antd 中 Tooltip 和 Popover 组件传图错位

antd 中 Tooltip 和 Popover 组件传图错位

最近遇到一个需求,当鼠标 hover 一个 icon 的时候展示一张图片,因为项目时基于 antd 进行开发的立马想到了 Popover 组件,写下了如下代码:

<Popover content={<img src="https://wtniu.xyz/res/pic.png" alt="" />}>
  <span>hover me</span>
</Popover>

猛一看没啥毛病,奈何现实无情打脸,真实效果如下(CSDN不支持上传视频,所有效果都用截图替代):
在这里插入图片描述
图片并没有出现在理想的位置,而是偏移到下方,为啥出现这个情况,官网没写,度娘和谷歌也没给答案,留给我的只有一条路,扒源码。

下面是 popover 的源码,关键在 React.createElement,插一句,Babel 其实就是把 JSX 转译成一个名为 React.createElement() 函数调用,对这个方法不明白的可以参考官网解释,分析后可以看到,这货基本上是个马甲,真正的逻辑都交给 Tooltip 去实现了。

image.png
图1 Popover 源码
**
看到这,我意识到那 Tooltip 会不会也有这个问题,于是我写下如下代码:

 <Tooltip title={<img src="https://wtniu.xyz/res/pic.png" alt="" />}>
   <span>hover me</span>
</Tooltip>

果然,结果印证了我的想法。

image.png

这里图片超出是因为 Tooltip 设置了最大宽度的缘故。

那么接下来要做的就是看下 Tooltip 的源码:

image.png
图2 Tooltip 源码
**
看过之后发现 Tooltip 依旧是个马甲,依赖的是 RcTooltip,于是马不停蹄,继续扒 RcTooltip 的源码。

image.png
图3 rc-tooltip 源码
**
没想到还是马甲,意不意外,啥都不说了继续扒。

image.png
** 图4 rc-trigger 源码**
**
这次不是马甲了,作者洋洋洒洒写了近千行的代码,我直接截取其中的一段,这里第一行代码很关键,React.cloneElement 中第一个参数 child 就是 Tooltip组件包裹的内容,也就是例子中的 hover me,newChildProps 就是给这个组价添加的各种属性, 比如点击事件和hover事件等,通过分析找到hover 事件其实触发的是 onMouseEnter

image.png
图5 rc-trigger 源码
**
而这个方法又调用了 delaySetPopupVisible

image.png
** 图6 rc-trigger 源码**
**
delaySetPopupVisible 又调用了 setPopupVisible

image.png
图7 rc-trigger 源码
**
而这个组件的作用是控制组件的显示和隐藏,如果 event 事件存在,将 event 传递进去。

通过 debugger 发现 alignPoint event 都不存在,setPoint 这里不会执行,真正执行的是 onPopupVisibleChange,而这个函数是通过 props 获取,也就是说它是在父组件上实现的

image.png
图8
**
我们回到其父组件 rc-tooltip 中寻找

image.png
图9 rc-tooltip 源码
**
源码显示 onPopupVisibleChange 其实是 onVisibleChange,而 onVisibleChange 也是父组件传递的,所以绕了一大圈又回到了 tooltip 组件,而 tooltip 组件上的 onVisibleChange 其实就是官方文档中的 API,从这里也可以看到,这个 API 通过props 不断向下传递,最终在 rc-trigger 的 setPopupVisible 中执行,但是这里我们没有传入 onVisibleChange 属性,此路不通,所以我们回到 **图4 rc-trigger 源码,**继续分析,这里有两部分组成,一个是我们刚刚分析过的 trigger,还有一份是 portal,那么这个 portal 是什么呢?

image.png
图10 rc-trigger 源码
**
其实就是我们需要显示的部分,源码中的 this.getComponent() 就是我们需要显示的图片,并用 Portal 进行包裹,Portal 源自 rc-util 我们先不管,继续分析 getComponent 方法

image.png
图11 rc-trigger 源码
**
发现逻辑又指向了 Popup,我们继续分析 Popup

image.png
图12 rc-trigger Popup 源码
**
这里有两个 React.createElement,分别对应了两个组件,其中 Align 对应的 是 rc-align,PopupInner 对应的是 PopupInner.js ,我们先分析 PopupInner 源码代码如下:

image.png
图13 rc-trigger PopupInner 源码

这个组价很简单,就是将传入的 children 外层添加一个 div,并给这个 div 添加一些属性,比如 className,visible 等属性,所以关键点还是在 rc-align 里面

image.png
图14 rc-align 源码

这里关键点有两个,一个是 55 行的判断,一个是 64 行的 onAlign 方法,我们一个一个分析,首先是 55 行 判断后有两个方法 alignElement, alignPoint,这两个方法都是通过 dom-align 组件获取的,源码就不贴了,这里说下这两个方法是含义,如果 element 存在,那么依赖传入元素的宽高来设置组件显示的位置,否则依赖触发事件上面的 pageX, pageY 来设置。另外一个 onAlign 则是通过 props 传递进来的

image.png
图15 rc-trigger Popup 源码

这里的 popupDomNode 就是要显示的元素,而 align 是显示的方位,通过 debugger 我们看到,当组件内传入图片的时候包裹图片的元素已经显示,但是图片还没有加载,导致父组件塌陷,宽和高都变成了最小值
image.png
图16 rc-trigger Popup

通过对 dom-align 源码分析,可以看到元素在参与计算的时候使用的是图片未加载之前的宽高和位置,此时宽高是塌陷的,所以经过计算的位置也是错误,dom-align 中其实存在一个修正位置的算法,但奈何不住输入一个错误的值。
image.png
图15 dom-align getRegion 源码

这个组件还要一个诡异的点,就是第二次 hover 的时候显示的位置是正确的,通过 debugger,我们发现第二次显示的位置仍然是错误的,但是此时图片已经显示,和第一次的区别在于宽高塌陷的问题没有了,放开 debugger 后位置自动修改到正确位置。

image.png


通过以上分析,我们可以得出结论了,图片位置显示错误是宽高塌陷所致,那么我们给图片手动设置一个宽高,或者给图片包裹到一个固定宽高的div中再传入到组件中,问题不就可以解决了,抱着这个想法,修改代码如下:

<Popover content={<img style={{ width: 300, height: 223 }} src="https://wtniu.xyz/res/pic.png" alt="" />}>
  <span>hover me</span>
</Popover>

再次刷新后 hover 问题修复。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值