🥰 收藏系列 | 弧形tab实现方案

1,134 阅读8分钟

本文为稀土掘金技术社区首发签约文章,30天内禁止转载,30天后未获授权禁止转载,侵权必究!

🌻 前言

如下图弧形tab是非常常见的UI场景,一般你如何实现呢?
我这里给大家提供三种思路,使用到的dom元素由多至少,难度由简入难,具体使用哪种方式更好可以先看完三种方案之后在结尾处说明。最终效果:CSS Tips

image.png

另外,做这个demo的时候,寻思干脆直接整个简单的网站,以后在上面更新一些css技巧。地址贴在这了,感兴趣的可以收藏下: CSS Tips

🎨 实现

方法1:伪元素+1个额外元素

为了方便调整弧度大小,弧度半径用css变量var(--r)抽离

其实看到这个样式,大部分人都能想到上半部分的弧度用border-radius实现,主要难度就在于下半部分两边的弧度角。

要保证 S 型弧度和谐,那么上半部分弧度和下半部分弧度的半径就要一致。那么只要往tab两边用伪元素设置相同半径的弧度不就有相同弧度了吗,如下:

image.png

可是现在下面的角没颜色,倒是两个伪元素有背景色。此时只要把tab的下半部分延伸出来背景色,然后伪元素设置成页面的背景色,遮挡住底部延伸出的颜色,就可以达到一个弧形tab的效果。

image.png

怎么在两边延伸出背景色呢?首先一个额外元素占满整个宽度是不行的,会把tab里的文字遮挡,所以只能在两边延伸出来。当然你可以直接用两个额外元素绝对定位到两侧。

当然你也可以用一个额外元素占满宽度,只不过不能简单的设置背景色,要用linear-gradient()线性渐变画两个小方块定位到两侧,用两个linear-gradient()也可以,用一个linear-gradient()然后在x方向重复也可以,如下:

left: calc(0px - var(--r));
bottom: 0;
width: calc(100% + 2 * var(--r));
height: 50%;
background: linear-gradient(to right, var(--bg) 0, var(--bg) var(--r), #0000 var(--r), #0000 100%) 0 0/calc(100% - var(--r)) 100% repeat-x;

 /* 
 0 0/calc(100% - var(--r)) 100% repeat-x
 对应
 left位置 top位置/宽 高 x方向重复
 */

简单对上面的渐变背景做个解释:建立一个从左到右的线性渐变,先用蓝色画够方块的长度之后,后面用透明色填充,后面的配置看上面代码中的注释,只要宽度等于总宽度减去一个方块的宽度,然后重复渲染就好了。

image.png

另外,如果tab有父元素,可以直接在tab元素上用box-shadow在tab下方创建投影,填充伪元素没遮盖的那个角,然后父元素要设置溢出隐藏。这样就不用额外元素延伸出来,填充那个角了。

方法2:伪元素+径向渐变

这种方法理解起来最简单,实现也最简单。就是在两侧用伪元素创建两个小方块,然后用径向渐变画出个弧度的角来,渐变色由透明开始,到var(--r)时变成tab的背景色,如下:

&::before,
&::after {
    content: '';
    position: absolute;
    bottom: 0;
    width: calc(2 * var(--r));
    aspect-ratio: 1;
}

&::before {
    left: calc(0px - 2 * var(--r));
    background: radial-gradient(var(--r) at 0 0, transparent 100%, red 100%) 100% 100%/50% 50% no-repeat;
}

&::after {
    right: calc(0px - 2 * var(--r));
    background: radial-gradient(var(--r) at var(--r) 0, transparent 100%, red 100%) 0 100%/50% 50% no-repeat;
}

image.png

这个方法主要就是要画出渐变后,左边的伪元素只需要上图右下角的四分之一部分,右边的伪元素同理。所以要利用backgroud-positionbackground-size只显示这部分。

方法3:无额外元素 & 无伪元素

上面两种方法都得依赖额外元素或伪元素,而且你会发现,假如这个tab是渐变色的,上面两种方案虽说可以在两边的伪元素分别取渐变的起始颜色和结束颜色,但是效果肯定不会那么完美。

那怎么办呢?上面的两种方法都是在做加法,下面我们做下减法。其实这种“不规则”图案,看到的第一反应应该是被裁剪出来的。

关于裁剪,自然而然就会想到clip-pathmask

clip-path毋庸置疑,肯定是能办到的,例如下面用clip-path裁出一个下边的弧度角来,这个就可以代替上面方案2中的径向渐变了。当然直接用clip-path裁出完整的弧度tab来也完全可以做到。但是复杂度就比较高了,还得自己写path绘制路径。

div {
  width: 200px;
  height: 200px;
  border: 1px solid black;
  background: red;
  clip-path: path('M100 100 L0 100 A100 100 0 0 0 100 0 Z');
}

image.png

所以clip-path的方案可行但没必要。

那最后就要说到本文的重点了,用mask来实现弧形tab。

mask和clip-path有相似之处,可以大致理解为都能保留图形覆盖的部分。所以用mask你照样可以类似用个弧形tab的 svg 的 id 作为mask的 url()。但是很明显,既然都这么麻烦了,还不如直接切图呢,而且用svg作为mask,调整起来也有一定复杂度。

所以,我这里用mask实现弧形tab主要利用了padding-box和径向渐变,其中的处理关系很巧妙。如下:

上边的弧度还是由border-radius实现。因为如果你正常设置上边的弧度,那么下边的弧度怎么办?这个ui难点就在于上边的弧度是往里走,下边的弧度是往外走。所以下半部分必须得延伸出来一部分,就跟上文方案1中延伸出来背景一样。

这里利用了padding-box来保留中间主体

image.png

中间的padding-box,即主体部分,因为要完全保留,所以随便用个渐变占满padding-box即可,例如我这里用了线性渐变:

linear-gradient(#000, #000) padding-box

如上图的解释,两边可以用border延伸出来背景色,然后就可以在border区域用mask“裁剪”出一个带有弧度的角了。稍微思考可得:用径向渐变,从中心由透明开始变化,到弧度半径var(--r)后开始设置个背景色(因为mask会保留有背景色遮盖的部分)。这样就可以用蒙层保留一个角,另外,我们只需要下半部分有弧度,所以这个径向渐变的高度只需要 50% 即可,用径向渐变做蒙层实现一个角的效果如下:

image.png

同样,实现一边之后,另一边可以再写个径向渐变,也可以用 repeat 重复绘制,如方案1中延伸背景也用了重复绘制,这里就不做过多阐述了。

最后,padding-box的完整mask 和两边的 径向渐变mask 一叠加,就完美实现了弧形tab的效果:

以下是关键代码:

      /* tab高度 */
      --height: 36px;
      /* 弧度半径控制曲率 */
      --r: 14px;
      /* 边框遮罩 */
      --mask: radial-gradient(var(--r) at var(--r) 0, #0000 100%, #000 100%) calc(-1*var(--r)) 100%/100% var(--r) repeat-x,linear-gradient(#000, #000) padding-box;
      width: fit-content;
      height: var(--height);
      /* 靠border延伸出背景色,这里border设置透明也行,设置tab背景色也行,但是设置透明可以就不用后续调整了 */
      border-left: var(--r) solid #0000;
      border-right: var(--r) solid #0000;
      /* 给 border-radius 让padding-box上边具有弧度 */
      border-radius: calc(2*var(--r)) calc(2*var(--r)) 0 0/var(--r);
      /* 设置蒙层,注意要同时设置带-webkit-前缀的 */
      mask: var(--mask);
      -webkit-mask: var(--mask);

image.png

image.png

🎁 最后

总结一下:

  • 方案1逻辑比较简单,但是代码略多;
  • 方案2相对也比较简单,而且没有额外元素,但是在处理渐变tab上效果并不是很好;
  • 方案3逻辑复杂,代码最少,而且能完美处理渐变tab,还能随意控制曲率。

方案3是最好用的,但是mask毕竟有点兼容性问题,使用时要同时设置mask属性和带有-webkit-前缀的属性。不过现在mask的兼容性还是可以的,一般可以大胆用。

查看效果:CSS Tips

学如逆水行舟,不进则退~👊👊👊

先看后赞,养成习惯👍
收藏吃灰,不如学会🍗
点个关注,不要迷路🪤