Buenas, hoy vamos a hacer el efecto Parallax utilizando solo CSS. Porque muchos ejemplos que hay por internet lo enseñan utilizando Javascript y, aunque eso funcione, la verdad es que tiene un mal rendimiento.

Las soluciones con Javascript suelen estar basadas en respuestas al evento de scroll y precisamente el evento de scroll no es el más preciso de todos. Si el evento se emite un poco más tarde pues tu efecto estará retrasado y eso sí que es cutre.

Aparte... para qué saturar tu web con códigos extra si puedes hacerlo simplemente usando CSS.

¿Qué es el efecto Parallax?

Si estás leyendo esto porque ya sabes lo que es, pero bueno, básicamente es un efecto que consiste en desplazar uno o varios elementos a una velocidad distinta a la velocidad a la que haces scroll. Así da la sensación de profundidad y un toque de flow que queda bastante bien en tu diseño.

Es algo que surgió hace muchos años pero se aplicaba más a videojuegos planos. Como antes no había la potencia que hay ahora pues había que conseguir un toque de realismo intentando hacer lo mejor posible con las tecnologías que había en ese momento.

Y se optó por solucionar el asunto creando distintas capas de profundidad que se desplazasen a velocidades distintas, más lento cuanto más al fondo, consiguiendo así simular la percepción humana.

Y bueno pues gracias a la evolución de HTML y CSS ahora es posible hacer lo mismo en tu web de manera relativamente sencilla.

¿Cómo hacer el efecto Parallax?

Te voy a dar unos conceptos básicos antes de saltar al código porque si no te va a sonar a chapurrín filipín.

Vamos a trabajar con capas de profundidad reales, nada de recalcular la posición cada vez que se hace scroll ni cambiar las coordenadas del fondo.

Si podemos hacerlo bien y encima es más fácil y encima funciona mejor que otras alternativas pues vamos a hacerlo.

Lo guay del asunto es que CSS nos permite hacerlo.

En concreto, nos ayudarán las transformaciones 3D que se pueden hacer desde hace tiempo de manera nativa y fácil.

Pasos resumidos

Haciéndolo por capas es realmente simple:

  • Primero crearemos un contenedor con scroll vertical cuyos elementos estén en perspectiva, alejados al fondo.
  • Le diremos a ese contenedor que sea el contenedor padre posicionado en la primera capa de profundidad. Es decir, la capa del primer plano.
  • Le meteremos elementos hijos a ese contenedor con una cierta profundidad.

Pero bueno, vamos a verlo bien hecho con un ejemplo porque así puede ser que no te hayas enterado de gran cosa.

Código paso a paso

Creamos el contenedor Parallax:

.parallax-contenedor{
height: 100vh;
overflow-x: hidden;
overflow-y: auto;
perspective: 1px;
}

Le hemos dicho que tenga de alto todo el alto de la pantalla (100 unidades viewport height) y una perspectiva de 1 px (cuanto más pequeño el valor de perspectiva más intenso se ve el efecto).

Creamos las capas (una base y una de fondo)

Puedes crear todas las capas que quieras para tener distintas velocidades de desplazamiento pero lo típico en el mundo web es meter dos capas.

.parallax-capa{
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}

.parallax-capa.base{
transform: translateZ(0);
}

.parallax-capa.atras{
transform: translateZ(-1px); /* Cuanto más negativo, más alejado se ve */
}

Y ya está hecho.

En nuestro HTML llamamos estas clases creadas y estaría listo.

<div class="parallax-contenedor">

    <div class="parallax-capa atras">
        AQUI EL FONDO
    </div>

    <div class="parallax-capa base">
        AQUI LA BASE
    </div>


</div>

Pero obviamente tenemos que meterle algunos elementos para que se pueda percibir el asunto, ¿no te parece?

Pues vamos a meter cuadrados, para ser originales.

<div class="parallax-contenedor">

    <div class="parallax-capa atras">
        <div class="cuadrado pos-A"></div>
        <div class="cuadrado pos-B"></div>
        <div class="cuadrado pos-C"></div>
        <div class="cuadrado pos-D"></div>
    </div>

    <div class="parallax-capa base">
        <div class="cuadrado pos-E"></div>
        <div class="cuadrado pos-F"></div>
        <div class="cuadrado pos-G"></div>
        <div class="cuadrado pos-H"></div>
    </div>

    <div class="relleno-forzar-scroll"></div>

</div>

Y ahora hay que darle estilo al asunto.

/* Ponemos cuadrados en capas parallax para poder ver el efecto */
.cuadrado{
position: absolute;
width: 10rem;
height: 10rem;
}

/* Colorear los cuadrados del plano base */
.base .cuadrado{
background-color: rgb(0, 60, 255);
}

/* Colorear los cuadrados del plano de atrás */
.atras .cuadrado{
background-color: #999;
}

Y creamos algunas clases simplemente para posicionarlos en distintos lugares de la pantalla.

En un caso real pues posicionarás tus elementos como quieras, obviamente.

/* Posicionar los cuadrados en distintos sitios */
.pos-A{
top: 40%;
left: 1%;
}

.pos-B{
top: 28%;
left: 31%;
}

.pos-C{
top: 80%;
left: 40%;
}

.pos-D{
top: 70%;
left: 80%;
}

.pos-E{
top: 50%;
left: 25%;
}

.pos-F{
top: 30%;
left: 5%;
}
.pos-G{
top: 80%;
left: 50%;
}

.pos-H{
top: 70%;
left: 80%;
}

Vale.

Finalmente metemos un relleno para que se fuerce el scroll y puedas verlo funcionando. Ya que si no hay scroll que hacer pues tampoco hay efecto que ver.

.relleno-forzar-scroll{
height: 200vh;
}

Muy bonito.

Resultado del ejemplo

Así es como queda finalmente:

Pincha aquí para ver la demo funcionando

Pero podemos seguir rizando el rizo. Y de hecho deberíamos.

Fíjate que pusimos el mismo tamaño para los cuadrados azules (delante) y los grises (detrás), pero aparecen más chicos los de la capa de atrás.

Es obvio, es una cuestión de perspectiva.

Si están más lejos pues se verán más chicos.

Deshacer la reducción de tamaño

Si por un casual quieres deshacer la reducción de tamaño, puedes escalar los elementos para hacerlos más grandes.

Y... ¿cuánto tengo que escalar los elementos?

Usa esta proporción:

$escala = \dfrac{perspectiva + distancia}{ perspectiva}$

En nuestro caso la perspectiva es $1px$ y la distancia de profundidad en el eje $z$ es $1px$.

$escala = \dfrac{1px + 1px}{ 1px} = \dfrac{1px + 1px}{1px} = 2$ç

Date cuenta que si pones valores más alejados se verían más pequeños los elementos de atrás y la escala de corrección saldrá más gorda.

Pues nada, escalaríamos la capa de atrás usando la propiedad de CSS, y en nuestro caso hay que escalarlo al doble:

.parallax-capa.atras{
transform: translateZ(-1px) scale(2);
}

Bastante simple.

Aquí puedes ver cómo queda ahora

Separarlo en distintas secciones

El ejemplo anterior nos sirve suponiendo que tenemos un contenedor genérico, pero es verdad que lo que suele hacerse es usar el efecto para distintas secciones individuales, con algo de espacio libre por medio.

En este caso lo que hacemos es agruparlo por secciones distintas, no hay mucho misterio.

<div class="parallax-contenedor">

    <div class="parallax-seccion">

        <div class="parallax-capa atras">
            <div class="cuadrado pos-A"></div>
            <div class="cuadrado pos-B"></div>
            <div class="cuadrado pos-C"></div>
            <div class="cuadrado pos-D"></div>
        </div>

        <div class="parallax-capa base">
            <div class="cuadrado pos-E"></div>
            <div class="cuadrado pos-F"></div>
            <div class="cuadrado pos-G"></div>
            <div class="cuadrado pos-H"></div>
        </div>

    </div>

    <h1 class="separador-secciones">ESPACIO PARA OTRA COSA</h1>

    <div class="parallax-seccion">

        <div class="parallax-capa atras">
            <div class="cuadrado pos-A"></div>
            <div class="cuadrado pos-B"></div>
            <div class="cuadrado pos-C"></div>
            <div class="cuadrado pos-D"></div>
        </div>

        <div class="parallax-capa base">
            <div class="cuadrado pos-E"></div>
            <div class="cuadrado pos-F"></div>
            <div class="cuadrado pos-G"></div>
            <div class="cuadrado pos-H"></div>
        </div>

    </div>

</div>

Bueno sí que hay un mini-misterio.

Añadimos propiedades a las secciones para mantener el estilo 3D y pasarlo a los hijos. Y por otro lado ocultamos lo que se salga de las secciones.

.parallax-seccion{
position: relative;
transform-style: preserve-3d;
overflow: hidden;
}

Puedes darle un alto distinto a cada sección.

Aquí puedes ver la demo con secciones