平时做个网页提示框,那个带尖尖的小三角看着简单,真动手整起来就有点懵。网上搜一圈,方法五花八门,跟做菜似的,每家都有独门秘方。今儿就把这些“野路子”挨个捋一遍,从最基础的边框大法到只用单个元素的骚操作,手把手带小白走一遍流程。
提示框的小三角本质上是个视觉小把戏。常见思路有两种:一是用边框拼出三角形,二是把正方形旋转45度再切掉多余部分。核心都是利用CSS的透明、定位和裁剪属性,把一个普通元素伪装成尖角。下面这几个方案,覆盖从简单到复杂的各种场景,按需取用就行。
三角形咋来的
先整明白原理。一个宽高为0的元素,给它四周都加上边框,每个边框会变成梯形。当宽高为0时,四个梯形就变成了四个三角形,每个角一个。把其中三个边框颜色设成透明,剩下的那个就单独露出来了。这就是边框大法的核心。另一种思路是弄个正方形,转45度角,让其中一个角露在外面,看起来就是三角形。搞懂了这俩基础,后面所有花活都围绕它们转。
边框大法走起
这个方法最老牌,2010年的Stack Overflow上就有了,经得起考验。咱一步步来。
搭骨架
先写个提示框的HTML,只用单个元素,干净利落:
<span class="tooltip">别摸我,有毒</span>画三角形
用伪元素::before来画那个小三角。需要把伪元素的宽高归零,只留边框。
.tooltip::before {
content: "";
border-width: 8px; /* 这个控制三角底边宽度 */
border-style: solid;
border-color: transparent; /* 先全透明 */
border-top-width: 12px; /* 顶部边框单独加粗,三角高度就变大了 */
border-top-color: #ff5722; /* 只让顶部边框有色,其他方向透明 */
}代码里的border-width设8px,四个边框都是8px粗。然后border-top-width单独改成12px,相当于顶部边框比其他三边更厚。因为宽高为0,顶部边框会形成一个底边宽16px(左右边框各8px)、高12px的三角形。想要三角朝哪个方向,就改哪个方向的边框颜色,其他方向保持透明。
贴到提示框上
需要把提示框设成相对定位,伪元素设成绝对定位,这样才能把三角粘在框框的某个边上。
.tooltip {
position: relative;
display: inline-block;
background: #ff5722;
color: white;
padding: 8px 12px;
border-radius: 6px;
}
.tooltip::before {
content: "";
position: absolute;
top: 100%; /* 让三角出现在提示框底部外侧 */
left: 50%;
border-width: 8px;
border-style: solid;
border-color: transparent;
border-top-width: 12px;
border-top-color: #ff5722;
transform: translateX(-50%); /* 水平居中,因为left:50%会让三角左边缘在中间,平移一半宽度才正中 */
}这里transform: translateX(-50%)是个坑点。伪元素宽高为0,但它的定位原点在左上角。left: 50%会让左上角跑到父元素中间,三角本身不对称,所以需要往左挪自身宽度的一半。由于没有宽高,平移比例是按边框算的?实际上translateX(-50%)是相对于伪元素自身的宽度,但伪元素宽度为0,这步有用吗?仔细测过,因为边框撑开了实际占据空间,translateX(-50%)确实能居中。稳妥起见,也可以用left: calc(50% - 4px)之类的硬调,但平移更省事。
如果想把三角放到左边或右边,只需要改top和left的值,同时改transform的方向。比如三角朝右,就把border-left-color改成有色,然后设置left: 100%; top: 50%; transform: translateY(-50%);。
翻车现场
边框大法有个硬伤:三角的方向变了,就得重新调边框颜色和位置。比如提示框出现在元素上方,三角得朝下,那就得把border-bottom-color设成有色,然后把top改成auto,bottom: 100%。每次都得改好几个属性,不够灵活。而且用了边框属性,就没法再给伪元素加其他边框效果了。
旋转正方形
换个思路,不用边框,直接弄个正方形,转45度,只让一个角露出来。
画方块
.tooltip::before {
content: "";
width: 16px;
height: 16px;
background: #ff5722;
position: absolute;
top: 75%;
left: 50%;
transform: translateX(-50%) rotate(45deg);
z-index: -1; /* 藏到提示框背景后面,只露出半个角 */
}这个方块转了45度之后,原本的四个角变成了上下左右四个尖尖。只让朝上的那个尖尖露出来,其他部分被提示框的背景盖住。z-index: -1就是把方块塞到提示框的底层。
调整位置
方块的位置需要算一下。因为旋转中心默认是元素中心,top: 75%配合translateX(-50%)再加rotate(45deg),能让尖尖刚好卡在提示框底部中间。这里top值需要根据提示框的高度微调,一般取100%的话三角会掉出去,取75%左右比较稳。
潜在bug
这种方法做出来的不是严格意义上的三角形,是个旋转45度的正方形露出的角。如果提示框高度特别小(比如只有20px高),那个方块的上半截可能会从顶部冒出来。解决办法是给提示框加overflow: hidden,但这样又会把超出部分切掉,三角可能缺一块。所以只适合提示框高度足够大的场景。
clip-path精准切割
旋转正方形不够完美,那就直接用clip-path把正方形切成真正的三角形,想切多尖就切多尖。
切三角形
.tooltip::before {
content: "";
width: 20px; /* 三角底边宽度 */
height: 14px; /* 三角高度 */
background: #ff5722;
position: absolute;
top: 100%;
left: 50%;
transform: translateX(-50%);
clip-path: polygon(0% 0%, 100% 0%, 50% 100%);
}polygon(0% 0%, 100% 0%, 50% 100%)的意思是:从左上角(0%,0%)开始,画到右上角(100%,0%),再画到底边中点(50%,100%),最后闭合。这样就切出了一个朝上的三角形。如果想朝下,把三个点改成0% 100%, 100% 100%, 50% 0%。朝左朝右同理,改x轴百分比就行。
防越界
三角贴边的时候容易戳出去。比如三角要贴在提示框左侧,并且提示框靠页面左边缘很近,三角的左边可能会超出屏幕。这时可以用min()和max()函数动态限制切割点。
.tooltip::before {
clip-path: polygon(
max(50% - var(--left-offset), 0%) 0%,
min(150% - var(--left-offset), 100%) 0%,
50% 100%
);
}--left-offset是三角离提示框左边界的距离。当三角太靠左时,max(50% - 偏移, 0%)保证切割点不会跑到0%左边;当三角太靠右时,min(150% - 偏移, 100%)保证不会超过100%。这样三角永远不会被切崩。
单元素终极方案
前面几个都用到了伪元素。万一伪元素被占用了(比如已经用来做其他装饰),或者单纯就想用一个标签搞定所有,那就得祭出border-image加clip-path的组合拳。
思路
不用伪元素,直接在.tooltip本体上动刀。先用border-image给元素扩展出一块“假背景”,然后用clip-path把本体和扩展出来的三角一起切出来。
操作流程
.tooltip {
/* 先画本体背景 */
background: #ff5722;
padding: 8px 12px;
border-radius: 6px;
position: relative;
/* 关键:用border-image造一个额外的三角区域 */
border-image: fill 0 // 14px conic-gradient(#ff5722 0 0);
/* fill表示让背景填充到边框区域,14px是三角高度,conic-gradient就是纯色 */
/* 再用clip-path切出带三角的形状 */
clip-path: polygon(
0% 100%, /* 左下角 */
0% 0%, /* 左上角 */
100% 0%, /* 右上角 */
100% 100%, /* 右下角 */
calc(50% + 10px) 100%, /* 三角右边起点 */
50% calc(100% + 14px), /* 三角尖点 */
calc(50% - 10px) 100% /* 三角左边起点 */
);
}border-image那一行比较绕。fill表示把背景延伸到边框区域。0是边框宽度(这里不设边框厚度),// 14px是边框外扩的距离(三角的高度)。conic-gradient(#ff5722 0 0)是生成一个纯色渐变,相当于一张纯色图片。整句的意思就是:在元素底部往外扩展14px的区域,涂上红色。
然后clip-path把本体和这个扩展出来的三角一起切出来。七个点的顺序按逆时针或顺时针都行,只要连起来是个带尖角的形状。calc(50% + 10px)里的10px是三角底边的一半,根据实际需要调整。
动态防越界
同样需要加上边界保护,避免三角太靠边时被切歪。
.tooltip {
clip-path: polygon(
0% 100%,
0% 0%,
100% 0%,
100% 100%,
min(calc(50% + var(--三角半宽)), 100%) 100%,
var(--三角水平位置) calc(100% + var(--三角高度)),
max(calc(50% - var(--三角半宽)), 0%) 100%
);
}--三角水平位置用left百分比传入,--三角半宽和--三角高度按实际尺寸设置。min()和max()保证三角的左右两个底角不会超出提示框的边缘。
这个方案的优点是只用一个元素,没有任何伪元素,代码干净。缺点是border-image的语法比较冷门,新手看了容易懵,但抄下来能用就行。
各方案咋选
| 方案名称 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 边框大法 | 简单易懂 | 改方向麻烦 | 固定方向提示框 |
| 旋转方块 | 代码少 | 不精确 | 高度较大的提示框 |
| clip-path | 形状精确 | 需伪元素 | 大部分常规场景 |
| 单元素终极 | 省标签 | 语法复杂 | 伪元素被占时 |
平时开发,优先用边框大法或者clip-path的伪元素方案,够用了。实在有洁癖或者伪元素不够使,再上单元素那个硬核玩法。这几种法子都跑一遍,以后碰见提示框小三角,心里就有数了。
