纯CSS实现圆角阴影的折角效果

 2016-10-04 13:14

把元素的一角处理类似折角的形状,再配上或多或少的修饰,这种效果已经成为一种非常流行的装饰手法。

目前,我们已经拥有一些使用纯CSS实现的方案,其中某些技巧早在2010年就由伪元素大师Nicolas Gallagher发表了。这些方法的基本原理通常是在右上角增加两个三角形:一个三角形用来体现折页的形状,另外一个白色的三角形遮住元素的一角,用来模拟翻折所产生的缺角。这两个三角形通常都是由经典的边框技巧来生成的。


尽管这些方法在过去光彩夺目,但在今天看来却有一些局限性,而且在以下场景中还会暴露出明显的缺陷:

  • 当折角元素之下的背景不是纯色,而是一幅图案、一层纹理、一张照片、一幅渐变或者其他一种背景图案时;

  • 当我们想要一个45°以外的折角时;

45°折角方案

我们会从一个右上角具有斜面切角的元素开始,在之前的“切角效果”我们已经讲述过方法。通过一个渐变来实现缺角的效果:

background:#58a;/* 回退机制 */
background:linear-gradient(-135deg,transparent 2em,#58a 0);


接下来需要的增加一个暗黑色的三角形来实现翻折效果。实现方法也是通过一层渐变来生成这个三角形,并将其定位在右上角,通过background-size来控制折角的大小。

为了生成这个三角形,我们所需要就是一个有角度的线性渐变,而这个渐变的两个色标需要在正中间重合:

background:linear-gradient(to left bottom,
    transparent 50%,rgba(0,0,0,.4) 0)
     no-repeat 100% 0 / 2em 2em;


最后,我们把两层组合起来,应该可以收工了吧?我们来试试看:

background:#58a;/* 回退机制 */
background:
    linear-gradient(to left bottom,
        transparent 50%,rgba(0,0,0,.4) 0) 
        no-repeat 100% 0 / 2em 2em,
    linear-gradient(-135deg,transparent 2em,#58a 0);


奇怪,为什么会这样?

原因在于第二层渐变中的2em折角是写在色标中的,因此它是沿着渐变轴进行度量的,是对角线尺寸。另一面,在background-size中的2em长度是背景贴片的宽度和高度,是在水平和垂直方向上进行度量的。

为了将两者对齐,我们需要选择以下任意项进行调整,选择哪一项取决于我们最终想保留哪一方面的尺寸设置。

  • 如果想保留对角线的2em长度,就要将background-size乘以√2;

  • 如果想保留水平和垂直方向上的2em长度,就要用切角渐变的角标位置除以√2。

由于background-size需要把这个长度重复两次,而且绝大多数的CSS度量都不是在对角线上进行的,因此第二种方案更合适。色标的位置值将变成2/√2=√2≈1.414213562;

background:#58a;/* 回退机制 */
background:
    linear-gradient(to left bottom,
        transparent 50%,rgba(0,0,0,.4) 0) 
        no-repeat 100% 0 / 2em 2em,
    linear-gradient(-135deg,transparent 1.414213em,#58a 0);


其他角度的解决方案

现实中的折角往往不是精准的45°,如果我们希望它看起来更真实一些,可以稍微改变下角度,比如-150deg可以产生30°的切角。那么显然,第二层肯定不会跟着变化,这会我们又需要请出三角函数了。

20161004145047.png

本人数学不是很好,大家请自己算吧 = =!都是初中时候教的,勾股定理,正弦和余弦函数之类的。

我们把角度计算改变下,变成了这样:


但细心的人会发现,这样的效果和现实中真实的折纸效果(如下图)是不一样的,它不够真实。

现实中的折角效果

现实中的折角是微微旋转的,它的尺寸和我们从元素角上切下来的那个三角形是一致的。由于我们无法旋转背景,就只能找到伪元素来做了。

.zhejiao{
    position:relative;
    background:#58a;/*回退效果*/
    background:linear-gradient(-150deg,transparent 1.5em,#58a 0);
}
.zhejiao::before{
    content: '';
    position: absolute;
    top: 0;right: 0;
    background: linear-gradient(to left bottom,
        transparent 50%,rgba(0,0,0,.4) 0) 100% 0 no-repeat;
    width:3em;
    height:1.73em;
}

到这里,我们把原来由背景一次性做完的东西放置到了伪元素层,下一步要把折页的三角形的width和height对调,以此改变它的方向,这样就可以得到跟折角页缺口对称的三角形,而不是一个可以补足折页缺口的三角形。然后,我们再以逆时针旋转30°{(90°-30°)-30°}。这样可以让它的斜边与折线平行:

.zhejiao::before{
    content: '';
    position: absolute;
    top: 0;right: 0;
    background: linear-gradient(to left bottom,
        transparent 50%,rgba(0,0,0,.4) 0) 100% 0 no-repeat;
    width:1.73em;
    height:3em;
    transform:rotate(-30deg);
}


现在可以看到,我们得到了这个一样形状,但要把它移到准确的位置上,通过计算尺寸是很困难的。因此,我们把transform-origin设置为bottom right,让三角形右下角成为旋转的中心,这样就可以让它的右下角保持固定。然后再计算出垂直方向上的偏移量就可以了。


.zhejiao::before{
    content: '';
    position: absolute;
    top: 0;right: 0;
    background: linear-gradient(to left bottom,transparent 50%,rgba(0,0,0,.4) 0) 100% 0 no-repeat;
    width:1.73em;
    height:3em;
    transform:translateY(-1.279em) rotate(-30deg);
    transform-origin:bottom right;
}

有个需要注意的是,确保需要把translateY(-1.279em)放置在变形之前,否则三角形就会在30°方向上进行位移了。

修饰并增加圆角和阴影效果

有时候,我们会需要更加真实点的效果,比如增加圆角、渐变以及投影!那么,我们修饰下代码,增加这些内容进去:

.zhejiao{
    position:relative;
    background:#58a;
    background:linear-gradient(-150deg,transparent 1.5em,#58a 0);
    border-radius: .5em;
}
.zhejiao::before{
    content: '';
    position: absolute;
    top: 0;right: 0;
    background: linear-gradient(to left bottom,
        transparent 50%,
        rgba(0,0,0,.2) 0,
        rgba(0,0,0,.4)) 100% 0 no-repeat;
    width:1.73em;
    height:3em;
    transform:translateY(-1.3em) rotate(-30deg);
    transform-origin:bottom right;
    border-bottom-left-radius: inherit;
    box-shadow:-.2em .2em .3em -.1em rgba(0,0,0,.15);
}


处理器@mixin(不完全支持)

最终效果看来很不错,但代码不够DRY,让我们来看一看,如果对样式做些改动或对效果做些微调,会是什么情况?

  • 如果要改变元素的宽度或者其他尺寸(比如内边距等),只需要修改一处。

  • 如果要改变元素的背景色,则需要改变两处;

  • 如果要修改页面的大小,需要修改四处,并做一些复杂的计算;

  • 如果要修改折页的角度,则需要修改五处,并更加复杂了。

看来我们要清楚预处理器mixin了:

@mixin folded-corner($background,$size,$angle:30deg){
    position:relative;
    background:$background;
    background:linear-gradient($angle - 180deg,
        transparent $size,$background 0);
    border-radius: .5em;

    $x:$size / sin($angle);
    $y:$size / cos($angle);

&::before{
    content: '';
    position: absolute;
    top: 0;right: 0;
    background: linear-gradient(to left bottom,
        transparent 50%,
        rgba(0,0,0,.2) 0,
        rgba(0,0,0,.4)) 100% 0 no-repeat;
    width:$y;height:$x;
    transform:translateY($y-$x) rotate(2*$angle - 90deg);
    transform-origin:bottom right;
    border-bottom-left-radius:inherit;
    box-shadow:-.2em .2em .3em -.1em rgba(0,0,0,.15);
}
}
/*当调用时...*/
.note{
    @include folded-corner(#68d,1.3em,60deg);
}


作者头像

作者:紫铜炉

自由博主,网页设计师。我关注科技产品和个人博客发展,注重用户体验和界面优化。

 发表评论:

 评论列表

唐朝 2016-10-04  16:50

[/打酱油]

信心 2016-10-04  16:32

[/拳头]