Skip to content

圆角的实现

Posted on:2024年1月19日 at 13:32

背景

这两天工作中要实现一个圆角,本来以为是很简单的效果,就像下图一样

可以看到上面有一个 tab(sticky 定位),下面是个图片的 card,其实图片下面还有更多 card,内容很多,页面向下滚动。

需求是上面两个角要设置为 16px 的圆角,而且页面滚动时,两个圆角一直在。

方案一

一开始的想法是在这些 card 外围包裹一层容器,给容器设置下面 css

border-radius: 16px 16px 0 0;
overflow: hidden;

这样是可以实现的,但是这就有一个问题,包了一层 div 后,滚动容器就变了,原来是在 body 上,这样就变成在容器上了,这样滚动条就不在最右侧了,不可接受。

方案二

所以后面就打算用背景颜色画一个圆角的效果出来,遮住两个角,并且背景颜色和外层背景色一致,这样看上去就和圆角一样了,想法是好的,怎么实现呢?

在同事的提醒下,发现可以使用 background radial-gradient,径向渐变

.cornor {
  position: fixed;
  top: 0;
  left: 0;
  border: 1px solid red;
  width: 100px; /* 设置容器宽度 */
  height: 100px; /* 设置容器高度 */
  background: radial-gradient(
    circle at 100% 100%,
    transparent 100px,
    #cccccc 100px
  );
}

效果就是这样:

你可以点击这个在线示例查看。

看上去确实可以实现圆角的效果,那就抓紧试一试,使用 before after 两个伪元素,分别定位(相对于顶部 tab)左右两侧,看上去效果不错。拖到大屏上试试

怎么会有马赛克。。。这一时之间不知道怎么办才好了,看来得换一种方法了。

方案三

上面的方法思路其实是对的,所以我想或许可以换一种方式实现这个“圆角“,首先想到的是 svg。

svg 不太熟,手写是不可能的,去问了一下 ChatGPT

问:你用 svg 画一个正方形 边长 100px,中间一个圆 半径 50px, 正方形(出去圆的部分)背景 red 圆形的背景透明,显示的是 正方形下面元素的颜色,要透过正方形

答:你想要的是一个带有中心透明圆的正方形。这可以通过使用 <mask> 元素实现。下面是相应的 SVG 代码:

<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg">
  <defs>
    <mask id="circle-mask">
      <!-- 整个正方形都是白色,这部分会显示下面的填充 -->
      <rect x="0" y="0" width="100" height="100" fill="white" />
      <!-- 圆形是黑色,这部分将是透明的 -->
      <circle cx="50" cy="50" r="50" fill="black" />
    </mask>
  </defs>
  <!-- 使用 mask 属性应用我们的蒙版 -->
  <rect
    x="0"
    y="0"
    width="100"
    height="100"
    fill="red"
    mask="url(#circle-mask)"
  />
</svg>

在这个 SVG 中,我们首先定义了一个<mask>元素,它包含了一个填充为白色的正方形和一个填充为黑色的圆形。在 SVG 蒙版中,白色代表完全可见,黑色代表完全透明。

之后,我们创建了一个红色的正方形,并通过mask属性应用了我们定义的蒙版。这将使得正方形中间的圆形部分透明,显示下面的任何内容。正方形的其余部分将以红色显示。

代码复制出来看看效果

ok,使我们要的效果,修改一下代码

<svg
  :class="['radius-icon', { flips }]"
  width="16"
  height="16"
  xmlns="http://www.w3.org/2000/svg"
>
  <defs>
    <mask id="circle-mask">
      <rect x="0" y="0" width="32" height="32" fill="white" />
      <circle cx="16" cy="16" r="16" fill="black" />
    </mask>
  </defs>
  <rect
    x="0"
    y="0"
    width="32"
    height="32"
    class="radius-icon__fill"
    mask="url(#circle-mask)"
  />
</svg>

效果

完美,到这里才算是完成了,一个小小的需求,也学到了不少东西。

手动分割线

其实不算完美,放大看以及移动端还是可以看出来一些不自然,后面再想办法改改。。。

------ 2024.3.8 更新 ------

上面说了移动端显示还是有问题,因为元素是有媒体查询的,而上面 svg 标签上设置了 width height 属性,这就相当于把 svg 大小写死了。

其实 svg 是有响应式效果的, 我们需要改成这样 <svg viewBox="0 0 20 20" ...>,这样在使用的时候,设置宽高,svg 就会等比放大了。

当然最终还因为设计师改了方案,不需要两个角了。