Trazos, formas, recorte y enmascaramiento

La renderización de HTML se basa en el modelo de caja, pero la vida (y el diseño web) son mucho más que rectángulos. CSS admite varias formas de cambiar las áreas de un elemento que se renderizan, lo que les da a los desarrolladores la libertad de crear diseños que admitan todas las formas y tamaños. El recorte permite formas geométricas, mientras que el enmascaramiento afecta la visibilidad a nivel de píxel.

Rutas y formas

CSS usa funciones para definir formas. En el módulo de funciones de CSS, se incluye información general sobre las funciones. En esta sección, aprenderás a crear formas en CSS. Todos los siguientes ejemplos usan las formas que creas con la propiedad clip-path, lo que reduce el área visible solo a lo que está dentro de la forma. Esto permite que los elementos difieran visualmente de la caja del elemento. Más adelante, analizaremos el recorte con mayor detalle.

Las formas definidas en CSS pueden ser formas básicas (como círculos, rectángulos y polígonos) o rutas (que pueden definir formas complejas y compuestas).

Formas básicas

circle() y ellipse()

Las funciones circle() y ellipse() definen formas redondas y ovaladas con radios relativos a un elemento. La función circle() acepta un solo tamaño o porcentaje como argumento. De forma predeterminada, ambas funciones posicionan la forma en relación con el centro del elemento. Ambas aceptan una posición opcional después de la palabra clave at, que se puede expresar como longitudes, porcentajes o palabras clave posicionales.

.my-element {
  width: 60px;
  height: 60px;
  background: blue;
  clip-path: circle(50%);
}

Cuando la función circle() toma un argumento del 50%, se renderiza un círculo perfecto.

En el ejemplo anterior, se muestra una ruta de recorte circular con la función circle(). Ten en cuenta que un radio de 50% crea un círculo con el ancho completo del elemento. La función ellipse() acepta dos argumentos que representan los radios horizontales y verticales de la forma.

.my-element {
  width: 60px;
  height: 60px;
  background: blue;
  clip-path: ellipse(50% 25%);
}

La función ellipse() produce una elipse con argumentos de porcentaje. Los argumentos del 50% y el 25% producen una elipse que se extiende al doble del radio en el eje X que en el eje Y.

En el ejemplo anterior, se muestra una ruta de recorte elíptica con la función ellipse(). Ten en cuenta que un radio del 50% crea una elipse con el ancho completo del elemento. En el siguiente ejemplo, se muestra la misma elipse posicionada con su centro en la parte superior del elemento.

.my-element {
  width: 60px;
  height: 60px;
  background: blue;
  clip-path: ellipse(50% 25% at center top);
}

rect() y inset()

Las funciones rect() y inset() proporcionan diferentes formas de definir un rectángulo estableciendo la posición de sus lados en relación con los lados de un elemento. Esto te permite crear rectángulos que difieren visualmente del cuadro predeterminado del elemento. Opcionalmente, aceptan la palabra clave round para crear un rectángulo con esquinas redondeadas, con la misma sintaxis que la propiedad abreviada border-radius.

La función rect() define la posición de los lados superior e inferior del rectángulo en relación con el borde superior del elemento, y los lados izquierdo y derecho en relación con el borde izquierdo del elemento. Esta función acepta cuatro unidades de tamaño o porcentaje como argumentos que definen los lados superior, derecho, inferior e izquierdo. Puedes elegir la función rect() cuando quieras un rectángulo que no se ajuste cuando cambia el tamaño del elemento o uno que mantenga las mismas proporciones a medida que cambia el elemento.

.my-element {
  width: 80px;
  height: 60px;
  background: blue;
  clip-path: rect(15px 75px 45px 10px);
}

La función rect() acepta cuatro argumentos para definir el tamaño de un rectángulo. En este caso, los argumentos son 15 px, 75 px, 45 px y 10 px.

En el ejemplo anterior, se muestra una ruta de recorte rectangular definida con la función rect(). Las dimensiones son relativas a los bordes superior e izquierdo del elemento, como se muestra en el diagrama.

La función inset() define la posición de los lados de un rectángulo según la distancia hacia adentro desde cada uno de los lados de un elemento. Esta función acepta de una a cuatro unidades de tamaño o porcentaje como argumentos, lo que te permite definir varios lados a la vez. Puedes elegir la función inset() cuando quieras un rectángulo que se ajuste al tamaño del elemento o un rectángulo que tenga una distancia fija desde los bordes del elemento.

.my-element {
  width: 80px;
  height: 60px;
  background: blue;
  clip-path: inset(15px 5px 15px 10px);
}

La función inset() puede restar del tamaño intrínseco del elemento. Los argumentos para esta función en este diagrama son 15 px, 5 px, 15 px y 10 px.

En el ejemplo anterior, se muestra una ruta de recorte rectangular definida con la función inset(). Las dimensiones son relativas a los lados del elemento.

Las funciones rect() y inset() aceptan de forma opcional la palabra clave round para crear un rectángulo con esquinas redondeadas, con la misma sintaxis que la propiedad abreviada border-radius. En el siguiente ejemplo, se muestran versiones redondeadas de los rectángulos que se mostraron anteriormente.

.rounded-rect {
  width: 80px;
  height: 60px;
  background: blue;
  clip-path: inset(15px 5px 15px 10px round 5px);
}

.rounded-inset {
  width: 80px;
  height: 60px;
  background: blue;
  clip-path: inset(15px 5px 15px 10px round 5px);
}

polygon()

Para otras formas, como triángulos, pentágonos, estrellas, etcétera, la función polygon() te permite crear formas conectando varios puntos con líneas rectas. La función polygon() acepta una lista de pares que constan de dos unidades de longitud o porcentaje. Cada par describe un punto del polígono: el primer valor es la distancia desde el borde izquierdo del elemento, y el segundo es la distancia desde el borde superior del elemento. No es necesario que cierres el polígono, ya que se completará conectando el último punto con el primero.

.my-element {
  width: 60px;
  height: 60px;
  background: blue;
  clip-path: polygon(
    50% 0,
    0 100%,
    100% 100%
  );
}

La función polygon() acepta una cantidad variable de argumentos para dibujar formas complejas. En este caso, los argumentos se elaboran de tal manera que se cree un triángulo.

En el ejemplo anterior, se crea una ruta de recorte triangular definiendo tres puntos.

De forma predeterminada, la función polygon() renderiza las áreas superpuestas como rellenadas. Puedes cambiar este comportamiento con un primer argumento opcional llamado regla de relleno. Para alternar entre áreas completas y no completas, establece la regla de relleno en evenodd. Para usar la regla de relleno predeterminada, configúrala en nonzero.

En el ejemplo anterior, se muestra la función polygon() con funciones trigonométricas para crear polígonos regulares y formas de estrella. Esto no crea el polígono regular más grande posible que quepa dentro de un elemento ni lo centra. Te dejamos eso como ejercicio para que lo intentes. Las formas de estrella de este ejemplo también demuestran las reglas de relleno nonzero y evenodd.

Formas complejas

Cuando las funciones de forma básica no son suficientes para describir una forma compleja, CSS proporciona funciones que usan una sintaxis más sofisticada para describir características como curvas y líneas. Estas funciones también son útiles para las formas compuestas (formas que se componen de varias formas, como un círculo con un agujero).

path()

La función path() acepta una cadena de sintaxis de ruta de SVG para describir una forma. Esto permite crear formas complejas con instrucciones que describen las líneas y las curvas que componen la forma. Editar directamente la sintaxis de SVG puede ser complicado, por lo que te recomendamos que consideres usar un editor visual específico que pueda exportar la sintaxis cuando crees formas con la función path().

La función path() no usa unidades de tamaño de CSS, y todos los valores se interpretan como píxeles. Esto significa que las formas creadas con la función path no responden al tamaño del elemento o contenedor. Recomendamos usar path() solo para formas que tengan dimensiones fijas.

shape()

La función shape() usa una sintaxis de comando para describir una forma, similar a la función path(). Sin embargo, los comandos de la función shape() son CSS nativos y pueden usar unidades de tamaño de CSS. Esto permite que las formas definidas con la función shape() se dimensionen de forma responsiva.

En el ejemplo anterior, se usan las funciones path() y shape() para definir una forma de corazón y un círculo con un agujero en el centro. En el ejemplo, se usa el mismo valor en píxeles para ambas funciones, pero las funciones shape() también podrían haber usado otras unidades de tamaño de CSS, como porcentajes o unidades relativas al contenedor.

Recorte

El recorte define qué áreas de un elemento son visibles, de manera similar a cuando se recorta una imagen de una revista. La propiedad clip-path establece la ruta de acceso que se usa para definir el área de recorte.

Como viste en los ejemplos de la sección anterior, cualquiera de las funciones básicas de forma o ruta se puede usar como clip-path. La propiedad clip-path también admite rutas definidas en un elemento clipPath SVG, que puede estar incorporado o en un archivo separado.

Cómo el recorte puede afectar, en particular, una imagen: En esta imagen, se recorta una foto de un gatito con un círculo y con una ruta de recorte compleja que delinea el gatito en su totalidad.

En el diagrama anterior, se muestra cómo la adición de un clip-path a un elemento de imagen cambia el área visible de la imagen. La ruta de recorte superior usa la función circle(), mientras que la inferior usa un clipPath de SVG. Ten en cuenta que el círculo creado con la función circle() se centra en el elemento de forma predeterminada.

La propiedad clip-path solo acepta una sola ruta de acceso. Para recortar un elemento con varias formas que no se superponen, usa las funciones path() o shape() para definir una ruta compuesta, o bien usa un clipPath SVG. Otra opción para situaciones complejas es usar el enmascaramiento en lugar del recorte, lo que se abordará en una sección posterior.

Recorte con formas

Para realizar el recorte con una forma básica o una función de ruta, establece la propiedad clip-path en el valor que devuelve la función, como en los ejemplos anteriores. Cada función posicionará la forma de recorte de manera diferente en relación con el elemento, por lo que debes consultar la referencia de cada función.

En el ejemplo anterior, se aplicó un clip-path circular a dos elementos con la clase .clipped. Ten en cuenta que el clip-path se posiciona en relación con cada elemento y que el texto dentro del clip-path no se redistribuye para seguir la forma.

Caja de referencia de una ruta de recorte

De forma predeterminada, la ruta de recorte de un elemento incluye su borde. Cuando usas una de las funciones de forma básica, puedes establecer el cuadro de referencia de la ruta de recorte para que incluya solo el área del elemento dentro del borde. Los valores válidos para la caja de referencia son stroke-box (el valor predeterminado) y fill-box (para incluir solo el área dentro del borde).

En el ejemplo anterior, se muestran elementos con un borde grande (20px), cada uno con la función inset() para establecer el clip-path. El elemento que recorta en relación con el borde del elemento aún muestra una parte del borde. Los elementos que se recortan en relación con el área dentro del borde no muestran ningún borde y son más pequeños, incluso con el mismo valor de inserción.

Recorte con gráficos

Se puede definir una ruta de recorte en un documento SVG, ya sea incorporada en el documento HTML o a la que se haga referencia de forma externa. Esto puede ser útil para definir trazados de recorte complejos creados en programas de gráficos o trazados de recorte que combinan varias formas.

<img id="kitten" src="kitten.png">

<svg>
  <defs>
    <clipPath id="kitten-clip-shape">
      <circle cx="130" cy="175" r="100" />
    </clipPath>
  </defs>
</svg>

<style>
  #kitten {
    clip-path: url(#kitten-clip-shape);
  }
</style>

En el ejemplo anterior, el clipPath con un id de kitten-clip-shape se aplica al elemento <img>. En este caso, el documento SVG está incorporado en el HTML. Si el documento SVG es un archivo externo llamado kitten-clipper.svg, entonces se haría referencia a clipPath como url(kitten-clipper.svg#kitten-clip-shape).

Enmascaramiento

El enmascaramiento es otro método para definir qué áreas de un elemento se muestran o se ocultan. Mientras que el recorte usa formas o rutas básicas, el enmascaramiento usa los píxeles de una imagen o un gradiente para determinar la visibilidad. A diferencia del recorte, el enmascaramiento permite que las áreas de un elemento sean parcialmente transparentes. Se pueden aplicar varias imágenes de máscara a un elemento para producir una variedad de efectos.

Para aplicar una máscara, establece la propiedad mask-image. Esta propiedad acepta una o más imágenes, degradados o referencias a elementos <mask> en un documento SVG. Se pueden aplicar varias imágenes de máscara separándolas con comas.

.my-element {
  mask-image: url(my-mask.png),
              linear-gradient(black 0%, transparent 100%);
}

En el ejemplo anterior, .my-element se enmascara con una imagen PNG, seguida de un gradiente lineal. De forma predeterminada, se suman varias máscaras para crear la máscara final.

En el ejemplo anterior, se muestra una imagen con una o más máscaras aplicadas. Activa o desactiva cada máscara para ver cómo se combinan y producen el efecto final.

Máscara alfa en comparación con la máscara de luminancia

Puedes aplicar una máscara con el alpha o el luminance de la imagen. Cuando se enmascara en función de alpha, la transparencia de cada píxel de la imagen de máscara se aplica al elemento, y se ignora la información de color de ese píxel. Cuando se aplica una máscara basada en luminance, tanto la transparencia como el valor de cada píxel (qué tan brillante u oscuro es) se aplican al elemento. El enmascaramiento por luminancia trata los colores más brillantes como visibles y los más oscuros como invisibles.

Para establecer el modo de enmascaramiento, usa la propiedad mask-mode. De forma predeterminada, la propiedad mask-mode se establece en match-source, lo que establece un modo basado en el tipo de imagen de máscara. En el caso de las imágenes y los gradientes, el valor predeterminado será alpha. En el caso de las máscaras SVG, el valor predeterminado será el valor de la propiedad mask-type del elemento <mask> o luminance, si no se define ningún mask-type.

En el ejemplo anterior, se usa un patrón de prueba que muestra diferentes valores de color y alfa como máscara. Si activas o desactivas el botón de activación mask-mode, puedes ver cómo el modo alpha se basa en la transparencia, mientras que el modo luminance se basa en el brillo y la transparencia del color.

Propiedades de enmascaramiento adicionales

CSS proporciona propiedades adicionales para ajustar el comportamiento de tus máscaras. Cada una de las propiedades acepta una lista de valores separados por comas, que se compararán con la lista de máscaras establecida por la propiedad mask-image. Si hay menos valores que máscaras, la lista se repetirá hasta que se haya establecido un valor para cada máscara. Si hay más valores que máscaras, se descartan los valores excedentes.

Propiedad Descripción
mask-clip

Establece a qué caja de referencia de las máscaras de elementos se aplican. La configuración predeterminada es border-box..

mask-composite

Establece la interacción entre máscaras cuando se aplican varias máscaras al mismo elemento. La configuración predeterminada es add.

mask-origin

Establece el cuadro de referencia que actúa como origen de una máscara. La configuración predeterminada es border-box. Se comporta de manera similar a background-origin y acepta las mismas palabras clave.

mask-position

Establece la posición de una máscara en relación con el mask-origin. Acepta valores de palabras clave de posición, como top o center, porcentajes, unidades de tamaño o valores relativos a una palabra clave de posición. Se comporta de manera similar a background-position y acepta los mismos tipos de argumentos.

mask-repeat

Establece cómo se repite una máscara si el elemento enmascarado es más grande que la máscara. La configuración predeterminada es repeat. Se comporta de manera similar a background-repeat y acepta los mismos tipos de argumentos.

mask-size

Establece cómo se cambia el tamaño de una máscara en relación con el tamaño del elemento enmascarado. La configuración predeterminada es auto. Se comporta de manera similar a background-size y acepta los mismos tipos de argumentos.

La abreviatura de la máscara

Puedes establecer varias propiedades de máscara a la vez con la abreviatura de máscara. Esto puede simplificar la configuración de varias máscaras agrupando todas las propiedades de cada máscara. La abreviatura de máscara equivale a configurar estas propiedades en el siguiente orden: mask-image, mask-mode, mask-position, mask-size, mask-repeat, mask-origin, mask-clip y mask-composite. No es necesario incluir todas las propiedades, y las que no se incluyan se restablecerán a su valor inicial. Con la compatibilidad de hasta ocho propiedades por máscara, puede ser útil tener una referencia completa disponible.

.longhand {
  mask-image: linear-gradient(white, black),
              linear-gradient(90deg, black, transparent);
  mask-mode: luminance, alpha;
  mask-position: bottom left, top right;
  mask-size: 50% 50%, 30% 30%;
}

.shorthand {
  mask: linear-gradient(white, black) luminance bottom left / 50% 50%,
        linear-gradient(90deg, black, transparent) alpha top right / 30% 30%;
}

En el ejemplo anterior, cada clase tiene dos máscaras aplicadas. El primero usa propiedades individuales, mientras que el segundo usa la abreviatura mask. Ambos estilos son equivalentes.

Texto que fluye alrededor de elementos flotantes

Cuando recortas o enmascaras un elemento, solo cambias el área visible dentro de su caja, pero la caja en sí permanece sin cambios. Esto significa que un elemento flotante afectará el flujo del documento según su cuadro delimitador original, no las partes visibles del elemento. Para definir el flujo alrededor de un elemento, usa la propiedad shape-outside junto con la ruta de recorte.

La propiedad shape-outside define la forma alrededor de la cual fluirá el contenido de un elemento. Esta forma puede ser cualquiera de las funciones de forma básica, pero no las formas definidas con las funciones path() o shape(), ni un clipPath definido en un documento SVG.

La propiedad shape-outside también acepta una imagen o un degradado. Al igual que con el enmascaramiento, los límites de la forma se determinarán según la transparencia de la imagen o el degradado. La propiedad shape-image-threshold establece qué niveles de transparencia se consideran dentro de la forma.

Formas en la animación

Cómo animar clip-path

Puedes animar la propiedad clip-path, combinando formas. Debes usar la misma función de forma para cada fotograma clave y producir animaciones fluidas. Cuando se usan las funciones polygon() o shape(), se debe usar la misma cantidad de puntos en cada fotograma clave.

En el ejemplo anterior, el clip-path de un elemento realiza una transición entre un pentágono y una forma de estrella definidos con la función polygon(). En el ejemplo, se usa la regla de relleno evenodd para mostrar cómo los puntos de animación crean áreas superpuestas.

Cómo animar con offset-path

También puedes animar elementos a lo largo de las rutas creadas con estas funciones de forma. La propiedad offset-path establece la forma que se usará como ruta, y offset-distance establece la posición a lo largo de esa ruta. También puedes usar la función ray() con la propiedad offset-path para animar a lo largo de una línea recta.

En el ejemplo anterior, se muestra el uso del mismo polígono para un clip-path y un offset-path. La animación usa offset-distance para mover las estrellas más pequeñas a lo largo del mismo polígono que la estrella grande usa como su clip-path.

Verifica tus conocimientos

¿Cuáles de las siguientes son funciones de forma válidas?

circle()
Correcto.
square()
Incorrecto.
hexagon()
Incorrecto.
polygon()
Correcto.
rectangle()
Incorrecto.
inset()
Correcto.

Verdadero o falso: Las formas definidas con la función path() se pueden definir con porcentajes.

Verdadero
Incorrecto.
Falso
Correcto.

Verdadero o falso: Establecer la ruta de recorte de un elemento no cambiará el flujo de texto alrededor de él.

Verdadero
Correcto.
Falso
Incorrecto.

¿Cuál de las siguientes opciones se puede usar como ruta de recorte?

Una forma básica
Correcto.
Un elemento clipMask SVG
Correcto.
Una imagen de mapa de bits
Incorrecto.
Un gradiente
Incorrecto.

¿Cuál de las siguientes opciones se puede usar como máscara?

Una imagen de mapa de bits
Correcto.
Un gradiente
Correcto.
Un elemento de máscara SVG
Correcto.
Una forma básica, como circle() o rect()
Incorrecto.