Uso de transiciones CSS en cotas automáticas | Programar Plus

Todos hemos estado allí. Tiene un elemento que desea poder contraer y expandir sin problemas usando transiciones CSS, pero su tamaño expandido debe depender del contenido. Tu has puesto transition: height 0.2s ease-out. Has creado un collapsed Clase CSS que se aplica height: 0. Lo prueba y … la altura no cambia. Se encaja entre los dos tamaños como si transition nunca se había establecido. Después de tocar el violín, se da cuenta de que este problema solo ocurre cuando la altura comienza o termina como auto. Los porcentajes, los valores de píxeles y las unidades absolutas funcionan como se esperaba. Pero todos ellos requieren una codificación rígida de una altura específica de antemano, en lugar de permitir que resulte naturalmente del tamaño del contenido del elemento.

Nikita Vasilyev documentó esto bien.

En este artículo, hablo principalmente en términos de height por simplicidad, pero todo aquí también se aplica a width.

Si esperaba que tuviera una solución mágica y completa a este problema, lamento decepcionarlo. No existe una solución que logre el efecto deseado sin inconvenientes. Sin embargo, existen múltiples soluciones que vienen con un conjunto diferente de ventajas y desventajas y, en la mayoría de los casos de uso, al menos una de ellas hará el trabajo de manera aceptable. Resumiré los principales y enumeraré sus altibajos para que pueda elegir el mejor para su situación.

¿Por qué no se ha solucionado este problema a nivel del navegador?

Según los documentos de Mozilla Developer Network, los valores automáticos se han excluido intencionalmente de la especificación de transiciones CSS. Parece que ha sido solicitado por algunas personas, pero cuando lo piensas, tiene al menos un poco de sentido que no se haya incluido. El proceso del navegador que vuelve a calcular los tamaños y posiciones de todos los elementos en función de su contenido y la forma en que interactúan entre sí (conocido como “reflujo”) es caro. Si tuviera que hacer la transición de un elemento a un height de auto, el navegador tendría que realizar un reflujo para cada etapa de esa animación, para determinar cómo deben moverse todos los demás elementos. Esto no se puede almacenar en caché o calcular de una manera simple, ya que no conoce los valores iniciales y / o finales hasta el momento en que ocurre la transición. Esto complicaría significativamente las matemáticas que se deben hacer bajo el capó y probablemente degradaría el rendimiento de una manera que podría no ser obvia para el desarrollador.

Técnica 1: max-height

Si busca este problema en la web, max-height El enfoque probablemente se mencionará en los primeros cinco a diez resultados. En realidad, es bastante poco ideal, pero pensé que valía la pena incluirlo aquí por el bien de la comparación.

Funciona así: los valores CSS solo se pueden cambiar hacia y desde valores unitarios fijos. Pero imagina que tenemos un elemento cuyo height se establece en auto, pero de quien max-height se establece en un valor fijo; decir, 1000px. No podemos hacer la transición height, pero podemos hacer la transición max-height, ya que tiene un valor explícito. En cualquier momento dado, la altura real del elemento será el mínimo de la height y el max-height. Así que mientras max-heightEl valor es mayor que lo auto sale a, podemos simplemente hacer la transición max-height y lograr una versión del efecto deseado.

Hay dos desventajas cruciales en esto

Uno es obvio y el otro es sutil. La desventaja obvia es que todavía tenemos que codificar una altura máxima para el elemento, incluso si no tenemos que codificar la altura en sí. Dependiendo de su situación, tal vez pueda garantizar que no necesitará más altura que esa. Pero si no, es un compromiso bastante grande. La segunda desventaja, menos obvia, es que la longitud de la transición no será realmente la que especifique a menos que la altura del contenido sea exactamente la misma que max-height. Por ejemplo, supongamos que su contenido mide 600 píxeles de alto y su max-height está pasando de 0px a 1000px con una duración de 1 segundo. ¿Cuánto tiempo tardará el elemento en llegar a 600px? 0,6 segundos! El max-height continuará la transición, pero la altura real dejará de cambiar una vez que alcance el final de su contenido. Esto será aún más pronunciado si su transición utiliza una función de temporización no lineal. Si la transición es rápida al principio y lenta al final, su sección se expandirá rápidamente y colapsará lentamente. No es ideal. Aún así, las transiciones son relativamente subjetivas, por lo que en los casos en que esta técnica sea apropiada, podría ser una compensación aceptable.

Técnica 2: transform: scaleY()

Si no está familiarizado con el transform propiedad, le permite aplicar transformaciones impulsadas por GPU (trasladar, escalar, rotar, etc.) a un elemento. Es importante tener en cuenta un par de cosas sobre la naturaleza de estas transformaciones:

  1. Operan en la representación visual del elemento como si fuera simplemente una imagen, en lugar de un elemento DOM. Esto significa, por ejemplo, que un elemento escalado demasiado se verá pixelado, ya que su DOM se renderizó originalmente en menos píxeles de los que ahora abarca.
  2. Ellos no desencadenar reflujos. Una vez más, la transformación no conoce ni se preocupa por la estructura DOM del elemento, solo por la “imagen” que el navegador dibujó de él. Esta es la razón por la que esta técnica funciona y su mayor desventaja.

La implementación funciona así: establecemos un transition para el elemento transform propiedad, luego alterna entre transform: scaleY(1) y transform: scaleY(0). Esto significa, respectivamente, “renderizar este elemento en la misma escala (en el eje y) en la que comienza” y “renderizar este elemento en una escala de 0 (en el eje y)”. La transición entre estos dos estados “aplastará” cuidadosamente el elemento hacia y desde su tamaño natural basado en el contenido. Como beneficio adicional, incluso las letras y / o imágenes del interior se “aplastarán” visualmente, en lugar de deslizarse detrás del límite del elemento. ¿La baja? Dado que no se activa ningún reflujo, los elementos alrededor de este elemento no se verán afectados por completo. No se moverán ni cambiarán de tamaño para llenar el espacio vacío.

Las ventajas y desventajas de este enfoque son claras.

Funcionará muy bien para su caso de uso o no será apropiado en absoluto.

Esto depende principalmente de si algún elemento sigue o no al en cuestión en el flujo del documento. Por ejemplo, algo que flota sobre el documento principal como un modal o una información sobre herramientas funcionará perfectamente de esta manera. También funcionaría para un elemento que se encuentra en la parte inferior del documento. Pero desafortunadamente, en muchas situaciones, este no es suficiente.

Técnica 3: JavaScript

Administrar una transición de CSS en CSS sería ideal, pero como estamos aprendiendo, a veces simplemente no es del todo posible.

Si es absolutamente necesario que las secciones se contraigan sin problemas, cuyo tamaño expandido depende completamente de su contenido, y qué otros elementos de la página fluirán a medida que se realicen la transición, puede lograrlo con algo de JavaScript.

La estrategia básica es hacer manualmente lo que el navegador se niega a hacer: calcular el tamaño completo del contenido del elemento, luego CSS hace la transición del elemento a ese tamaño de píxel explícito.

Deconstruyamos esto un poco. Lo primero que hay que tener en cuenta es que hacemos un seguimiento de si la sección está actualmente contraída o no mediante el data-collapsed atributo. Esto es necesario para que sepamos qué “hacer” con el elemento cada vez que se cambia su expansión. Si se tratara de una aplicación React o Angular, sería una variable de estado.

Lo siguiente que podría destacar es el uso de requestAnimationFrame(). Esto le permite ejecutar una devolución de llamada la próxima vez que el navegador vuelva a renderizar. En este caso, lo usamos para esperar a hacer algo hasta que el estilo que acabamos de configurar haya surtido efecto. Esto es importante donde cambiamos la altura del elemento desde auto al valor de píxeles explícitos equivalente porque no queremos esperar una transición allí. Entonces debemos aclarar el valor de transition, luego establece height, luego restaura transition. Si estas fueran líneas secuenciales en el código, el resultado sería como si se hubieran configurado todas simultáneamente, ya que el navegador no se vuelve a renderizar en paralelo a la ejecución de Javascript (al menos, para nuestros propósitos).

La otra idiosincrasia es donde establecemos height volver al modo automático una vez que la expansión haya terminado. Registramos un detector de eventos con transitionend, que se activa cada vez que concluye una transición de CSS. Dentro de ese detector de eventos, lo eliminamos (ya que solo queremos que responda a la transición inmediatamente siguiente), luego lo eliminamos height de los estilos en línea. De esta manera, el tamaño del elemento vuelve a estar definido, sin embargo, los estilos normales de la página lo definen. No queremos suponer que debería permanecer con el mismo tamaño de píxel, o incluso que debería permanecer auto tamaño. Queremos que nuestro JavaScript realice la transición y luego se salga del camino y no interfiera más de lo necesario.

El resto es bastante sencillo. Y, como puede ver, esto logra exactamente el resultado deseado. Dicho esto, a pesar de los mejores esfuerzos, hay varias formas en las que esto hace que nuestro código sea más frágil y potencialmente propenso a errores:

  • Hemos agregado 27 líneas de código en lugar de 3
  • Cambios en cosas como padding o border-box en nuestro elemento de sección podría requerir cambios en este código
  • Las transiciones CSS en la sección, que terminan mientras la transición de altura aún está en marcha, podrían hacer que la altura no vuelva a su valor predeterminado
  • Inhabilitando transition porque un marco podría interrumpir otras transiciones en ese elemento que ocurren al mismo tiempo
  • Si un error alguna vez causó la height estilo para desincronizarse con su data-collapsed atributo, su comportamiento podría tener problemas

Además de todo eso, el código que hemos escrito es procedimental en lugar de declarativo, lo que inherentemente lo hace más propenso a errores y complejo. Dicho todo esto, a veces nuestro código solo necesita hacer lo que debe hacer, y si vale la pena las compensaciones, entonces vale la pena las compensaciones.

Técnica de bonificación: Flexbox

Llamo a esta técnica una ventaja porque técnicamente no logra el comportamiento deseado. Ofrece una forma alternativa de determinar el tamaño de sus elementos que, en muchos casos, puede ser un reemplazo razonable y que admite totalmente las transiciones.

Es posible que desee leer sobre flexbox y flex-grow antes de leer esta sección, si aún no está familiarizado con ellos.

Flexbox es un sistema extremadamente poderoso para administrar la forma en que el tamaño de su interfaz se adapta a diferentes situaciones. Se han escrito muchos artículos sobre esto y no entraré en detalles. En lo que voy a entrar es en el hecho menos mencionado de que el flex La propiedad y otros relacionados con ella apoyan completamente las transiciones.

Lo que esto significa es que si su caso de uso le permite determinar el tamaño usando flexbox en lugar del tamaño de su contenido, hacer que una sección se colapse suavemente es tan simple como configurar transition: flex 0.3s ease-out y alternando flex: 0. Todavía no es tan bueno como estar basado en contenido, pero es más flexible (lo sé, lo sé, lo siento) que ir hacia y desde los tamaños de píxeles.

(Visited 7 times, 1 visits today)