Skip to content

巧用 CSS 实现高频出现的复杂怪状按钮 - 内凹平滑圆角 #272

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
chokcoco opened this issue Nov 21, 2024 · 0 comments
Open

Comments

@chokcoco
Copy link
Owner

chokcoco commented Nov 21, 2024

接上篇,巧用 CSS 实现高频出现的复杂怪状按钮 - 镂空的内凹圆角边框

上篇中,我们通过渐变的高阶技巧,实现了如下的内凹圆角边框:

本文,我们将探讨另外一个非常有意思的图形 -- 内凹平滑圆角

有点类似于 Chrome Tab 按钮造型,此图形我们在 使用 CSS 轻松实现高频出现的各类奇形怪状按钮 讨论过如何解决实现。

常见于这样的设计中:

内凹平滑圆角实现上的难点

上面提到的 Chrome TAB 图形,常见的解决方案如下:

但是内凹平滑圆角实现上的难点在于 -- 内凹部分要求是透明的

什么意思呢?思考上面的图形,如果红色部分要求是整个透明,而白色部分进行颜色填充,试想一下,是不是比较难实现呢?

巧妙通过 mask 图形拼接实现内凹平滑圆角

好的,如果不考虑透明这个因素,要拼接出内凹平滑圆角其实并不困难。

下面我通过一个动画,给出内凹平滑圆角的拼接过程:

<div class="inner-curve-1">
    <div class="g-rect"></div>
</div>
@property --ca {
  syntax: '<color>';
  inherits: false;
  initial-value: #000;
}
@property --cb {
  syntax: '<color>';
  inherits: false;
  initial-value: #ce03f1;
}
@property --cc {
  syntax: '<color>';
  inherits: false;
  initial-value: #ce03f1;
}

.inner-curve-1 {
    width: 300px;
    height: 100px;
    background: radial-gradient(circle at 50% -10px, var(--cc), var(--cc) 40px, #3f51b5 calc(40px + 0.5px), #3f51b5);
    animation: colorChange 6s infinite linear;
    
    .g-rect {
        position: absolute;
        width: 300px;
        height: 20px;
        left: 0;
        top: -15px;
        background: linear-gradient(90deg,var(--ca), var(--ca) 93px, transparent 93px, transparent calc(300px - 93px), var(--ca) calc(300px - 93px), var(--ca));
        z-index: -1;
        animation: colorChange 6s infinite linear;
        
    }
    
    &::before,
    &::after {
        content: "";
        position: absolute;
        width: 40px;
        height: 40px;
        border-radius: 50%;
        background: var(--cb);
        top: -15px;
        left: 72px;
        animation: colorChange 6s infinite linear;
    }
    
    &::after {
        left: unset;
        right: 72px;
    }
}

@keyframes colorChange {
    0% {
        --ca: #000;
        --cb: #ce03f1;
        --cc: #ce03f1;
    }
    70%,
    100% {
        --ca: #3f51b5;
        --cb: #3f51b5;
        --cc: #fff;
    }
}

我们能得到这么一个有意思的变换效果,本质上还是不同形状图形的拼接,动画示意下,应该非常好理解:

当然,这里为了让大家更好的理解,利用了几个元素(算上伪元素),并且,实际最后白色的部分并非透明的!

重点来了,利用多重 mask 实现裁剪拼接

如果我们希望白色部分一整个是透明的,可以如何处理呢?这里就需要运用上 mask,通过在一个元素内部进行裁剪、拼接,最终得到一个镂空的内凹平滑圆角:

<div class='inner-curve-2'></div>
.inner-curve-2 {
  --r: 20px;  
  --s: 40px;  
  --a: 20deg; 

  --_m:0/calc(2*var(--r)) var(--r) no-repeat
    radial-gradient(50% 100% at bottom,#000 calc(100% - 1px), transparent);
  --_d:(var(--s) + var(--r))*cos(var(--a));

  width: 300px;
  height: 100px;
  mask:
    calc(50% + var(--_d)) var(--_m),calc(50% - var(--_d)) var(--_m),
    radial-gradient(var(--s) at 50% calc(-1*sin(var(--a))*var(--s)),
      transparent 100%,#000 calc(100% + 1px)) 0 calc(var(--r)*(1 - sin(var(--a)))),
    linear-gradient(90deg,#000 calc(50% - var(--_d)), transparent 0 calc(50% + var(--_d)),#000 0);
  mask-repeat: no-repeat;
}

效果如下:

并且,它是真实的支持镂空的。我们将上面两种方式实现的镂空的内凹平滑圆角,放在一个渐变背景下,就能看出差别:

上述两种方式的完整代码,你在这里可以看到:CodePen Demo -- 内凹平滑圆角 - inner curve

此方法,最早见于 CSS Shape - Inner Curve/Notch,如果觉得的对于上述的 CSS 变量有点难以理解,可以通过上述链接,实现角度大小的在线编辑:

AI 挑战

那么,这种复杂度的图形,现在的 AI 模型能够实现吗?我们尝试一下,分别把样式造型传给 豆包deepseek

豆包实现

豆包显然没这个能力,最终的实现一看就知道是错误的。

将代码贴到 IDE 上,展示为一个矩形:

deepseek 实现

再试一下 deepseek。由于 deepseek 不支持不带文字的图片,且上传图片后补支持联网搜索,我们只能尽可能的描述清楚需求:

deepseek 需求描述:使用 HTML +CSS 实现一个内凹的平滑圆角图形,类似中文汉字中的凹字,但是凹陷部分是平滑曲线,在凹字上方的四个拐角,都是平滑曲线。整个凹字内部填充为一个颜色,其余部分皆为透明色。

deepseek 思考过程:

好的,我现在需要帮用户用HTML和CSS实现一个内凹的平滑圆角图形,类似“凹”字,但凹陷部分是平滑曲线。整个内部填充颜色,其余透明。首先,我得考虑如何构造这样的形状。

常见的 CSS 形状制作方法有几种:使用边框、伪元素、clip-path,或者 SVG。因为需要内凹的曲线,可能 clip-path 或径向渐变比较合适。但 clip-path 的浏览器支持可能有问题,特别是如果需要更复杂的路径的话。或者,可以考虑使用多个元素叠加,通过overflow:hidden 来隐藏部分区域。

用户要求凹陷部分是平滑曲线,所以可能需要贝塞尔曲线或者圆形切割。比如,可以用一个大的矩形,然后在中间用两个圆形来切割出凹陷的效果。或者使用径向渐变来创建凹陷的阴影效果,但可能不够精确。

另一个思路是使用伪元素。主元素是一个矩形,然后使用 ::before::after 伪元素来创建凹陷的部分。比如,主元素设置背景色,然后用伪元素覆盖中间部分,形成凹陷。但这样需要伪元素的形状是圆形或椭圆,并且通过定位来覆盖中间区域,同时设置mix-blend-mode 或者背景剪切,但可能比较复杂。

或者,使用 CSS 的 mask 属性,通过遮罩来创建透明区域。这可能更灵活。比如,创建一个矩形,然后用径向渐变作为 mask,中间部分透明,周围不透明,这样显示出来的就是中间凹陷的形状。但需要注意浏览器兼容性。

或者,使用 SVG 内联,直接绘制路径,这样更精确,但用户可能希望纯 CSS 实现。

再仔细想,clip-path 属性可以用多边形或圆形来剪切元素。比如,可以定义一个矩形的 clip-path,然后在中间挖一个圆形的区域。但 clip-path 目前不支持排除操作,无法直接挖洞。可能需要组合多个形状。

另一种方法是使用 CSS 渐变。比如,在元素中间放置一个径向渐变,从透明到颜色,这样中间看起来是凹陷的。但这样可能只是颜色渐变,而不是真正的形状凹陷。

或者,使用多个 box-shadow 来模拟凹陷效果,但可能不够精确。

综合考虑,可能使用伪元素叠加的方法。主元素是一个矩形,设置 overflow:hidden,然后内部有一个伪元素,定位到中间,用更大的尺寸和背景色,然后通过 border-radiustransform 来 创建凹陷效果。或者,主元素是一个圆角矩形,然后伪元素是一个圆形,覆盖在中间上方,通过背景色和位移来形成凹陷。

例如,主元素是一个矩形,背景色为红色,然后伪元素是一个更大的圆形,背景色为透明,通过 border 来显示周围的部分,但这样可能不太容易。

或者,使用两个元素叠加:外部容器是一个矩形,内部有一个圆形元素,通过overflow:hidden和定位,让内部元素遮盖中间部分,形成凹陷。但这样需要外部容器背景透明,内部元素背景色,中间被遮盖的部分透明,可能比较复杂。

或者,使用 CSS 的 mask-composite 属性,但兼容性可能不好。

deepseek 实现效果

Wow,从我的角度,看上面的分析,deepseek 的思考能力已经超强了,并且能够将 CSS 中一些比较高阶的属性 clip-pathmaskmix-blend-mode 进行组合使用,实属不易!

最终,看看 deepseek 给出的解法:

<div></div>
div {
    position: relative;
    margin: auto;
    width: 200px;
    height: 100px;
}
/* 主体颜色层 */
div::before {
  content: '';
  position: absolute;
  width: 100%;
  height: 100%;
  background: #2196F3;
  clip-path: path('M0 10 Q0 0 10 0 H190 Q200 0 200 10 V40 Q180 50 200 60 V90 Q200 100 190 100 H10 Q0 100 0 90 V60 Q20 50 0 40 V10');
  border-radius: 8px;
}

/* 平滑过渡层 */
div::after {
  content: '';
  position: absolute;
  width: calc(100% - 4px);
  height: calc(100% - 4px);
  top: 2px;
  left: 2px;
  background: #2196F3;
  clip-path: path('M2 12 Q2 2 12 2 H188 Q198 2 198 12 V38 Q178 50 198 62 V88 Q198 98 188 98 H12 Q2 98 2 88 V62 Q22 50 2 38 V12');
  filter: blur(1px);
  opacity: 0.8;
}

最终效果如下:

已经非常接近了,当然,内凹上方的曲线并非平滑连接。但是,如此复杂的图形,dp 已经可以通过思考过程,给我们实现它提供非常多的参考。假以时日,这种图形化问题,对于大模型而言,应该也都不在话下了。

最后

好了,本文到此结束,一个非常有意思的通过 CSS 拼接/裁剪/遮罩方式得到内凹圆角的技巧,希望本文对你有所帮助 :)

想 Get 到最有意思的 CSS 资讯,千万不要错过我的公众号 -- iCSS前端趣闻 😄

更多精彩 CSS 技术文章汇总在我的 Github -- iCSS ,持续更新,欢迎点个 star 订阅收藏。

如果还有什么疑问或者建议,可以多多交流,原创文章,文笔有限,才疏学浅,文中若有不正之处,万望告知。

@chokcoco chokcoco changed the title [WIP]巧用 CSS 实现高频出现的复杂怪状按钮(二) [WIP]巧用 CSS 实现高频出现的复杂怪状按钮 - 内凹平滑圆角 Feb 5, 2025
@chokcoco chokcoco changed the title [WIP]巧用 CSS 实现高频出现的复杂怪状按钮 - 内凹平滑圆角 巧用 CSS 实现高频出现的复杂怪状按钮 - 内凹平滑圆角 Feb 5, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant