Recortando con CSS: clip-path

Collage de recortes sobre trozo de madera
Imagen de Barret Cook - modificada.

CSS y SVG tienen cada vez más cosas en común, y es que hay una clara tendencia a introducir nuevas propiedades en CSS que vienen directamente importadas de SVG. Entre ellas se encuentra clip-path, una operación que forma parte de la especificación de SVG desde 2000 y que ha sido trasladada a CSS, permitiéndonos realizar "recortes" sobre elementos HTML.

Pero, ¿qué es un recorte en CSS?

Un recorte (clipping) es una operación gráfica que permite ocultar partes de un elemento, bien sea de forma parcial o total. Las partes del elemento que son mostradas u ocultadas son determinadas por una "trayectoria de recorte" (clipping path).

Cuando definimos una "trayectoria de recorte", estamos creando una región dentro de la cual todo es visible, por contra, todo lo que está fuera de ella desaparece del lienzo o canvas. Esto no sólo incluye fondos, bordes o sombreados, sino también eventos del tipo hover o click.

A pesar de que el nuevo elemento creado mediante esta operación, tiene nuevas dimensiones sea o no rectangular, el resto de elementos fluyen dentro del documento de la misma forma que si no se le hubiese aplicado dicho recorte. Para conseguir que el resto del contenido fluya, en función de la nueva forma que hemos creado mediante la operación de recorte, debemos usar las propiedades para formas de CSS shape-outside o shape-inside especificadas en el documento de la W3C CSS Shapes. Si quieres profundizar algo más sobre el tema te invito a que leas el interesante artículo CSS Shapes de Sara Soueidan.

Clip-path no es lo mismo que clip

La propiedad clip de CSS2.1 es una antigua y restrictiva propiedad, que permitía realizar recortes únicamente mediante formas rectangulares sobre elementos posicionados de forma absoluta. Actualmente ha sido reemplazada por clip-path.

Sin embargo aún se sigue usando, ya que es una de las mejores formas de ocultar un elemento a la vista y que este siga siendo accesible.


.objeto {
  clip: rect(110px, 160px, 170px, 60px);
}

La forma de construcción del rectángulo también difiere de la forma en que se construye en clip-path mediante la forma básica inset(). Si quieres profundizar más en esta propiedad hay un buen artículo de Louis Lazaris sobre la propiedad clip, que además explica de forma gráfica cómo se calcula el rectángulo de recorte.

Sintaxis y uso de clip-path

La sintaxis correcta para el uso de esta propiedad es:

clip-path: <clip-source> | [ <basic-shape> || <geometry-box> ] | none

Para esta primera toma de contacto con la propiedad clip-path vamos a obviar el valor geometry-box, que además es opcional, y centrarnos en lo sencillo que es crear un recorte bien sea mediante clip-source, que será un URL que especifica un elemento clipPath de un SVG en línea o externo, o bien mediante basic-shape, que será una de las formas básicas de CSS especificadas en el documento de la W3C CSS Shapes.

Es mucho más simple y directo de lo que parece:


/* referenciando una trayectoria de un SVG en línea clipPath */
clip-path: url(#path1); 

/* referenciando una trayectoria de un SVG externo */
clip-path: url(path.svg#path1);

/* usando una forma básica de CSS */
clip-path: polygon(...);

Formas básicas de CSS

En CSS y según la especificación que he mencionado anteriormente de la W3C, podemos crear cuatro formas geométricas básicas: círculos, elipses, rectángulos y polígonos de x lados. Estas formas básicas se pueden pasar como un valor a una propiedad como shape-outside, o a la propiedad clip-path, con el fin de cambiar el flujo de contenido entorno a ella o realizar un recorte, respectivamente.

Inset()

inset( <shape-arg>{1,4} [round <border-radius>]? )

La función inset() permite la creación de formas rectangulares. La primera parte de la función sigue las mismas reglas que la versión abreviada de la propiedad margin, permitiendo uno, dos o cuatro valores. Si se usan cuatro valores el orden es superior, derecha, inferior e izquierda y definen la distancia entre los lados del rectángulo creado con respecto a los bordes del elemento sobre el que se aplica la función.


.objeto {
    clip-path: inset(100px 100px 100px 100px);
}

En este ejemplo estamos creado un rectángulo cuyo lado superior está a 100px del lado superior de .objeto, el lado derecho a 100px del lado derecho, el lado inferior a 100px del lado inferior y el la izquierdo a 100px del lado izquierdo. En función del tamaño de .objeto el rectángulo será mayor o menor, pero siempre manteniendo las distancias fijadas sobre los bordes del objeto sobre el que lo aplicamos.

La segunda parte de la función es opcional y usa la forma abreviada de la propiedad border-radius con el fin de redondear las cuatro esquinas del rectángulo creado, tanto horizontal como verticalmente. Al igual que en la propiedad original border-radius puede tener de uno a ocho valores. Estos valores debe ir precedidos de la palabra clave reservada round.

La propiedad border-radius es una vieja conocida de este blog. A si que si no la dominas o necesitar refrescarte un poco la memoria no dudes en volver a leerte el artículo cómo redondear bordes con CSS.

Circle()

circle( [<shape-radius>]? [at <position>]? )

La función circle() se usa para crear círculos y ambos valores son opcionales. Si algunos de ambos valores se omite se tomará un valor por defecto marcado por el navegador.

El argumento shape-radius representa el radio del círculo, y no puede tomar valores negativos. Este argumento también puede tomar como valor dos palabras reservadas closest-side y farthest-side. La palabra reservada closest-side es el valor por defecto, tomando como valor la distancia más corta entre el centro del círculo y uno de los cuatro lados del elemento. La palabra reservada farthest-side toma como valor la distancia entre el centro del círculo y el lado más alejado respecto a este.

El argumento position representa el centro del círculo, que se calcula mediante dos coordenadas (x, y) con respecto a la esquina superior izquierda del elemento, y estos valores deben ir precedidos de la palabra clave reservada at.


.objeto {
    clip-path: circle(10px at 20px 30px);
}

En el ejemplo recortamos un círculo cuyo radio es de 10px y que está posicionado a 20px horizontalmente y 30px verticalmente de la esquina superior izquierda de .objeto.

Ellipse()

ellipse( [<shape-radius>{2}]? [at <position>]? )

La función ellipse() se usa para crear una elipse y toma la misma lista de argumentos que la función circle(), a excepción que en lugar de tomar un valor para el radio toma dos, un radio para el eje x y otro para el eje y, en ese orden en concreto.

Para el resto de valores que puede tomar y cómo funcionan sus argumento únicamente tienes que consultar lo detallado para la función circle().


.objeto {
    clip-path: ellipse(10px 20x at 10px 10px);
}

En el ejemplo recortamos una ellipse posicionada a 10px horizontalmente y 10px verticalmente, y cuyos radios son de 10px y 20px respectivamente.

Polygon()

polygon( [<fill-rule>,]? [<shape-arg> <shape-arg>]# )

La función polygon() se usa para construir formas complejas usando un número determinado de puntos. Estos puntos se definen mediante pares de coordenadas (<shape-arg> <shape-arg>). El primer argumento determina la posición x del punto y el segundo la posición y del mismo. La forma poligonal final se consigue uniendo la sucesión de puntos, para al final unir el último punto al primero de la sucesión. Cada par de coordenadas deber se separado mediante comas.

Adicionalmente a la sucesión de pares de coordenadas, la función polygon() admite un argumento opcional llamado fill-rule que especifica cómo tratar las áreas dentro de la forma poligonal que pueden cortarse consigo mismo. Para más información sobre este argumento puedes consultar la especificación de la W3C sobre fill-rule.


.objeto {
    clip-path: polygon(0 40px, 75px 0, 150px 40px, 150px 120px, 75px 200px, 0 120px);
}

En el ejemplo recortamos una forma compleja de 6 puntos, básicamente un hexágono, que si tienes paciencia para dibujarlo coordenada a coordenada te darás cuenta que es el mismo usado para crear el logo de Hexagonal Alien.

Compatibilidad en navegadores

La propiedad clip-path es relativamente nueva, de hecho pasó a "candidate recommendation" el 20 de Marzo de 2014, y ya sabemos lo lentas que van estas cosas.

El soporte es aún pobre, de hecho ni IE ni Edge la soportan aún, Firefox lo hace de forma parcial, y Chrome, Opera y Safary necesitan el prefijo -webkit-. Puedes mantenerte al tanto de la evolución de su compatibilidad en Can I use.

Sin embargo no está demás ir trasteando con la posibilidades que nos dan ciertas nuevas propiedades antes de que el soporte sea total, así no te pillará el toro y al menos tendrás una idea de como funcionan y de lo que puedes hacer con ellas.

¿Qué se puede hacer con clip-path?

Más abajo te dejo una lista de demos que usan la propiedad clip-path, para que veas lo que se puede hacer con ella y lo sencillo que es en comparación con otras técnicas: