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

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

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

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

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

常见于这样的设计中:

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

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

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

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

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

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

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

HTML 复制代码
<div class="inner-curve-1">
    <div class="g-rect"></div>
</div>
CSS 复制代码
@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,通过在一个元素内部进行裁剪、拼接,最终得到一个镂空的内凹平滑圆角:

HTML 复制代码
<div class='inner-curve-2'></div>
CSS 复制代码
.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 给出的解法:

HTML 复制代码
<div></div>
CSS 复制代码
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 订阅收藏。

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

相关推荐
天宇&嘘月2 小时前
web第三次作业
前端·javascript·css
小王不会写code2 小时前
axios
前端·javascript·axios
发呆的薇薇°3 小时前
vue3 配置@根路径
前端·vue.js
luckyext4 小时前
HBuilderX中,VUE生成随机数字,vue调用随机数函数
前端·javascript·vue.js·微信小程序·小程序
小小码农(找工作版)4 小时前
JavaScript 前端面试 4(作用域链、this)
前端·javascript·面试
前端没钱4 小时前
前端需要学习 Docker 吗?
前端·学习·docker
前端郭德纲4 小时前
前端自动化部署的极简方案
运维·前端·自动化
海绵宝宝_4 小时前
【HarmonyOS NEXT】获取正式应用签名证书的签名信息
android·前端·华为·harmonyos·鸿蒙·鸿蒙应用开发
码农土豆5 小时前
chrome V3插件开发,调用 chrome.action.setIcon,提示路径找不到
前端·chrome
鱼樱前端5 小时前
深入JavaScript引擎与模块加载机制:从V8原理到模块化实战
前端·javascript