接上篇,巧用 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-radius
和 transform
来 创建凹陷效果。或者,主元素是一个圆角矩形,然后伪元素是一个圆形,覆盖在中间上方,通过背景色和位移来形成凹陷。
例如,主元素是一个矩形,背景色为红色,然后伪元素是一个更大的圆形,背景色为透明,通过 border 来显示周围的部分,但这样可能不太容易。
或者,使用两个元素叠加:外部容器是一个矩形,内部有一个圆形元素,通过overflow:hidden和定位,让内部元素遮盖中间部分,形成凹陷。但这样需要外部容器背景透明,内部元素背景色,中间被遮盖的部分透明,可能比较复杂。
或者,使用 CSS 的 mask-composite
属性,但兼容性可能不好。
deepseek 实现效果
Wow,从我的角度,看上面的分析,deepseek 的思考能力已经超强了,并且能够将 CSS 中一些比较高阶的属性 clip-path
、mask
、mix-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 订阅收藏。
如果还有什么疑问或者建议,可以多多交流,原创文章,文笔有限,才疏学浅,文中若有不正之处,万望告知。