Las barras de desplazamiento personalizadas son muy poco frecuentes. Esto se debe principalmente las barras de desplazamiento son uno de los bits restantes de la Web que no estilable (los estoy mirando, selector de fecha). Puedes usar JavaScript para compilar el tuyo, pero eso es costoso y es bajo la fidelidad y la latencia. En este artículo, veremos matrices de CSS poco convencionales para crear un desplazador personalizado que no requiera ningún tipo JavaScript mientras te desplazas, solo algo de código de configuración.
A modo de resumen
¿No te importan los pequeños detalles? Solo tienes que mirar la Demostración del gato Nyan y obtener la biblioteca? Encontrarás el código de la demostración en nuestra Repositorio de GitHub.
LAM;WRA (largo y matemático; se leerá de todos modos)
Hace un tiempo, construimos un desplazador con paralaje (¿Leíste ese artículo? Es muy bueno y vale la pena tu tiempo). Remitiendo elementos con CSS 3D , los elementos se mueven más lento que nuestra velocidad de desplazamiento real.
Resumen
Empecemos con un resumen de cómo funcionó el desplazador de paralaje.
Como se muestra en la animación, logramos el efecto de paralaje enviando elementos “hacia atrás” en un espacio 3D, a lo largo del eje Z. Desplazarse por un documento es efectivamente un traslación a lo largo del eje Y. Si nos desplazamos hacia abajo, digamos 100 px, cada se traducirá hacia arriba en 100 px. Eso se aplica a todos los elementos, incluso los que están "más allá". Sin embargo, porque están más lejos de la cámara, su movimiento observado en pantalla será de menos de 100 px, lo que generará el efecto de paralaje deseado.
Por supuesto, mover un elemento al espacio también hará que parezca más pequeño, que corregimos escalando nuevamente el elemento. Descubrimos los cálculos exactos cuando creamos desplazamiento de paralaje, así que no repetiré todos los detalles.
Paso 0: ¿Qué queremos hacer?
Barras de desplazamiento Eso es lo que vamos a compilar. Pero, ¿alguna vez has pensado sobre lo que hacen? Claro que no. Las barras de desplazamiento son un indicador de cuánto del contenido disponible es visible actualmente y cuánto progreso que hiciste como lector. Si te desplazas hacia abajo, también se moverá la barra de desplazamiento para indican que estás avanzando hacia el final. Si todo el contenido encaja en la viewport, la barra de desplazamiento suele estar oculta. Si el contenido es el doble de la altura del viewport, el que ocupa 1⁄2 de la altura del viewport. Contenido cuyo valor equivale a 3 veces la altura de el viewport ajusta la barra de desplazamiento a 1⁄3 del viewport, etc. Verás el patrón. En lugar de desplazarse, también puedes hacer clic y arrastrar la barra de desplazamiento para desplazarte para agilizar el sitio. Es una cantidad sorprendente de comportamiento para una persona con ese elemento. Vamos a pelear una batalla a la vez.
Paso 1: Invierte
Bien, podemos hacer que los elementos se muevan más lento que la velocidad de desplazamiento con CSS 3D. transformaciones como se describe en el artículo sobre desplazamiento con paralaje. ¿Podemos revertir la dirección? Resulta que sí podemos, y ese es nuestro camino para crear con una barra de desplazamiento personalizada y perfecta para lograr un fotograma perfecto. Para entender cómo funciona, debemos abordar algunos conceptos básicos sobre CSS 3D.
Para obtener cualquier tipo de proyección de perspectiva en el sentido matemático, lo más probablemente termines usando coordenadas homogéneas. No entraré en detalles sobre qué son ni por qué funcionan, pero pueden pensar en como coordenadas 3D con una cuarta coordenada adicional llamada w. Esta coordenada debe ser 1, excepto si quieres que haya una distorsión de perspectiva. Mié no te preocupes por los detalles de w, ya que no usaremos ningún recurso distinto de 1. Por lo tanto, a partir de ahora, todos los puntos corresponden a vectores de 4 dimensiones. [x, y, z, w=1] y, por lo tanto, las matrices necesitan para que también sea 4x4.
Una ocasión en la que puedes ver que CSS usa coordenadas homogéneas en el
es cuando defines tus propias matrices 4x4 en una propiedad de transformación usando la
función matrix3d()
. matrix3d
toma 16 argumentos (porque la matriz es
4x4), especificando una columna después de la otra. Así que podemos usar esta función para
para especificar manualmente las rotaciones, las traslaciones, etc.
es perder la coordenada w.
Antes de poder usar matrix3d()
, necesitamos un contexto 3D, porque sin un
contexto 3D, no habría ninguna distorsión de perspectiva ni necesidad de
coordenadas homogéneas. Para crear un contexto en 3D, necesitamos un contenedor con un
perspective
y algunos elementos dentro que podemos transformar en la nueva
creado espacio 3D. Para
ejemplo:
El motor de CSS procesa los elementos dentro de un contenedor de perspectiva de la siguiente manera:
- Convertir las esquinas (vértices) de un elemento en coordenadas homogéneas
[x,y,z,w]
, en relación con el contenedor de perspectiva - Aplica todas las transformaciones de los elementos como matrices de derecha a izquierda.
- Si el elemento de perspectiva se puede desplazar, aplica una matriz de desplazamiento.
- Aplica la matriz de perspectiva.
La matriz de desplazamiento es una traslación a lo largo del eje y. Si nos desplazamos hacia abajo 400 px, todos los elementos se deben mover hacia arriba 400 px. La matriz de perspectiva es una que “jala” apunta más cerca del punto de fuga cuanto más atrás en 3D espacio donde están. Esto logra el efecto de hacer que las cosas parezcan más pequeñas cuando están más atrás y también los hace "mover más lento" cuando se los traduce. Por lo tanto, si se aleja un elemento, una traducción de 400 px provocará que el elemento para mover solo 300 px en la pantalla.
Si quieres conocer todos los detalles, consulta el spec de los CSS de transformación de datos, pero, para los fines de este artículo, simplifiquemos algoritmo anterior.
La caja está dentro de un contenedor de perspectiva con el valor p para perspective
.
Supongamos que el contenedor es desplazable y se desplaza hacia abajo
n píxeles.
La primera es la de perspectiva, y la segunda, el desplazamiento de salida. En resumen: el trabajo de la matriz de desplazamiento es hacer que un elemento se mueva hacia arriba cuando se se desplazan hacia abajo, de ahí el signo negativo.
Para nuestra barra de desplazamiento, queremos lo opuesto; queremos que nuestro elemento
mover hacia abajo cuando nos desplacemos hacia abajo Podemos usar el truco en los siguientes casos:
Invertimos la coordenada w de las esquinas de nuestro cuadro. Si la coordenada w es
-1. Todas las traducciones se aplicarán en la dirección opuesta. Entonces, ¿cómo lo
eso? El motor de CSS se encarga de convertir las esquinas de nuestro cuadro en
coordenadas homogéneas, y establece w en 1. ¡Es hora de brillar matrix3d()
!
.box {
transform:
matrix3d(
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, -1
);
}
Esta matriz no hará nada más que anular w. Cuando el motor CSS tiene
convierte cada esquina en un vector de la forma [x,y,z,1]
, la matriz
conviértela en [x,y,z,-1]
.
Enumeramos un paso intermedio para mostrar el efecto de nuestra transformación de elementos de salida. Si no te sientes cómodo con la matemática de matrices, no hay problema. La Eureka momento es que en la última línea terminamos agregando el desplazamiento n a nuestra y en lugar de restarlo. El elemento se traducirá hacia abajo. si nos desplazamos hacia abajo.
Sin embargo, si solo colocamos esta matriz en nuestro ejemplo, el elemento no se mostrará. Esto se debe a que las especificaciones de CSS requieren que vértice con w < 0 bloquea la renderización del elemento. Y como nuestra actual es 0, p es 1, w será -1.
Por suerte, podemos elegir el valor de z. Para asegurarnos de obtener w=1, necesitamos para establecer z = -2.
.box {
transform:
matrix3d(
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, -1
)
translateZ(-2px);
}
He aquí, nuestro regresó la caja.
Paso 2: Haz que se mueva
Ahora nuestra caja está ahí y tiene el mismo aspecto que sin transformaciones de datos. En este momento, el contenedor de perspectiva no se puede desplazar, por lo que no podemos pero sabemos que nuestro elemento irá en otra dirección cuando desplazado. Hagamos que el contenedor se desplace, ¿de acuerdo? Podemos agregar una elemento separador que ocupa espacio:
<div class="container">
<div class="box"></div>
<span class="spacer"></span>
</div>
<style>
/* … all the styles from the previous example … */
.container {
overflow: scroll;
}
.spacer {
display: block;
height: 500px;
}
</style>
Y ahora desplázate por el cuadro. El cuadro rojo se mueve hacia abajo.
Paso 3: Asígnale un tamaño
Tenemos un elemento que se mueve hacia abajo cuando la página se desplaza hacia abajo. Qué difícil es un poco, realmente. Ahora necesitamos aplicar un estilo para que se vea como una barra de desplazamiento y hacerlo un poco más interactivo.
Una barra de desplazamiento suele estar formada por un círculo y un "recorrido", mientras que el segmento no es siempre visibles. La altura del pulgar es directamente proporcional a la porción de la el contenido sea visible.
<script>
const scroller = document.querySelector('.container');
const thumb = document.querySelector('.box');
const scrollerHeight = scroller.getBoundingClientRect().height;
thumb.style.height = /* ??? */;
</script>
scrollerHeight
es la altura del elemento desplazable, mientras que
scroller.scrollHeight
es la altura total del contenido desplazable.
scrollerHeight/scroller.scrollHeight
es la fracción del contenido que se
sean visibles. La proporción del espacio vertical que cubre el pulgar debe ser igual a
proporción del contenido visible:
<script>
// …
thumb.style.height =
scrollerHeight * scrollerHeight / scroller.scrollHeight + 'px';
// Accommodate for native scrollbars
thumb.style.right =
(scroller.clientWidth - scroller.getBoundingClientRect().width) + 'px';
</script>
El tamaño del pulgar es que se vean bien, pero se mueve demasiado rápido. Aquí es donde podemos tomar nuestra técnica de la desplazador de paralaje. Si movemos el elemento más atrás, se moverá más lento mientras el desplazamiento. Podemos corregir el tamaño agrandándolo. Pero ¿cuánto debemos impulsar exactamente? Hagamos algo de matemáticas, ya lo adivinaste. Esta es la última vez, muy prometedores.
Lo esencial es que queremos
que el borde inferior del pulgar
se alinea con el borde inferior del elemento desplazable cuando el usuario se desplaza por completo.
fuera de servicio. En otras palabras: si nos desplazamos
scroller.scrollHeight - scroller.height
píxeles, queremos que el pulgar
traducido por scroller.height - thumb.height
Para cada píxel de desplazador,
Queremos que nuestro dedo se mueva una fracción de un píxel:
Ese es nuestro factor de escala. Ahora debemos convertir el factor de escala en un
traslación en el eje z, algo que ya hicimos en el desplazamiento con paralaje
. Según el
sección relevante de la especificación:
El factor de escala es igual a p/(p − z). Podemos resolver esta ecuación para z a
descubrir cuánto necesitamos trasladar el pulgar en el eje z. Pero mantén
debido a nuestros trucos de coordinación,
-2px
adicionales a lo largo de z. Además, ten en cuenta que las transformaciones de un elemento se aplican
De derecha a izquierda, todas las traducciones antes de nuestra matriz especial no
se invertirá, sin embargo, todas las traducciones que aparezcan después de nuestra matriz especial sí lo harán. Veamos
¡codifica esto!
<script>
// ... code from above...
const factor =
(scrollerHeight - thumbHeight)/(scroller.scrollHeight - scrollerHeight);
thumb.style.transform = `
matrix3d(
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, -1
)
scale(${1/factor})
translateZ(${1 - 1/factor}px)
translateZ(-2px)
`;
</script>
Tenemos un scrollbar. Y es solo un elemento del DOM al que podemos aplicar diseño de la forma que queramos. Una cosa que es en términos de accesibilidad es hacer que el pulgar responda a hacer clic y arrastrar, ya que muchos usuarios están acostumbrados a interactuar con una barra de desplazamiento de esa manera. Para no alargar esta entrada de blog, no voy a explicar los detalles de esa parte. Consulta la código de biblioteca para obtener más detalles si quieres ver cómo se hace.
¿Qué sucede con iOS?
Ah, mi viejo amigo Safari iOS. Al igual que con el desplazamiento con paralaje, nos encontramos con
problema. Debido a que nos desplazamos sobre un elemento, debemos especificar
-webkit-overflow-scrolling: touch
, pero eso causa acoplamiento 3D y toda nuestra
el efecto de desplazamiento deja de funcionar. Resolvimos este problema en la barra de desplazamiento con paralaje.
detectando Safari en iOS y recurriendo a position: sticky
como solución alternativa
haremos lo mismo aquí. Consulta la
artículo de paralaje
para refrescar tu memoria.
¿Qué ocurre con la barra de desplazamiento del navegador?
En algunos sistemas, tendremos que lidiar con una barra de desplazamiento nativa permanente.
Históricamente, la barra de desplazamiento no se podía ocultar (excepto con un
pseudoselector no estándar).
Así que, para ocultarlo, tenemos que recurrir a un hackeo (sin matemáticas). Completamos nuestra
elemento de desplazamiento en un contenedor con overflow-x: hidden
y haz que el
el elemento de desplazamiento sea más ancho que el contenedor. La barra de desplazamiento nativa del navegador es
ahora fuera de la vista.
Fin
Al unir todos estos elementos, podemos crear una imagen personalizada perfecta para un marco barra de desplazamiento, como la de nuestra Demostración del gato Nyan.
Si no ves el gato Nyan, estás experimentando que encontramos y presentamos mientras compilas esta demostración (haz clic en el pulgar para que aparezca el gato Nyan). Chrome es muy bueno para evitar el trabajo innecesario como pintar o animar cosas fuera de la pantalla. La mala noticia es que las travesuras de las matrices hacen creer a Chrome que el GIF del gato Nyan realmente está fuera de la pantalla. Esperamos que esto se solucione pronto.
Ahí lo tienes. Eso fue mucho trabajo. Te felicito por leer toda la más grande. Estas son algunas un verdadero truco para hacerlo, y probablemente rara vez valga la pena el esfuerzo excepto cuando una barra de desplazamiento personalizada es una parte esencial de la experiencia. Sin embargo, es bueno saber que es posible, ¿no? El hecho de que sea tan difícil la barra de desplazamiento personalizada muestra que hay trabajo por hacer en el lado del CSS. ¡Pero no temas! En el futuro, Houdini AnimationWorklet hará lo siguiente: mucho más sencillos con los efectos vinculados con desplazamiento de fotograma perfecto.