这篇文章来自于 Slicing SVG 9 Ways ,提供的思路方法确实不错,学习了下,文章没有逐字逐句翻译。主要是学习核心的思路和方法。

最近在一个项目中碰到一个问题,如下图所示:

demo地址

是一个卡片翻转的效果,仔细观察在卡片翻转的同时,在卡片的底部有一个阴影效果,阴影在卡片翻转过程中主要以下两个变化:

  • 在卡片翻转的过程中阴影会随着卡片尺寸变化有个位移的变化。
  • 阴影的尺寸也要跟随卡片的尺寸自适应。
  • 当然要实现这样的效果,有很多方法。但是要同时兼顾性能和自适应的方法却不是很多。

    我们要找的方案要同时能够兼顾下面这两个方面:

  • 性能友好 。这个可以通过使用改变阴影的 transform opacity 属性来实现,这两个属性的变化会直接使用设备的 GPU 来做渲染处理。
  • 维护性好 。即阴影可以非常方便的通过代码来维护,如改变阴影的颜色等。
  • 解决方案1:通过 box-shadow 来实现

    首先想到可能就是通过使用 box-shadow 来实现。简简单单一行代码就可以实现阴影效果。

    不过由于 box-shadow 是直接作用于元素本身,这意味着如果你想通过改变阴影的 blur 或者是它的 X Y 值来实现阴影的动画效果。会触发网页不停的重绘,从而会影响网页的性能。

    当然还可以通过利用改变元素伪元素如 :after 或者是 before 阴影的opacity来实现,但是灵活性却不是很好。

    解决方案2:通过滤镜来实现(Blur Filter)

    还有一种方法是通过使用CSS滤镜来实现即Blur Filter。比如 filter:blur(12px)

    比如下图所示:

    正常的图:

    使用滤镜后:

    我们可以通过控制滤镜的尺寸来实现阴影的动画效果,不过它和 box-shadow 同样有一个问题即改变它的相关值,会触发网页不停的重绘,从而会影响网页的性能,特别是在移动端更要注意性能的问题,稍有不剩,就会卡到爆。看来这也不是一个很好的方法。

    解决方案3:SVG滤镜

    在SVG中,使用滤镜也非常方便,通过下面一段代码就是用SVG中的高斯模糊滤镜来实现的一个模糊的效果:

    version = "1.1" xmlns = "http://www.w3.org/2000/svg" width = "112" height = "112" > <!-- Define the blur --> < filter id = "blur-2px" > < feGaussianBlur in = "SourceGraphic" stdDeviation = "2" /> </ filter > </ defs > <!-- Make a rect that uses it --> < rect filter = "url(#blur-2px)" stroke = "none" fill = "#000000" x = "6" y = "6" width = "100" height = "100" > </ rect > </ svg >

    在高斯模糊滤镜中, stdDeviation 使用来控制模糊程度的参数,数字越大越模糊。

    实际如下图所示:

    并且通过改变filter的相关属性很容易来控制模糊的效果,比如模糊的程度。如果直接的增大 stdDeviation 的值来改变模糊程度,我们可能会得到下面的效果,滤镜效果被剪裁了:

    发生了什么,在W3C官网有这样一段描述:

    The bounds of this rectangle act as a hard clipping region for each filter primitive included with a given ‘filter’ element; thus, if the effect of a given filter primitive would extend beyond the bounds of the rectangle (this sometimes happens when using a ‘feGaussianBlur’ filter primitive with a very large ‘stdDeviation’), parts of the effect will get clipped. - SVG Filter Effects Spec

    主要的意思是:就是讲滤镜的效果区域。这些属性定义了滤镜起作用的矩形区域。滤镜效果不会应用在超过这个区域的点上。

    在滤镜中,x,y的默认值是-10%,width与height的默认值是120%。所以如果你指定滤镜模糊程度的值超过默认的120%即滤镜起作用的区域,就会出现被剪裁的效果。

    要不被剪裁,就需要相应的设置x,y以及width与height的值。

    译者注 :其实在滤镜中还有一个重要参数需要设定即filter的 filterUnits 的值, userSpaceOnUse 表示使用引用该filter元素的元素的用户坐标系统。如果不设置的话,那它的值默认为 objectBoundingBox 表示使用引用该filter元素的元素的包围盒的百分比做取值范围。

    原作者文章中出现被剪裁的效果就是因为没有设置 filterUnits 的值为 userSpaceOnUse 而导致的,设置下这个值就不会出现剪裁了。

    可以去这里看看实际例子: demo

    下面是重头戏了,来说说怎么使用高斯模糊滤镜来实现阴影动画效果。

    border-image

    我们这里会使用 border-image 这个属性来实现阴影的效果,具体关于 border-image 的详细说明可以去 这里 看看。

    具体来我会在html中编写一个类名为shadow的元素来实现阴影效果,CSS如下:

    .shadow {
      position: absolute;
      width: calc(100% + 12px);
      height: calc(100% + 12px);
      left: -6px;
      top: -6px;
      opacity: 0.3;
      box-sizing: border-box;
      border-style: solid;
      border-width: 18px;
      border-image: url(images/shadow-2px.svg) 18 fill stretch;
            

    简单说明下代码:

  • 这里设置元素的定位属性为绝对定位,宽高相对于父元素为100%,当然还需要把阴影的模糊值计算进去,这里模糊的值是12px,所以使用来calc属性来计算元素的真实的宽高。
  • 使用border-box。这样可以改变盒模型的计算方式,border的宽度不会被计算到元素的宽高中去。
  • 设置元素的边框为18px,下面的border-image要用到。
  • 下面来说下border-image是怎么来填充18px边框的:

    border-image: url(images/shadow-2px.svg) 18 fill stretch;
                
  • 首先是引入已经制作好的SVG图片url(images/shadow-2px.svg)
  • 18:用来设置边框的宽度,其单位是px,其实就像border-width一样取值,可以使用1~4个值,其具体表示四个方位的值,可以参考border-width的设置方式。
  • stretch:用来设置边框背景图片的铺放方式,类似于background-position,其中stretch是拉伸,repeat是重复,round是平铺,stretch为默认值。
  • fill:这里要说下border-image-slice指定边框图像顶部、右侧、底部、左侧内偏移量。没有具体的单位值,只要给一个单纯的数字即可,当然也可以按照百分比来给设置值。作用就是把边框图像切成9个区域:4个角、4边区域和一个中间部位,即9宫格,如果不应用fill这个可选属性值的话,那么中间第九块格子被当做透明不见。所以这里使用fill属性,指定在没有边框图片的中间部分用模糊图片来填充。
  • 所以这里使用SVG配合border-image属性来实现了阴影的效果,并且可以通过改变阴影元素的相关值可以实现一些动画效果,性能也非常不错。当然更重要的是可维护性也不赖,可以通过编辑SVG文件来轻松的改变阴影的尺寸或者是颜色。

    最后来看下一些使用这种方法来实习的阴影效果:

    具体代码可以在这里查看

    Emptied
    私信
     4,187