本文为原创, 遵循 CC 4.0 BY-SA 版权协议, 转载需注明出处: https://blog.csdn.net/big_cheng/article/details/130262213.
https://web.dev/building-a-tooltip-component/ 介绍了一个tooltip 的实现. 本文将它简化并分析原理. 效果对比图如下:
鼠标hover 到父容器时, tooltip 浮现.
tooltip 实现为子元素:
a<BR>b<BR>c<BR>
preceding
<div id="ct" style="display: inline-block; border: 1px solid;">
div content div content div content
<tool-tip>this is tip contenttool-tip>
div>
浏览器实际将这个不认识的"tool-tip" 元素视为"div".
tooltip 绝对定位, 停靠在父容器上方:
tool-tip {
--_triangle-size: 7px;
position: absolute;
bottom: calc(100% + 0.75ch + var(--_triangle-size));
......
为实现水平居中, 首先将左边缘居中, 再往左偏移自身宽度的一半:
left: 50%;
transform: translateX(-50%) translateY(3px);
通过修改"opacity" 来显示tooltip, 并且微调y 值实现微微地’跳动’:
tool-tip {
......
opacity: .2;
transform: translateX(-50%) translateY(3px);
transition: opacity .2s ease, transform .2s ease;
......
}
#ct {
position: relative;
}
#ct:hover > tool-tip {
opacity: 1;
transform: translateX(-50%) translateY(0);
transition-delay: 200ms;
}
“translateY” 正值是向下.
注: 为了方便调试, 初始"opacity" 改为了".2".
tool-tip {
......
width: max-content;
max-width: 25ch;
text-align: center;
按内容来定宽. 调试可发现屏宽压缩到tool-tip ‘内部’ 时, body 出现HScroll.
tool-tip 没有边框线 - 通过filter 来实现类似效果:
will-change: filter;
filter:
drop-shadow(0 3px 3px hsl(0 0% 0% / 15%))
drop-shadow(0 12px 12px hsl(0 0% 0% / 15%));
}
用::after 来实现tooltip 下边缘的尖角.
::after 元素使用绝对定位, 占满tool-tip 的区域:
tool-tip::after {
......
background: white;
position: absolute;
z-index: -1;
top: 0; right: 0; left: 0;
bottom: -7px;
border-bottom: 7px solid transparent;
......
底部多延伸7px - 用于尖角.
调试:
a) 禁掉tool-tip 的"opacity|filter", 增加"border: 1px solid"
b) 修改::after: “background: red;”
可见::after 是在tool-tip (作为父容器) 的范围内, 并下延.
例如横线
<hr>
<style>
#ct2 {width: 50px; height: 50px;
background: white conic-gradient(
from -30deg at bottom, #0000, #000 1deg 60deg, #0000 61deg);
......
}
style>
<div id="ct2">div>
a) 从原点竖直朝上为0度, -30度是再朝左偏(逆时针)
b) 原点"bottom" 即"bottom center" - 在所修饰容器的底部的中点
c) “#0000”: 左偏30度的这条线为黑色, 全透明(第4个零)
d) “#000 1deg 60deg”: 紧接着前一条线往右(顺时针)偏1到60度的范围为黑色(前一条透明线起消除锯齿的作用)
e) “#0000 61deg”: 最后再右偏1度黑色透明, 仍是消除锯齿
f) 360度范围内剩余的部分未定义 - 显示conic-gradient 前面定义的背景色(white)
将锥形渐变应用到::after 元素的下侧:
tool-tip::after {
......
-webkit-mask: conic-gradient(from -30deg at bottom, #0000, #000 1deg 60deg, #0000 61deg) bottom / 100% 50% no-repeat;
mask: conic-gradient(from -30deg at bottom, #0000, #000 1deg 60deg, #0000 61deg) bottom / 100% 50% no-repeat;
a) “bottom”: 锥形渐变的mask 图的起始位置 是mask 区域的下方的中点. mask 区域 默认 是所修饰容器的border-box.
b) “100% 50%”: mask 图的大小. 这里是mask 区域横向的全部、纵向的一半.
c) “no-repeat”: 不重复, 即mask 图不会横向纵向绘制多张.
注: Chrome 浏览器使用"-webkit-mask" 而非"mask".
调试:
a) 修改::after: “background: red;”
可见mask 图从::after 元素底部的中点开始绘制.
因为::after “z-index” 为-1, ::after 的内容显示在tool-tip 内容的下方(不会’切掉’ 文字).
调试:
a) tool-tip: 禁掉"opacity", 增加"border: 1px solid"
b) ::after 修改: “background: red; border-bottom: 7px solid blue;”
::after 禁掉和启用"-webkit-mask" 的效果对比图:
::after 的颜色仅在锥形渐变的黑色区域范围内被保留(tool-tip border-bottom 重叠的部分也被’擦掉’ 了).
tool-tip “filter” 在尖角及两侧底边造成阴影效果. 如果禁掉"filter", 则尖角看不到, 仅底边中间有个小缺口.
DOCTYPE html>
<html>
<body>
<style>
tool-tip {
--_triangle-size: 7px;
pointer-events: none;
user-select: none;
position: absolute;
left: 50%;
bottom: calc(100% + 0.75ch + var(--_triangle-size));
z-index: 1;
opacity: .2;
transform: translateX(-50%) translateY(3px);
transition: opacity .2s ease, transform .2s ease;
width: max-content;
max-width: 25ch;
text-align: center;
padding: 0.75ch 1.5ch;
margin: 0;
border-radius: 5px;
background: white;
color: CanvasText;
will-change: filter;
filter:
drop-shadow(0 3px 3px hsl(0 0% 0% / 15%))
drop-shadow(0 12px 12px hsl(0 0% 0% / 15%));
}
tool-tip::after {
content: "";
background: white;
position: absolute;
z-index: -1;
top: 0; right: 0; left: 0;
-webkit-mask: conic-gradient(from -30deg at bottom, #0000, #000 1deg 60deg, #0000 61deg) bottom / 100% 50% no-repeat;
mask: conic-gradient(from -30deg at bottom, #0000, #000 1deg 60deg, #0000 61deg) bottom / 100% 50% no-repeat;
bottom: -7px;
border-bottom: 7px solid transparent;
}
#ct {
position: relative;
}
#ct:hover > tool-tip {
opacity: 1;
transform: translateX(-50%) translateY(0);
transition-delay: 200ms;
}
style>
a<BR>b<BR>c<BR>
preceding
<div id="ct" style="display: inline-block; border: 1px solid;">
div content div content div content
<tool-tip>this is tip contenttool-tip>
div>
<hr>
<style>
#ct2 {width: 50px; height: 50px;
background: white conic-gradient(
from -30deg at bottom, #0000, #000 1deg 60deg, #0000 61deg);
filter:
drop-shadow(0 3px 3px hsl(0 0% 0% / 15%))
drop-shadow(0 12px 12px hsl(0 0% 0% / 15%));
}
style>
<div id="ct2">div>
body>
html>