Recorte y enmascaramiento en CSS

Ambas cosas se usan para ocultar algunas partes de elementos y mostrar otras partes. Pero hay, por supuesto, diferencias entre los dos. Diferencias en lo que pueden hacer, diferencias en sintaxis, diferentes tecnologías involucradas, nuevas y obsoletas, y diferencias en el soporte del navegador.

Y lamentablemente hay bastante información desactualizada. Veamos si podemos solucionarlo.

 

# La diferencia entre el recorte y el enmascaramiento

Las máscaras son imágenes; Los clips son caminos.

Imagine una imagen cuadrada que es un degradado de izquierda a derecha, negro a blanco. Eso puede ser una máscara. El elemento al que se aplica será transparente (transparente) donde haya negro en nuestra imagen de máscara de degradado y opaco (normal) donde haya blanco. Entonces, el resultado final será un elemento que se desvanece de izquierda a derecha.

Los clips son siempre caminos de vector. Fuera el camino es transparente, dentro de la ruta es opaco.

Personalmente, me pareció confuso, porque a menudo te topas con un tutorial sobre enmascaramiento que utiliza una imagen enmascaradora que una forma blanca con aspecto de vector en negro, por lo que básicamente está haciendo lo mismo que un clip. Está bien, solo confunde las cosas un poco.

# The Old / Deprecatedclip

La primera presencia de recorte en CSS (que no sea overflow: hidden;engaño) era la clippropiedad. ( MDN ).

Fue así:

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

Esos cuatro valores están en el mismo orden que margen / relleno:

  • 10px desde la parte superior del elemento
  • 20px desde la derecha del elemento
  • 30px desde la parte inferior del elemento
  • 40px desde la izquierda del elemento

La clippropiedad está en desuso en CSS , lo que significa que su uso no se recomienda porque hay una versión más nueva y estandarizada que los navegadores realmente enfocarán sus esfuerzos.

Hay algunos puntos fuertes del clip: porque se clipenvía en navegadores, probablemente siempre funcione. Y el soporte del navegador es extremadamente fuerte: casi todos los navegadores. Además, he oído que el rendimiento de la animación supera los métodos más nuevos.

Sin embargo, hay dos debilidades bastante significativas de clip, que hace que no sea más fácil de tragar:

  • clip solo funciona si el elemento está absolutamente posicionado
  • clip solo puede hacer rectángulos.

Bastante limitado. Así que sigamos adelante.

# The Newclip-path

La nueva versión recomendada de aplicar clipping a los elementos en CSS es clip-path. Uno pensaría que sería tan simple como:

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

Sin embargo, eso no funciona (incluso con prefijo, en cualquier lugar). Eventualmente, lo conseguiremos rectangle(), solo que todavía no :

Rectángulo pospuesto () al nivel 2

La nueva forma de hacerlo es con inserción ():

.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). */
}

Tenga en cuenta que no hay comas, y la sintaxis es diferente , pero al final hace el mismo tipo de cosas.

Sinclip-path embargo, con lo que también funciona (en algunos navegadores), son los círculos, las elipsis y los polígonos. Aquí hay algunos ejemplos:

.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 */
}

Polygon es bastante poderoso. Ryan Scherf publicó un tutorial aquí en CSS-Tricks sobre cómo usarlo programáticamente para producir un efecto incompleto.

Recomiendo jugar con él a través del Clippy de Bennet Feely :

Teóricamente, esto es lo clip-pathque apoyará (conocido como ” formas básicas “):

.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)

}

Parece que no puedo encontrar información si path()alguna vez será un valor válido.

# Usar clip-path con un <clipPath> definido por SVG

No es necesario definir el valor de la ruta de recorte directamente en CSS, puede hacer referencia a un <clipPath>elemento definido en SVG. Esto es lo que parece:

<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>
.clip-svg {
  clip-path: url(#myClip);
}

Manifestación:

Sara Soueidan también tiene una demostración de esto en acción.

There is a rather significant issue with using SVG-defined clip paths though: some browsers pin them to the upper left of the document. Here’s a demo of that issue:

Left: clip path moves along with image in Google Chrome 54 (good). Right: clip path is pinned to the upper left and doesn’t move along with image in Safari 10 (bad).

#Animating/Transitioning clip-path

When you declare a basic shape as a clip-path, you can animate it! Dirk Schulze has a wonderful article covering this same kind of stuff which features this demo:

Here’s a simple example of code:

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%);
}

Try it out:

#Masking

There was a WebKit-only version of masking where you could link up a raster image or define a gradient to be a mask. It looked like this:

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)));
}

As far as I know, that’s deprecated. That’s the definitely deprecated gradient syntax, and when I changed it to the new syntax, it didn’t work. So yeah, probably deprecated. It still works though:

And it gave birth to things like this old tutorial WebKit Image Wipes, which still works (you know, in Blink/WebKit land).

More modern references I’ve found only mention masks as being defined in SVG and referenced in CSS by ID or URL. Here’s an example two ways. The mask is defined in the SVG, and on the left, the image is within the SVG in an tag. On the right, that mask applied to an in the HTML (which only seems to work in Firefox at the moment).

Check out this demo in Firefox too (example code lifted from Dirk Shulze’s article). This kind of thing is a little dangerous at the moment, as it doesn’t just not work in WebKit/Blink, it erases the element it’s applied to entirely.

You can also link up an entire SVG file as the mask, like:

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

#Mask Types

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

#Border Masks

This is very similar to how border-image works in CSS. You define an SVG image, and nine-slice scaling is applied (like a tic-tac-toe board over the image). The corners are used in the corners, the edges (can be) repeated along the edges, and the middle (can) stretch in the middle. The basics of that:

.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/ */
}

Here’s a demo of regular masking versus border masking:

#Browser Support

It’s so hard so summarize succinctly, since different properties and even values have different support levels all over the place. Not to mention how you use them and on what. It’s a wilderness out there, so I’d recommend using as progressive enhancement the best you can at the moment. Which might be a little tough, as there isn’t even Modernizr tests for this stuff really.

As far as prefixing goes: use the non-prefixed and -webkit- prefix on pretty much everything.

Yoksel has a great browser support chart she made related to all of this:

#More