Desafortunadamente, clip-path: path() sigue estando prohibido
Me emocioné mucho cuando escuché por primera vez que clip-path: path()
llegaría a Firefox. ¡Imagínese poder codificar fácilmente un cuadro de respiración como el que se muestra a continuación con solo un elemento HTML y muy poco CSS sin necesidad de SVG o una lista enorme de puntos dentro de la función de polígono!
Chris también estaba entusiasmado con la implementación inicial.
Que divertido sería esto:
Decidí probarlo. Ingrese a CodePen, presione div
un panel HTML, le di dimensiones en unidades de ventana gráfica para que se escalara bien y agregué un background
para poder verlo. Luego entré a MDN para ver algunos ejemplos de uso… ¡y la nube esponjosa de mi sueño comenzó a fallar!
Tenga en cuenta que clip-path: path()
solo funciona en Firefox 63-70 con la layout.css.clip-path-path.enabled
bandera configurada true
en about:config
y en Firefox 71+ sin necesidad de habilitar ninguna bandera. (Fuente: MDN.)
Estos fueron los ejemplos que encontré:
path('M0 200L0 110A110 90 0 0 1 240 100L 200 340z')path('M.5 1C.5 1 0 .7 0 .3A.25 .25 1 1 1 .5 .3 .25 .25 1 1 1 1 .3C1 .7 .5 1 .5 1Z')
¿Cuales son esas coordenadas? ¡La triste respuesta son los valores de píxeles ! Se utilizan porque la path()
función toma una path
cadena SVG como argumento que, al igual que el valor del d
atributo SVG en un path
elemento, solo contiene un tipo de valor de coordenadas: píxeles sin unidades. En el caso de SVG, estos píxeles se escalan con el viewBox
del svg
elemento, ¡pero no se escalan en absoluto dentro de la path()
función CSS!
Esto significa que el elemento siempre se recorta en la misma área fija si tenemos un elemento responsable con un path()
valor para la clip-path
propiedad. Por ejemplo, considere un cuadrado .box
cuya longitud de arista es 35vw
. Lo recortamos a un corazón usando la path()
función:
clip-path: path('M256 203C150 309 150 309 44 203 15 174 15 126 44 97 73 68 121 68 150 97 179 68 227 68 256 97 285 126 285 174 256 203')
Esta forma de corazón permanece del mismo tamaño mientras que las dimensiones de nuestro .box
elemento real cambian con la ventana gráfica:
Estas son malas noticias en 2020, donde el diseño responsivo es el estándar, no la excepción. Salvo en el extraño caso en el que el elemento que queremos recortar en realidad tiene un tamaño de píxel fijo, ¡la path()
función es completamente inútil! Todavía es mejor usar un SVG real hoy en día, o incluso un polygon()
valor aproximado para clip-path
. En definitiva, path()
todavía necesita mejoras, a pesar de haber despegado.
Amelia Bellamy-Royds ha sugerido aquí dos posibilidades:
Opción 1: Permitir
calc()
valores/unidades dentro depath
los datos. Esto probablemente se haría al extenderpath
la sintaxis SVG en general.Opción 2: Especificar
viewBox
enclip-path
la declaración, escalar la ruta para que se ajuste.
Personalmente prefiero la primera opción. La única ventaja que ofrece el segundo sobre el uso de SVG es el hecho de que no tenemos que incluir un SVG real. Dicho esto, incluir un SVG real siempre tendrá un mejor soporte.
La primera opción, sin embargo, podría ser una gran mejora con respecto al uso de SVG: al menos una mejora suficiente para justificar su uso clip-path
en un elemento HTML en lugar de incluir un SVG dentro de él. Consideremos el cuadro de respiración en la parte superior de esta publicación. Usando SVG, tenemos el siguiente marcado:
svg viewBox='-75 -50 150 100' path//svg
Tenga en cuenta que viewBox
está configurado de manera que el 0,0
punto esté muerto en el medio. Esto significa que tenemos que hacer que las coordenadas de la esquina superior izquierda (es decir, los dos primeros viewBox
valores) sean iguales a menos la mitad de las viewBox
dimensiones (es decir, los dos últimos viewBox
valores).
En SCSS, establecemos la longitud del borde ( $l
) del cuadro cuadrado inicial como la viewBox
dimensión más pequeña (que es el más pequeño de los dos últimos valores). Este es 100
nuestro caso.
Comenzamos el camino desde la esquina superior izquierda de nuestro cuadro cuadrado. Esto significa un comando de movimiento a ( M
) hasta este punto, con coordenadas que son ambas iguales a menos la mitad de la longitud del borde.
Luego bajamos a la esquina inferior izquierda. Esto requiere dibujar una línea vertical con una longitud que sea igual a la longitud del borde ( $l
) y que descienda, en la dirección positiva del eje y . Entonces, usaremos el v
comando.
A continuación, nos situamos en la esquina inferior derecha. Dibujaremos una línea horizontal con una longitud igual a la longitud del borde ( $l
) y va hacia la derecha, en la dirección positiva del eje x . Usaremos el h
comando para que eso suceda.
Ir a la esquina superior derecha significa dibujar otra línea vertical con una longitud igual a la longitud del borde ( $l
), por lo que usaremos el v
comando nuevamente, solo que esta vez, la diferencia es el hecho de que vamos en la dirección opuesta a la Eje y , lo que significa que usamos las mismas coordenadas, pero con un signo menos.
Juntándolo todo, tenemos el SCSS que nos permite crear el cuadro cuadrado inicial:
.box { d: path('M#{-.5*$l},#{-.5*$l} v#{$l} h#{$l} v#{-$l}'); fill: darkorange}
El CSS generado (donde $l
se reemplaza por 100
) se ve así:
.box { d: path('M-50,-50 v100 h100 v-100'); fill: darkorange;}
El resultado se puede ver en la demostración interactiva a continuación, donde al pasar el cursor sobre una parte de los datos de la ruta se resalta la parte correspondiente en el SVG resultante y al revés:
Sin embargo, si queremos que los bordes laterales respiren, no podemos utilizar líneas rectas. Reemplacémoslos por q
unos cuadráticos de Bézier ( ). El punto final sigue siendo el mismo, que está a un largo de borde hacia abajo a lo largo de la misma línea vertical. Pasamos por 0,#{$l}
allí para llegar.
Pero ¿qué pasa con el punto de control que debemos especificar antes de eso? Colocamos el punto verticalmente a medio camino entre los puntos inicial y final, lo que significa que bajamos hasta él la mitad de nuestro recorrido para llegar al punto final.
Y digamos que, en horizontal, lo colocamos un cuarto de longitud de borde hacia un lado en una dirección u otra. Si queremos que las líneas sobresalgan para ensanchar el cuadro o apretarlas para estrecharlo, debemos hacer algo como esto:
d: path('M#{-.5*$l},#{-.5*$l} q#{-.25*$l},#{.5*$l} 0,#{$l} h#{$l} v#{-$l}'); /* swollen box */d: path('M#{-.5*$l},#{-.5*$l} q#{.25*$l},#{.5*$l} 0,#{$l} h#{$l} v#{-$l}'); /* squished box */
Esto se compila con el siguiente CSS:
d: path('M-50,-50 q-25,50 0,100 h100 v-100'); /* swollen box */d: path('M-50,-50 q25,50 0,100 h100 v-100'); /* squished box */
La demostración interactiva a continuación muestra cómo path
funciona esto. Puede pasar el cursor sobre los componentes de datos de ruta para verlos resaltados en el gráfico SVG. También puedes alternar entre las versiones hinchada y aplastada.
Este es sólo el borde izquierdo. También debemos hacer lo mismo con el borde derecho. La diferencia aquí es que vamos desde la esquina inferior derecha a la esquina superior derecha, que está hacia arriba (en la dirección negativa del eje y ). Colocaremos el punto de control fuera del cuadro para obtener el efecto de buey ancho, lo que también significa colocarlo a la derecha de sus puntos finales (en la dirección positiva del eje x ). Mientras tanto, colocaremos el punto de control adentro para obtener el efecto de cuadro estrecho, lo que significa colocarlo a la izquierda de sus puntos finales (en la dirección negativa del eje x ).
d: path('M#{-.5*$l},#{-.5*$l} q#{-.25*$l},#{.5*$l} 0,#{$l} h#{$l} q#{.25*$l},#{-.5*$l} 0,#{-$l}'); /* swollen box */d: path('M#{-.5*$l},#{-.5*$l} q#{.25*$l},#{.5*$l} 0,#{$l} h#{$l} q#{-.25*$l},#{-.5*$l} 0,#{-$l}'); /* squished box */
El SCSS anterior genera el CSS siguiente:
d: path('M-50,-50 q-25,50 0,100 h100 q25,-50 0,100'); /* swollen box */d: path('M-50,-50 q25,50 0,100 h100 q-25,-50 0,-100'); /* squished box */
Para conseguir el efecto respiratorio, animamos entre el estado hinchado y el estado aplastado:
.box { d: path('M#{-.5*$l},#{-.5*$l} q#{-.25*$l},#{.5*$l} 0,#{$l} h#{$l} q#{.25*$l},#{-.5*$l} 0,#{-$l}'); /* swollen box */ animation: breathe .5s ease-in-out infinite alternate}@keyframes breathe { to { d: path('M#{-.5*$l},#{-.5*$l} q#{.25*$l},#{.5*$l} 0,#{$l} h#{$l} q#{-.25*$l},#{-.5*$l} 0,#{-$l}'); /* squished box */ }}
Dado que lo único que difiere entre los dos estados es el signo de la diferencia horizontal de los puntos de control (el signo del primer número después del q
comando curva cuadrática de Bézier), podemos simplificar las cosas con un mixin:
@mixin pdata($s: 1) { d: path('M#{-.5*$l},#{-.5*$l} q#{-.25*$s*$l},#{.5*$l} 0,#{$l} h#{$l} q#{.25*$s*$l},#{-.5*$l} 0,#{-$l}')}.box { @include pdata(); animation: breathe .5s ease-in-out infinite alternate}@keyframes breathe { to { @include pdata(-1) } }
Esto es más o menos lo que estoy haciendo para la demostración real de la caja de respiración, aunque el movimiento es un poco más discreto. Aún así, esto no hace absolutamente nada por el CSS generado: todavía tenemos dos rutas largas, feas y casi idénticas en el código compilado.
Sin embargo, si pudiéramos usar un div
, recortado con un clip-path: path()
que admitiera todo tipo de valores, incluidos calc()
los valores internos, entonces podríamos convertir el signo en una propiedad personalizada --sgn
, que luego podríamos animar entre -1
y 1
con la ayuda de Houdini.
div.box { width: 40vmin; height: 20vmin; background: darkorange; --sgn: 1; clip-path: path(M 25%,0% q calc(var(--sgn)*-25%),50% 0,100% h 50% q calc(var(--sgn)*25%),-50% 0,-100%); animation: breathe .5s ease-in-out infinite alternate}@keyframes breathe { to { --sgn: -1 } }
Ser capaz de hacer esto haría una gran diferencia. Nuestro elemento se escalaría muy bien con la ventana gráfica y también lo haría la caja de respiración que recortamos. Y, lo más importante, no necesitaríamos repetir este trazado de recorte para obtener las dos versiones diferentes (la hinchada y la aplastada), porque usar la propiedad personalizada del signo ( --sgn
) dentro de un calc()
valor sería suficiente. . Sin embargo, tal como está ahora, clip-path: path()
es prácticamente inútil.
Deja un comentario