记得@Lea Verou的《CSS Secrets》一书和前几天@Chris Coyier刚发的帖子都介绍了CSS怎么实现元素斜切口的效果。我也尝试着借助Vue的能力,把这种效果构建成 一个Vue组件。我把这种效果定义为外切口。而今天将要聊的是与其刚好相反的一个效果:CSS如何实现内凹角的效果。

上图展示的效果就是接下来所要聊的内凹角的效果。也就是说,通过下文的介绍,我们可以知道这种效果是如何做的,而且如何在多个元素上实现这样的内凹角效果。在实现这样的效果当中,将会遇到些什么棘手的问题,又是怎么绕过这些问题的。
最初的想法:box-shadow
对于box-shadow的属性,想必大家已经非常了解了,如果你从未接触过box-shadow属性,那么强烈建议您花一点时间去了解一下box-shadow相关的知识。这样能帮助你更好的理解后续的内容。
我先假设你对box-shadow有了一定的了解。就算你不了解,也没有关系。你也可以继续后面的内容。假设我们有一个div的元素。给这个元素添加了一个.box的类名:
<div class="box"></div> 我们可以显式的给这个
.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元素
const _R = document.getElementById('r'),
_O = _R.nextElementSibling.querySelector('output');
// 设置一个变量v
let v;
// 创建一个update函数,更新--r的值
function update() {
if(v !== +_R.value) {
document.body.style.setProperty(`--r`, `${_O.value = v = +_R.value}px`)
}
};
update();
_R.addEventListener('change', update, false);
_R.addEventListener('input', update, false);最终效果如下:

现在离我们想要的效果越来越近了。我们已经知道如何通过box-shadow给单个.box设置单个凹角的效果。那么如果我们想要给一个元素添加四个凹角效果,怎么实现呢?想想,如果你想得出来,可以立马动手试试,就算你想不出来,也并不要紧,后面我们会介绍怎么给.box盒子的每个角添加凹角的效果。
那么到这一步,咱们先暂停一下。上面我们看到的是CSS的自定义属性和JavaScript来实现想要的凹角效果。那么咱们先暂停一步,来看看怎么通过Vue来实现上面示例的效果。

有关于Vue的代码这里就不展示了。详细的可以查看上面Demo的代码,其实你还可以添加其他的参数,比如除了给scooped-corners组件传凹角半径值之外,还可以传border-radius和background-color之类。感兴趣的可以尝试一下,并且欢迎在下面的评论中分享您的成果。
就用这种技术
接下来,咱们再深入一点,看看怎么运用这种技术来实现文章开头展示的效果。这里有一点不一样,伪元素的中心点与盒子不致,但他们都有一个共同点,伪元素的中心点,在每个盒子的顶点处。
使用的HTML结构非常简单,这里使用了4个<article>元素(相当于前面所讲的.box元素),在<article>元素中包含了一些文本内容:
<article>
<h3>Yogi Bear</h3>
<section>
<p>Smaaaarter than the average bear!</p><a href="#">go</a>
</section>
</article>
... <body>包含了四个<article>元素,还有一个<header>元素,整个布局效果采用的是Flexbox。从文章开头的效果上来看,<header>宽度非常宽,然后每行有一个个或两个<article>元素。具体每行展示一个还是两个,这取决于浏览器视窗的宽度。
如果我们每一行只有一个<article>时,那么元素上就不会有凹角的效果,这个时候需要把凹角的半径设置为0。否则我们就要设置一个非零的半径,也就是说--r的值不为0。
:root {
--minW: 15rem; /* 每个article元素的最小宽度 */
--m: 1rem; /* 每个article元素的margin值 */
--r: 0px; /* 每个article元素上凹角的半径 */
}
article {
margin: var(--m);
min-width: var(--minW);
width: 21em;
}
@media (min-width: 2*($min-w + 2*$m)) {
html {
--r: 4rem;
}
article {
width: 40%;
}
} 特别注意,CSS自定义属性不能用于媒体查询的条件中,但可以用于媒体查询的区块内。
现在我们考虑一下,每行有两个<article>元素(当然,每个元素都有一个内凹角,因为这个才是我们感兴趣的东东)。
第一个元素中,凹角的圆形在它的父元素的最右边边,也就是left:100%。为了将凹角圆中心点x坐标移到其父元素的右边缘,我们就需要减去圆的半径--r,那么left的值就变成了calc(100% - var(--r))。但我们不想让它出现在右边,而是希望它在<article>元素向右移--m。这样我们就可以算出我们最终想要的一个值:
left: calc(100% - var(--r) + var(--m)); 


对于最后一个,其水平方向的偏移量和第二个具有相同的值,垂直方向的偏移量和第三个具有相同的值。

所以四个元素对应的left和top的偏移量如下:
article:nth-of-type(1) { /* 1st */
left: calc(100% - var(--r) + var(--m));
top: calc(100% - var(--r) + var(--m));
}
article:nth-of-type(2) { /* 2nd */
left: calc( 0% - var(--r) - var(--m));
top: calc(100% - var(--r) + var(--m));
}
article:nth-of-type(3) { /* 3rd */
left: calc(100% - var(--r) + var(--m));
top: calc( 0% - var(--r) - var(--m));
}
article:nth-of-type(4) { /* 4th */
left: calc( 0% - var(--r) - var(--m));
top: calc( 0% - var(--r) - var(--m));
} 这意味着凹角圆的中心位置取决于<article>元素之间的间距(这个间距是我们设置的margin: var(--m)的两倍),凹角的半径是--r。在一对水平和垂直的乘数因子分别是--i和--j,而且他们的最初的值都是-1。
对于第一行的两个<article>元素(第一行是一个2 x 2的网格),我们需要改变垂直方向的乘数因子--j为1,这样就可以让凹角的圆心在y轴上低于父容器底部边缘;而对于奇数的<article>(第一列),需要改变水平方向的乘数因子--i为1,这样就可以让凹角的圆心在x轴上位于父容器的右侧边缘。
/* multipliers initially set to -1 */
html {
--i: -1;
--j: -1
}
h3, section {
&:before {
/* set generic offsets */
top: calc((1 + var(--j)) * 50% - var(--r) + var(--j) * var(--m));
left: calc((1 + var(--i)) * 50% - var(--r) + var(--i) * var(--m));
}
}
@media (min-width: 2*($min-w + 2*$m)) {
article {
/* change vertical multiplier for first two (on 1st row of 2x2 grid) */
&:nth-of-type(-n + 2) {
--j: 1
}
/* change horizontal multiplier for odd ones (on 1st column) */
&:nth-of-type(odd) {
--i: 1
}
}
}注意,第一行的的两个凹角位置位于<article>中的<section>元素上,所以这两个元素的凹角使用的是<section>的伪元素::before;另第二行的两个凹角位置位于<article>中的<h3>元素上,因此这两个凹角用的是<h3>的伪元素::before。前两个元素的<h3>元素的::before的半径--r设置为0,后两个元素中<section>的::before的--r设置为0。@media (min-width: 2*($min-w + 2*$m)) {
article {
&:nth-of-type(-n + 2) h3,
&:nth-of-type(n + 3) section {
&:before {
--r: 0 ;
}
}
}
} 以类似的方式,我们为
<article>元素的子元素添加不同的样式:h3, section {
--p: .5rem;
padding: $p;
}
@media (min-width: 2*($min-w + 2*$m)) {
article {
&:nth-of-type(-n + 2) section,
&:nth-of-type(n + 3) h3 {
padding-right: calc(.5*(1 + var(--i))*(var(--r) - var(--m)) + var(--p));
padding-left: calc(.5*(1 - var(--i))*(var(--r) - var(--m)) + var(--p));
}
}
} 最终效果如下:

特别声明:今天使用CSS自定义属性,在媒体查询的条件中使用自定义属性踩了一个坑。那是因为我想在代码中统一使用CSS自定义属性来替代Sass这样处理器的变量。一直以为在CSS的媒体查询的条件中使用CSS自定义属性是OK的,结果实测代码的时候才发现不支持。最后查找了一下原因:
The
var()function can be used in place of any part of a value in any property on an element. Thevar()function can not be used as property names, selectors, or anything else besides property values. (Doing so usually produces invalid syntax, or else a value whose meaning has no connection to the variable.) —— From the spec
值得庆達的是,你可以使用PostCSS插件postcss-media-variables来做处理。感兴趣的可以自己试试。说实话,再一次感叹PostCSS的神奇之处和无所不能。
潜在的问题
上面的示例看上去完美,方法简单而又能跪浏览器兼容。或许你已经发现了,上例是在一个特定情况下想的结果,但很多时候我们总不是这么的幸运。哪一天需求一变,是不是还能如此轻易而又完美的实现呢?
首先,我们需要用一个伪元素来做这个凹角,当你只需要一个(比如上面看到的示例)或者两个的时候,都不是问题,但有的时候元素的四个角都需要这样的凹角时,那么我们就需要引入一个额外的元素。另外当你的伪元素被其他功能(比如Icon)占用时,你也不得不为此效果添加一个额外的标签元素。蛋疼了吧!
其次上面示例中的background是一个纯色,但我们不可能总是在使用纯色背景的场景中。如果我们想要一个半透明的或者渐变的背景,或者在一张背景图片之下,那么凹角将会成为我们的一个痛点,甚至会说,这个没法实现。
因此,我们需要探索其他更可靠的方案,并且也能让它得到众多浏览器的支持。
灵活性和良好的浏览器支持?是SVG?
想到SVG并不奇怪,但是如果我们想要灵活一点,浏览器兼容性全面一点,SVG可以说是一个最好的解决方案。在.box容器中包含了一个<svg>元素,而且放置在内容的前面。SVG中包含了一个<circle>元素,在这个元素上设置了r属性。
<div class='box'>
<svg>
<circle r='50'/>
</svg>
TEXT CONTENT OF BOX GOES HERE
</div> 让
svg相对于.box元素做相对定位,将将其大小设置为能完全覆盖父容器:.box {
position: relative;
}
svg {
position: absolute;
width: 100%;
height: 100%;
} 到目前为止,没有什么有趣的东西,所以给
<circle>添加一个id属性,并且使用SVG的<use>元素来复制多个id相同的<circle>:<circle id='c' r='50'/>
<use xlink:href='#c' x='100%'/>
<use xlink:href='#c' y='100%'/>
<use xlink:href='#c' x='100%' y='100%'/> 看到这里,是不是会觉得比使用
::before伪元素要来得简便,而且也非常方便,就算你要移去一个或多个凹角(示例效果的紫色部分),你只需要少使用几个<use>去克隆就行了。
从上例效果中可以看到,.box的四个角落都圆圈在那了,但这并不是我们想要的凹角,对吧!不要纳闷了,我们的做法是对的。请接着往下看。接下来要做的就是把这些圆圈放到一个<mask>中,给这个<mask>设置一个white的填充色(fill属性来搞定)。同时在其里面使用<rect>元素设置一个和SVG元素一样大小的矩形,主要用来覆盖整个SVG。然后我们在另一个再次使用<use>来调用这个已创建好的<mask>:
<mask id='m' fill='#fff'>
<rect id='r' width='100%' height='100%'/>
<circle id='c' r='50' fill='#000'/>
<use xlink:href='#c' x='100%'/>
<use xlink:href='#c' y='100%'/>
<use xlink:href='#c' x='100%' y='100%'/>
</mask>
<use xlink:href='#r' fill='#f90' mask='url(#m)'/> 最终效果如下:

同样的,我录制了一个动图来演示效果中的每一个元素:

特别注意:如果
.box中有内容,建议放置在svg元素之后。当然也可以放置在其前面,如果放置在前面,svg在做定位时,需要显式的设置top、right、bottom和left之类的值。至于为什么,这里不做过多的阐述,感兴趣的同学可以自己去深究其中的为什么。
如果.box有文本,需要把.box的padding的值和圆角的半径设置相同,同样的,如果使用了CSS自定义属性,可以使用JavaScript来控制它。最好把圆的半径和.box的padding使用同一个CSS自定义属性--r。这样会更好的控制一点:

我喜欢的就是CSS
或许你会说,我不懂SVG,我就是想使用CSS来实现。其实很高兴你能这样的深究与思考。事实上我们的确可以使用CSS来实现这样的效果。
遗憾的是,使用CSS的方案目前为止并不是所有浏览器都能支持,但使用CSS让我们把事情变得更简化,而且在不远的将来,它们肯定是能得到众多浏览器支持的。
在HTML元素上使用CSS的mask
这里我们移除SVG所有的东西,然后使用CSS,可以在.box元素上设置一个background(可以是一个纯色、半透明、渐变、图像或者多背景,甚至是你任何你想要的CSS)和mask属性。
.box {
/* any kind of background we wish */
mask: url(#m);
} 
使用CSS设置圆半径
这意味着,需要把<circle>中的r属性删除,然后在CSS中给其设置半径的大小,这里设置的半径大小与.box容器的padding值一样:
.box {
padding: var(--r);
}
[id='c'] {
r: var(--r);
} 这样一来,如果我们改变半么--r的值,凹角的大小和.box的padding也会随着更新。
注意,在CSS中给SVG元素设置几何属性只在Blink浏览器中有效!
结合前两种方法
虽然这很酷,但遗憾的是目前在任何浏览器中都看不到效果。但值得庆幸的是我们可以做得更好!
使用渐变来做朦层
Note that CSS masking on HTML elements doesn't work at all in Edge at this point, though it's listed as "In Development" and a flag for it (that doesn't do anything for now) has already shown up in
about:flags.
由于我们需要完全抛弃SVG,所以我们需要使用CSS的渐变为mask做些事情。这里将使用CSS径向渐变来画圆,下面就是CSS绘制的一个半径为--r的圆,并且这个圆位于.box的左上角。
.box {
background: radial-gradient(circle at 0 0, #000 var(--r, 50px), transparent 0);
} 如果您对CSS的渐变不太了解,建议您花点时间阅读这几篇文章:《再说CSS3渐变:线性渐变》、《再说CSS3渐变:径向渐变》、《 为什么要使用
repeating-linear-gradient》、《 你真的理解CSS的linear-gradient?》。
这个时候你可以看到像上面这样的一个效果:
接下来在mask使用相同的渐变:
.box {
/* same as before */
/* any CSS background we wish */
mask: radial-gradient(circle at 0 0, #000 var(--r, 50px), transparent 0);
} 注意,Webkit浏览器仍然需要给
mask属性添加-webkit-前缀。如果你不知道mask怎么使用,建议你花点时间阅读《如何在CSS中使用遮罩》一文。因为后面很多内容都会涉及到这个属性,这样能帮助更好的理解后续的内容。
给.box每个角落都添加渐变绘制的圆:
$grad-list: radial-gradient(circle at 0 0 , #000 var(--r, 50px), transparent 0),
radial-gradient(circle at 100% 0 , #000 var(--r, 50px), transparent 0),
radial-gradient(circle at 0 100%, #000 var(--r, 50px), transparent 0),
radial-gradient(circle at 100% 100%, #000 var(--r, 50px), transparent 0);
.box {
/* same as before */
/* any CSS background we wish */
mask: $grad-list
}看到上面的代码是不是感觉要崩溃了,有太多重复的代码要写,其实我们使用一个CSS自定义属性--stop-list可以让我们把事情简化不少:$grad-list: radial-gradient(circle at 0 0 , var(--stop-list)),
radial-gradient(circle at 100% 0 , var(--stop-list)),
radial-gradient(circle at 0 100%, var(--stop-list)),
radial-gradient(circle at 100% 100%, var(--stop-list));
.box {
/* same as before */
/* any CSS background we wish */
--stop-list: #000 var(--r, 50px), transparent 0;
mask: $grad-list;
} 上面这样做还不是很好,可以借助CSS处理器的循环特性来做,会更好一些:
$grad-list: ();
@for $i from 0 to 4 {
$grad-list: $grad-list,
radial-gradient(circle at ($i%2)*100% floor($i/2)*100%, var(--stop-list));
}
.box {
/* same as before */
/* any CSS background we wish */
--stop-list: #000 var(--r, 50px), transparent 0;
mask: $grad-list;
} 就代码而言,这样写已经很完美了。因为我们不需要多次编写,并且以后在任何地方使用都不需要做任何更改。但到目前为止的结果并不是我们想要的:
从上面的示例中可以看出,我们除了凹角部分之外的东西都剪切掉了,这正好和我们想要的东西相反。要得到我们想要的效果,咱们只需要做一件事情,把渐变反过来。让凹角的圆变成透明,剩余的部分全部是黑色。
--stop-list: transparent var(--r, 50px), #000 0;
需要注意的是,如果我们只使用一个渐变的时候,那么上面的代码就帮我们解决了问题:
但是,当我们把所有的四个圆圈(甚至两个)都堆起来的时候,就会得到一个黑色的矩形,这个矩形的大小相当于我们的mask的大小,这意味着没有任何东西会被掩盖掉。
因此,我们需要把每个渐变的大小限制在盒子的四分之处(width的50%和height的50%),从而得到25%的面积:

这个意思就是,我们需要设置mask-size的值为50% 50%,同时mask-repeat的值为no-repeat以及每个mask-image自身的位置。
$grad-list: ();
@for $i from 0 to 4 {
$x: ($i%2)*100%;
$y: floor($i/2)*100%;
$grad-list: $grad-list
radial-gradient(circle at $x $y, var(--stop-list)) /* mask image */
$x $y; /* mask position */
}
.box {
/* same as before */
/* any CSS background we wish */
--stop-list: transparent var(--r, 50px), #000 0;
mask: $grad-list;
mask-size: 50% 50%;
mask-repeat: no-repeat;
}
但这里有一个大问题,一般情况下,我们的四分之一计算的每个部分会经过四舍五入,那么这四个部分重新组合在一起的时候,width和height都有可能产生间距。如下图所示:
好吧,我们不能用linear-gradient()来做这个线条或者说把mask-size的尺寸增加到51%。比如下面的这个示例,增加了mask-size的尺寸来处理四个渐变区载之间的间距。
但是,难道没有更优雅的方式来处理这个间距?不是的,可以使用mask-composite属性来帮我们处理。当我们返回全部渐变的全尺寸时,可以把mask-composite的值设置为intersect。
$grad-list: ();
@for $i from 0 to 4 {
$grad-list: $grad-list,
radial-gradient(circle at ($i%2)*100% floor($i/2)*100%, var(--stop-list));
}
.box {
/* same as before */
/* any CSS background we wish */
--stop-list: transparent var(--r, 50px), #000 0;
mask: $grad-list;
mask-composite: exclude;
}
这非常酷,因为它是纯CSS的解决方案,没有使用任何SVG代码,但不幸的是,目前得能看到效果的也仅限于Firefox53+。
corner-shape
大约在五年前,@Lea Verou提出了一个想法,甚至还为它创建了一个预览页面。遗憾的是,它不仅没有被任何浏览器实现,而且在此期规范还没有得到很大的提高。对于未来,它仍然是值得期待的,因为它提供了很多灵活性,而且代码非常少。比如说,实现我们前面所说的效果,只需要以下几行代码:
padding: var(--r);
corner-shape: scoop;
border-radius: var(--r);
就是一个非常简单的CSS。是不是值得期待,但最终还是要看浏览器什么时候会对其支持。
CSS Houdini
CSS Houdini慢慢的开始进入大家的世界当中,试问一下,我们使用CSS Houdini是不是可以更方便的实现这个内凹角的效果呢?比如像下面这样的一个效果,它就是使用CSS Houdini实现的:
咱们不仿尝试一下使用Paint Worklet或者CSS Paint API来实现呢?请开动你的大脑,动手撸一撸。希望您能把你的成果在下面的评论中与大家一起分享?如果你感兴趣,也可以在下面的评论中留言,我们后续可以专门花一点时间来看看CSS Houdini可以实现内凹角的效果,甚至是前面所讲的斜外切口的效果。
构建一个内凹角的Vue组件
记得在《使用Vue制作切口盒子组件》一文中,咱们就尝试使用Vue构建了一个斜外切口的Vue组件c-noth:
那么我们来看看怎么使用Vue来构建一个内凹角的组件,具体代码如下:
特别声明,如果您的浏览器没有看到任何效果,请使用Firefox 53+浏览器查阅。具体原因,前面文章已经介绍过来了。
为了照顾其他同学查看最终的效果,我录了一个屏:
这就是最终的效果。由于我自己是Vue的初学者,现在有一个病,看到什么东西都想用Vue来写,而且想封装成一个组件。如果写得不好,或者有更好的方案,欢迎大家指点,并且希望能看到您的分享的成果。如果你和我一样,也是Vue的一个初学者,可以和我一起来学习Vue。整理了一些有关于Vue的学习笔记,希望大家能喜欢,更希望能帮助到初学者,同时也希望不会误人子弟。
总结
文章开头抛出了怎么实现内凹角的一个效果。首先从CSS的box-shadow着手,使用CSS的box-shadow可以轻易的实现内凹角的效果,但这个方案有一定的局限性,比如要多个内凹角时,需要通过增加元素标签来实现,特别是在面对渐变,或者有背景图像和半透明的情景之下,这个方案基本上无法来满足我们的需求。
接着探索了SVG的方案,通过SVG的mask和use之类的一些独有的特性,可能灵活的帮助我们实现想要的效果,而且能做到box-shadow无法做到的事情。特别是通过CSS自定义属性来修改SVG的属性,让事情变得更具灵活性,只不过部分浏览器还不支持CSS来修改SVG的属性,这算是其中的一个坑吧。不过我们还是可以规避掉的。
虽然SVG能实现想要的效果,但对于一位CSS执着者而言,总是希望不借助其他的外力,通过纯CSS来实现这个效果,事实上也是可以的,使用CSS的径向渐变和mask相关的知识,可以实现我们想要的效果。遗憾的是,目前众多浏览器对mask还是有所保留,未能全面支持。比如文中提到的,很多mask相关的特性,仅能在Firefox 53+上看到。包括咱们写的示例,有些仅能在Firefox上看到。
随着CSS Houdini技术越来越成熟,我在试想,是否可以通过CSS Houdini来实现。正如@Lea Verou五年前提出的corner-shape属性。我想是可以的,后面可以尝试动手写写。当然,CSS Houdini虽然还没有得到所有浏览器支持,但这并不防碍我们去尝试着写各种效果。有兴趣的一起动手写写,看看这个想法是否能成真。
最后为了能练习Vue相关的知识,尝试使用Vue封装了一个简单的凹角组件。写得比较拙逼,希望能得到大神的指点。
最后的最后,需要特别感谢@ANA TUDOR写了这么优秀的教程。我在原作者的基础上做过一些调整,如果你觉得这里整理和不好,可以查阅原文。
大漠
常用昵称“大漠”,W3CPlus创始人,目前就职于手淘。对HTML5、CSS3和Sass等前端脚本语言有非常深入的认识和丰富的实践经验,尤其专注对CSS3的研究,是国内最早研究和使用CSS3技术的一批人。CSS3、Sass和Drupal中国布道者。2014年出版《图解CSS3:核心技术与案例实战》。
原文发布时间为:2018年04月23日
原文作者:掘金
本文来源: 掘金 如需转载请联系原作者


