Paralaje de alto rendimiento

Te guste o no, el paralaje llegó para quedarse. Cuando se usa con cuidado, puede agregar profundidad y sutileza a una aplicación web. Sin embargo, el problema es que implementar usar paralaje de forma eficaz puede ser un desafío. En este artículo, analizaremos una solución que sea eficaz y, igualmente importante, que funcione en varios navegadores.

Ilustración de paralaje.

Resumen

  • No uses eventos de desplazamiento ni background-position para crear animaciones de paralaje.
  • Usa transformaciones 3D en CSS para crear un efecto de paralaje más preciso.
  • En Mobile Safari, utiliza position: sticky para garantizar que el efecto de paralaje se propaga.

Si quieres la solución directa, dirígete al repositorio de GitHub de muestras de elementos de la IU y toma el JS del asistente de Paralaje. Puedes ver una demostración en vivo del desplazador paralaje en la Repositorio de GitHub.

Paralajeres de problemas

Para comenzar, veamos dos formas comunes de lograr un paralaje. efecto y, en particular, por qué no son adecuadas para nuestros fines.

Malo: uso de eventos de desplazamiento

El requisito clave del paralaje es que debe tener acoplamiento de desplazamiento. para cada cambio en la posición de desplazamiento de la página, la capa de la posición debe actualizarse. Si bien eso suena simple, es un mecanismo importante para navegadores modernos es su capacidad de funcionar de forma asíncrona. Esto se aplica, en nuestra un caso particular, para desplazar eventos. En la mayoría de los navegadores, los eventos de desplazamiento se entregan como “mejor esfuerzo” y no se garantiza que se entreguen en cada fotograma de la animación de desplazamiento.

Esta información importante nos dice por qué debemos evitar un Solución basada en JavaScript que mueve elementos en función de eventos de desplazamiento: JavaScript no garantiza que el paralaje siga el mismo la posición de desplazamiento de la página. En las versiones anteriores de Mobile Safari, los eventos de desplazamiento eran entregado al final del manuscrito, lo que impedía hacer una Efecto de desplazamiento basado en JavaScript Las versiones más recientes incluyen eventos de desplazamiento durante la animación, pero, al igual que en Chrome, en un "mejor esfuerzo" base. Si el botón el subproceso principal está ocupado con otro trabajo, no se entregarán los eventos de desplazamiento de inmediato, lo que significa que se perderá el efecto de paralaje.

Incorrecto: actualizando background-position

Otra situación que nos gustaría evitar es pintar en cada marco. Muchas soluciones intenta cambiar background-position para proporcionar el aspecto de paralaje, que hace que el navegador vuelva a pintar las partes afectadas de la página durante el desplazamiento puede ser lo suficientemente costoso como para bloquear significativamente la animación.

Si queremos cumplir con la promesa del movimiento con paralaje, queremos algo se puede aplicar como una propiedad acelerada (lo que hoy significa que mantener transformaciones y opacidad), y que no se basa en eventos de desplazamiento.

CSS en 3D

Tanto Scott Kellum como Keith Clark tienen realicé un trabajo importante en el uso de CSS 3D para lograr movimientos con paralaje, y la técnica que utilizan es efectivamente la siguiente:

  • Configura un elemento contenedor para desplazarte con overflow-y: scroll (y probablemente overflow-x: hidden).
  • A ese mismo elemento, aplica un valor perspective y un perspective-origin se establece en top left o 0 0.
  • Aplica una traslación en Z a los elementos secundarios de ese elemento y reduce su escala para proporcionar movimiento con paralaje sin afectar su tamaño en pantalla.

El CSS para este enfoque se ve así:

.container {
  width: 100%;
  height: 100%;
  overflow-x: hidden;
  overflow-y: scroll;
  perspective: 1px;
  perspective-origin: 0 0;
}

.parallax-child {
  transform-origin: 0 0;
  transform: translateZ(-2px) scale(3);
}

Lo que supone un fragmento de HTML como el siguiente:

<div class="container">
    <div class="parallax-child"></div>
</div>

Ajustar la escala para la perspectiva

Si se vuelve a enviar el elemento secundario, este se reducirá en proporción al del valor de la perspectiva. Puedes calcular cuánto necesitarás escalar verticalmente con esta ecuación: (perspectiva - distancia) / perspectiva. Dado que probablemente queremos que el elemento de paralaje paralaje, pero que aparezca en el tamaño que creamos, debería escalarse verticalmente de esta manera, en lugar de quedar tal como está.

En el caso del código anterior, la perspectiva es 1 px, y la La distancia Z de parallax-child es de -2 px. Esto significa que el elemento deberá se escale verticalmente en 3x, que puedes ver es el valor conectado al código: scale(3)

Puedes hacer lo siguiente con el contenido que no tenga aplicado un valor de translateZ sustituirá un valor de cero. Esto significa que la escala es (perspective - 0) / , que da un valor de 1, lo que significa que se escaló ni hacia arriba ni hacia abajo. Bastante útil, de verdad.

Cómo funciona este enfoque

Es importante que quede claro por qué esto funciona, ya que lo usaremos tu conocimiento en breve. El desplazamiento es efectivamente una transformación, por eso se puede accelerated; sobre todo implica cambiar las capas con la GPU. En una un desplazamiento común, que no tiene noción de perspectiva, se desvía se produce de manera 1:1 cuando se compara el elemento de desplazamiento con sus elementos secundarios. Si te desplazas un elemento hacia abajo por 300px, sus elementos secundarios se transforman hacia arriba por la misma cantidad: 300px.

Sin embargo, aplicar un valor de perspectiva al elemento de desplazamiento es un desorden en este proceso; cambia las matrices que respaldan la transformación de desplazamiento. Ahora, un desplazamiento de 300 px solo puede mover a los elementos secundarios 150 px, dependiendo del Los valores de perspective y translateZ que elijas. Si un elemento tiene un valor translateZ tiene un valor 0, se desplazará a la par de 1:1 (como antes), pero un elemento secundario empujadas en Z desde el origen de la perspectiva se desplazará en un diferente de conversiones. Resultado neto: movimiento con paralaje. Y, muy importante, esto se maneja como parte de la maquinaria de desplazamiento interno del navegador automáticamente, lo que significa que hay No es necesario escuchar eventos de scroll ni cambiar background-position.

Una mosca en el ungüento: Mobile Safari

Hay advertencias para cada efecto, y una importante para las transformaciones es sobre la conservación de efectos 3D en elementos secundarios. Si hay elementos en las la jerarquía entre el elemento con una perspectiva y sus elementos secundarios con paralaje, la perspectiva 3D es "aplanada", lo que significa que el efecto se pierde.

<div class="container">
    <div class="parallax-container">
    <div class="parallax-child"></div>
    </div>
</div>

En el código HTML anterior, el elemento .parallax-container es nuevo y efectivamente aplanar el valor perspective y perderemos el efecto de paralaje. La solución, en la mayoría de los casos, es bastante sencillo: agregas transform-style: preserve-3d. al elemento, lo que provoca que se propague cualquier efecto 3D (como nuestra perspectiva valor) que se aplicaron más arriba en el árbol.

.parallax-container {
  transform-style: preserve-3d;
}

Sin embargo, en el caso de Mobile Safari, la situación es un poco más complicada. Técnicamente, la aplicación de overflow-y: scroll al elemento contenedor funciona, pero en el costo de poder desplazar el elemento de desplazamiento. La solución es agregar -webkit-overflow-scrolling: touch, pero también compactará el perspective y no tenemos paralaje.

Desde el punto de vista de la mejora progresiva, es probable que no sea un punto problema. Si no podemos usar paralaje en todas las situaciones, nuestra app seguirá funcionando, pero estaría bueno encontrar una solución alternativa.

¡position: sticky al rescate!

De hecho, hay ayuda en forma de position: sticky, que existe para permiten que los elementos se “estén” a la parte superior del viewport o a un elemento superior determinado durante el desplazamiento. Las especificaciones, como la mayoría, son bastante pesadas, pero contiene pequeña gema útil en:

Esto puede no parecer mucho a primera vista, pero es un punto clave esa oración se refiere a cómo, exactamente, el grado de interés de un elemento calculado: "el desplazamiento se calcula con respecto al principal más cercano con un cuadro de desplazamiento". En otras palabras, la distancia para mover el elemento fijo (para que aparezca unido a otro elemento o al viewport) se se calculan antes de que se apliquen otras transformaciones, no después. Esto significa que, al igual que en el ejemplo de desplazamiento anterior, si el desplazamiento se calculó a los 300 píxeles, hay una nueva oportunidad de usar perspectivas (o cualquier otra transformación). para manipular ese valor de desplazamiento de 300 px antes de que se aplique a cualquier o de terceros.

Si aplicamos position: -webkit-sticky al elemento de paralaje, podemos “revertir” eficazmente el efecto de compactación de -webkit-overflow-scrolling: touch Esto garantiza que el elemento de paralaje haga referencia a la principal con un cuadro de desplazamiento, que en este caso es .container. Luego, Al igual que antes, .parallax-container aplica un valor perspective. que cambia el desplazamiento calculado y crea un efecto de paralaje.

<div class="container">
    <div class="parallax-container">
    <div class="parallax-child"></div>
    </div>
</div>
.container {
  overflow-y: scroll;
  -webkit-overflow-scrolling: touch;
}

.parallax-container {
  perspective: 1px;
}

.parallax-child {
  position: -webkit-sticky;
  top: 0px;
  transform: translate(-2px) scale(3);
}

Esto restaura el efecto de paralaje de Mobile Safari, lo cual es una excelente noticia redondo.

Advertencias sobre el posicionamiento fijo

Sin embargo, hay una diferencia: position: sticky altera la la mecánica del paralaje. El posicionamiento fijo intenta apegar el elemento al de desplazamiento, mientras que las versiones no fijas no. Esto significa que el paralaje con pegajoso termina siendo lo contrario a aquel que no tiene:

  • Con position: sticky, cuanto más cerca se encuentra el elemento a z=0, menos se encuentra. se mueve.
  • Sin position: sticky, cuanto más cerca se encuentre el elemento de z=0, más se mueve.

Si todo te parece un poco abstracto, mira esta demostración de Robert Flack, que demuestra cómo los elementos se comportan de manera diferente con y sin elementos fijos y el posicionamiento de los datos. Para ver la diferencia, necesitas Chrome Canary (versión 56) al momento de escribir) o Safari.

Captura de pantalla de perspectiva de paralaje

Una demostración de Robert Flack en la que se muestra cómo position: sticky afecta el desplazamiento con paralaje.

Diversos errores y soluciones alternativas

Sin embargo, como con todo, todavía hay bultos y protuberancias que deben suavizado:

  • La compatibilidad fija no es coherente. La compatibilidad se sigue implementando Chrome, Edge no es compatible por completo, y Firefox tiene errores de pintura cuando lo fijo se combina con transformaciones de perspectiva. De tal forma vale la pena agregar un poco de código para agregar únicamente position: sticky (el versión con el prefijo -webkit-) cuando sea necesario, por ejemplo, para Mobile Safari solamente.
  • El efecto no funciona solo. en Edge. Edge intenta controlar el desplazamiento en el nivel de SO, lo cual suele ser bueno, pero, en este caso, evita la detección de los cambios de perspectiva durante el desplazamiento. Para solucionar este problema, puedes agregar Un elemento de posición fija, ya que parece cambiar Edge a un método de desplazamiento sin SO y garantiza que tenga en cuenta los cambios de perspectiva.
  • “El contenido de la página se ha hecho enorme”. Muchos navegadores tienen en cuenta la escala para decidir el tamaño del contenido de la página, pero Chrome y Safari no tengan en cuenta la perspectiva. De esta manera, si se aplica una escala de 3x a un elemento, se puede verás las barras de desplazamiento y otros similares, incluso si el elemento está en 1x después de Se aplicó perspective. Es posible solucionar este problema elementos escalando desde la esquina inferior derecha (con transform-origin: bottom right), lo cual funciona porque hará que los elementos de gran tamaño crezcan dentro de “región negativa” (por lo general, en la parte superior izquierda) del área desplazable; desplazable regiones nunca te permiten ver contenido en la región negativa ni desplazarte hasta ellos.

Conclusión

El paralaje es un efecto divertido cuando se usa cuidadosamente. Como pueden ver, es posible para implementarlo de manera eficaz, con desplazamiento y en varios navegadores. Dado que requiere un poco de giro matemático y una pequeña cantidad de código estándar para lograr el efecto deseado, empaquetamos una pequeña biblioteca auxiliar y muestra, que puedes encontrar en nuestro repositorio de GitHub de muestras de elementos de la IU.

Diviértete y cuéntanos cómo te va.