记得@Lea Verou的《CSS Secrets》一书和前几天@Chris Coyier刚发的帖子都介绍了CSS怎么实现元素斜切口的效果。我也尝试着借助Vue的能力,把这种效果构建成一个Vue组件。我把这种效果定义为外切口。而今天将要聊的是与其刚好相反的一个效果:CSS如何实现内凹角的效果。
上图展示的效果就是接下来所要聊的内凹角的效果。也就是说,通过下文的介绍,我们可以知道这种效果是如何做的,而且如何在多个元素上实现这样的内凹角效果。在实现这样的效果当中,将会遇到些什么棘手的问题,又是怎么绕过这些问题的。
最初的想法:box-shadow
对于box-shadow的属性,想必大家已经非常了解了,如果你从未接触过box-shadow属性,那么强烈建议您花一点时间去了解一下box-shadow相关的知识。这样能帮助你更好的理解后续的内容。
我先假设你对box-shadow有了一定的了解。就算你不了解,也没有关系。你也可以继续后面的内容。假设我们有一个div的元素。给这个元素添加了一个.box的类名:
我们可以显式的给这个.box元素设置大小或者通过其自己的内容来决定大小,不管是哪种方式,都并不很重要。这里为了简单起见,给其设置了max-width和min-height(也是用来设置其大小的)。另外为了能在浏览器中看到效果,其添加了一个outline的效果,让其看起来有边框的样子。或许你会问,为什么不直接使用border呢?这个问题留给大家去思考吧,因为不是这篇文章要探讨的内容。
.box {
outline: solid 2px;
max-width: 15em;
min-height: 10em;
}
接下来,通过伪元素::before来创建一个正方形,其边长等于圆角的直径(或者半径--r的两倍),而且对这个伪元素使用绝对定位。另外为了能在浏览器中看到效果,给这个伪元素添加了一个box-shadow和background属性。这只是用来辅助大家理解的,后续会删除的。
:root {
--r: 2em;
}
.box {
position: relative;
&::before {
content: '';
position: absolute;
padding: var(--r);
box-shadow: 0 0 7px #b53;
background: #95a;
}
}
特别声明:本文的实例代码都来自于@ANA TUDOR的《Scooped Corners in 2018》一文。不同的是我把文章中的Sass变量换成了CSS自定义变量。后续内容如无特别说明,都将类似的做了修改。
这个时候看到的效果如下:
效果如你所期望的一样。接下来对伪元素::before的border-radius值设置为50%,让它成为一个圆形,并且给它设置一个margin的负值,值等于它的半径--r。伪元素的中心点和它的父容器.box的左上角(0,0)重合。为了让溢出的.box的伪元素能隐藏起来,需要在.box中添加一个overflow:hidden。
:root {
--r: 2em;
}
.box {
position: relative;
overflow: hidden;
&::before {
content: '';
position: absolute;
padding: var(--r);
box-shadow: 0 0 7px #b53;
background: #95a;
margin: calc(var(--r) * (-1));
border-radius: 50%;
}
}
现在的结果是这样的:
但这样的效果仍然不是我们想要的。为了达到我们想要的效果,我们需要使用box-shadow的第四个参数值:box-shadow添加第四个参数值的效果,可以看下面这个Demo:
你可能已经猜到我们下一步要做什么了。把background和box-shadow前三个值(x和y轴的偏移值以及模糊半径)设置为0,并给box-shadow的扩展半径设置为一个较大的值。
box-shadow: 0 0 0 300px;
下面的这个示例演示了box-shadow的扩展半径如何让阴影效果覆盖容器更多的面积。
这里用到的一个技巧是让box-shadow有足够大的扩展半径,这样让伪元素的阴影能覆盖其容器更多的面积。这是非常有意思的一点,给.box设置box-shadow以及给其伪元素添加一个半透明的阴影效果。
.box {
overflow: hidden;
position: relative;
margin: .25em auto;
min-width: 15em;
max-width: 15em;
min-height: 10em;
border-radius: 1em;
&:before {
position: absolute;
margin: calc(var(--r) * -1);
padding: var(--r);
border-radius: 50%;
box-shadow: 0 0 0 300px rgba(#95a, .75);
content: ''
}
}
其实这是很关键的一步,如果你不仔细看,你或许会认为,那个凹角的效果是box-shadow实现的。或许你和我一样会纳闷,box-shadow是如何实现透明凹角的效果。事实并非如此,透明凹角部分是伪元素::before的background-color为transparent,而整个紫色部分是由::before的box-shadow实现的(就是阴影扩散半径有足够大的值,能铺满.box的容器大小)。我录一个视频给大家看看,或许能比文字更好的说明一切原理:
是不是一图胜过千言万语呀。
上面看到的效果,不难发现,凹角的大小是固定的。好在我们这里使用了CSS的自定义属性。因为使用CSS自定义属性之后,可以很容易的通过JavaScript来修改这个属性。这样一来,就可以很好的控制凹角的大小。比如:
:root { --r: 50px }
.box {
padding: var(--r);
&:before {
margin: calc(-1*var(--r));
padding: inherit;
}
}
这是实现凹角效果的关键样式。具体的不多说了,能只要仔细阅读上面的内容,你就能明白为什么。
值得一提的是,我们前面看到的效果都是.box中没有任何内容。也就是说.box里有内容的时候,我们是需要在样式上做一定的调整的。为什么这么说呢?先来看一个效果:
要解决这个问题,很简单,咱们只需要在.box的伪元素::before上添加z-index属性,并且给其设置值为-1。
另外通过.setProperty()来修改--r的值。这需要一些JavaScript代码来支持:
// 获取id为r的input元素 和 output元素