剪裁和遮罩都是用来隐藏元素的一些部分、显示其他部分的。当然了,这两者还是有区别的。区别主要在于这几方面:他们能做的东西,不同的语法,涉及到的不同技术,是新的还是旧的,以及浏览器支持的差异。

但不幸的是总是有相当多的过时信息在那儿,所以一起来看看能否解决这个问题。

剪裁和遮罩之间的区别

遮罩使用的是图像,剪裁使用的是路径。

想象一张从左到右、从黑到白渐变的正方形图像,它可以是一个遮罩。对于应用了这个渐变遮罩图像的元素,它在遮罩图像的黑色部分是透明(透视)的,而在遮罩图像的白色的部分是不透明(正常)的。所以作出的结论是:这个元素是从左到右淡入的。

而剪裁一直都是矢量路径的。路径之外的部分是透明的,路径里边的部分是不透明的。

个人觉得有点混乱。因为很多时候可能会碰到某个关于遮罩的教程用的是一个在黑色上有白色矢量形状的遮罩图像,这和剪裁基本是同一个原理。但这还好,它只是混淆了一点东西。

旧的/不推荐使用的clip

clip 属性是CSS中出现的第一种剪裁(除了 overflow: hidden;,它不是真正的剪裁)。(MDN)。

它的示例如下:

.element {
  clip: rect(10px, 20px, 30px, 40px);
}

这四个值和margin/padding属性的值是同样的顺序(上右下左):

  • 10px 为到元素顶部的距离
  • 20px 为到元素右边的距离
  • 30px 为到元素底部的距离
  • 40px 为到元素下边的距离

在CSS中, clip 属性是已过时的,也就是说它已经不再建议被使用,因为有一个更新的、规范的版本,各个浏览器也将集中努力使用它。

clip也是有一些优势的:因为clip是运行在浏览器中的,它可能会一直有效。而浏览器对它的支持是非常强大的:几乎是有史以来的每一个浏览器。另外,我也听说过了,它作出的动画效果胜过其它的新方法。

但是比起它的优势,clip有两个更为重要的弱点,这也使得它难以被广泛地使用:

  • clip 只对绝对定位的元素有效;
  • clip 只能用于矩形,即rect()函数。

这真的是非常大的限制!所以来让我们接着说。

新的 clip-path

在CSS中新的、推荐使用的应用于剪裁元素的版本是 clip-path ,你可能认为它的代码应该是这样简单的:

CSS

.element {
  /* NOPE */
  clip-path: rect(10px, 20px, 30px, 40px);
}

但这行不通(即使作为前缀,或任何地方),最终我们还是会得到 rectangle()所以还不是时候

Postponed rectangle() to level 2

新的解决方法是使用inset():

.element {
  clip-path: inset(10px 20px 30px 40px);
  /* Also can take single values to make all sides the same, or 2 values (vert/horz), or 3 values (top/horz/bottom). */
}

你会注意到各个值的中间没有用逗号隔开,语法也不一样,但是最终做的东西还是一样的。

那么 clip-path 还可以用来做什么东西呢(在一些浏览器中)?圆、椭圆以及多边形。下面是一些示例:

.clip-circle {
  clip-path: circle(60px at center);
  /* OLD VALUE example: circle(245px, 140px, 50px); */
  /* Yep, even the new clip-path has deprecated stuff. */
}
.clip-ellipse {
  clip-path: ellipse(60px 40px at 75px 30px);
  /* OLD VALUE example: ellipse(245px, 80px, 75px, 30px); */
}
.clip-polygon {
  clip-path: polygon(5% 5%, 100% 0%, 100% 75%, 75% 75%, 75% 100%, 50% 75%, 0% 75%);
  /* Note that percentages work as well as px */
}

多边形是非常强大的。Ryan Scherf 在CSS-Tricks上发布了一个以编程方式使用多边形生成简略效果的教程

我强烈建议大家去Bennet Feely的 Clippy 网站上玩一下这个东西:

CSS中的剪裁和遮罩

从理论上讲,这是 clip-path 将会支持的(作为它的"基本形状"):

.clip-examples {

  clip-path: rectangle(x, y, width, height, rounded-x, rounded-y)

  clip-path: inset-rectangle(from top, from right, from bottom, from left, rounded-x, rounded-y)
  /* Looks like this is what rect() used to be like with clip */
  /* Will replace inset(), I suppose */

  clip-path: polygon(a, bunch, of, points)
  clip-path: circle(radius at x, y)
  clip-path: ellipse(radius-x, radius-y at x, y)

}

我似乎难以找到关于path()是否会永远有效的信息

通过SVG定义的<clipPath>标签来使用clip-path

你不需要在CSS中定义clip-path的值,因为它能够引用SVG中定义的 <clipPath> 标签元素。下面是它的使用示例:

html

<img class="clip-svg" src="harry.jpg" alt="Harry Potter">

<svg width="0" height="0">
  <defs>
    <clipPath id="myClip">
      <circle cx="100" cy="100" r="40" />
      <circle cx="60" cy="60" r="40" />
    </clipPath>
  </defs>
</svg>

css

.clip-svg {
  clip-path: url(#myClip);
}

Demo:

Sara Soueidan也有个这样的示例

但是使用SVG定义的 clip paths 有个相当严重的问题:它们都被固定在文档的左上角。下面是这个问题的一个示例:

我可能只是忘了什么东西,但是我没有找到一个明确的方法能让路径随着应用了它的元素一起移动,就像当你直接使用CSS中的一个基本元素时,clip-path运行的方式。

clip-path 的动画/过渡

当你声明了一个基本形状作为一个clip-path路径时,你可以将它变成动画!Dirk Schulze 有一篇涉及这一方面的精彩文章,下面是它的简单示例效果:

这有一个简单的代码样例:

CSS

div {
  transition: 0.4s cubic-bezier(1, -1, 0, 2);
  clip-path: polygon(50% 5%, 0% 100%, 100% 100%);
}
div:hover {
  clip-path: polygon(50% 19%, 0 76%, 100% 76%);
}

来尝试一下:

Masking

Masking是只有webkit才有的版本,你可以通过衔接一个栅格图像或者定义一个渐变作为遮罩。它的代码是这样写的:

CSS

img {
  width: 150px;
  -webkit-mask-image: -webkit-gradient(
    linear, left top, right bottom, 
    color-stop(0.00,  rgba(0,0,0,1)),
    color-stop(0.35,  rgba(0,0,0,1)),
    color-stop(0.50,  rgba(0,0,0,0)),
    color-stop(0.65,  rgba(0,0,0,0)),
    color-stop(1.00,  rgba(0,0,0,0)));
}

据我所知,这是不推荐使用的。因为渐变语法绝对不推荐使用,但当我把它改成新的语法时,它就无效了。所以呀,它可能被弃用,但是目前仍然可用:

另外,遮罩催生了像WebKit图像擦除这样的现在仍然有效的旧版教程(你懂的,在Blink/Webkit领域还在使用)。

在我查到的更现代的参考文献里,只提及了遮罩在SVG中被定义以及在CSS中被ID或URL引用。下面是一个例子,两种方式。mask定义在SVG中,在左边,image图片是作为SVG中的一个标签;在右边,遮罩是被应用于HTML中的img标签元素(右边的图目前只能Firefox浏览器中看到)。

在Firefox中也可以看一下这个示例,这是从Dirk Shulze的文章中取出来的示例代码。这种东西目前还有有点危险的,因为它不能运行于Webkit/Blink,它会完全删除它应用的元素。

你也可以将整个SVG文件链接起来作为遮罩,就像下面这样:

CSS

.mask {
  mask: url(mask.svg);
}

遮罩类型

CSS

.mask {
  mask-type: luminance; /* white = transparent, grays = semi-transparent, black = opaque */
  mask-type: alpha; /* transparent areas of the image let image through, otherwise not */
}

边界遮罩

这和CSS中border-image 的运行方式非常相像。你定义一个SVG图像,然后应用 nice-slice scaling 样式。像一个井字游戏板上的图像。在拐角处使用拐角,边缘(可)沿边缘重复,中间(可)在中间延展。下面是基础的使用示例:

CSS

.border-mask {
  /* Note that the properties aren't quite the same */
  -webkit-mask-box-image: url(stampTiles.svg) 30 repeat;
  mask-border: url(stampTiles.svg) 30 repeat;
  /* Image, again, from http://www.html5rocks.com/en/tutorials/masking/adobe/ */
}

下面是普通遮罩vs边缘遮罩的示例:

浏览器支持

这很难去简明扼要地总结,因为他们不同的属性,甚至在所有的地方,value有不同的支持级别。且不说你如何使用、在哪些方面使用它们。这还是一片荒野,所以我建议把它们作为积累提升,努力做到你目前能做到的最好的地步。这可能有点困难,因为甚至连Modernizr的检测功能也还没有包括这一方面。

正如加前缀说的:在几乎所有东西上都记得非前缀以及 -webkit- 前缀一起使用。

扩展阅读

本文根据@Chris Coyier的《Clipping and Masking in CSS》所译,整个译文带有我们自己的理解与思想,如果译得不好或有不对之处还请同行朋友指点。如需转载此译文,需注明英文出处:https://css-tricks.com/clipping-masking-css/

译文:http://www.w3cplus.com/css3/clipping-masking-css.html

最后更新于 2015-04-03