Escribir un libro volteable con regiones de CSS y transformaciones 3D

Ilmari Heikkinen

Llegó el día. Por fin te aburriste de los largos desplazamientos de texto y estás buscando un nuevo formato. Algo elegante. Algo compacto. Algo que toma el manuscrito largo, lo corta en pequeños rectángulos prolijos y los une. A este invento le dejo el nombre "libro".

Gracias a la potencia de las regiones de CSS (CanIUse, ve a chrome://flags y habilita las regiones de CSS) y las transformaciones CSS 3D, la tecnología de libros de vanguardia ya está disponible en los navegadores modernos. Todo lo que necesitas son unas pocas líneas de JavaScript y mucho CSS.

Comencemos por definir la estructura de nuestro libro. El libro consta de páginas y estas tienen dos caras. Las caras incluyen el contenido del libro:

<div class="book">
    <div> <!-- first page -->
    <div> <!-- front cover -->
        # My Fancy Book
    </div>
    <div> <!-- backside of cover -->
        # By Me I. Myself
        ## 2012 Bogus HTML Publishing Ltd
    </div>
    </div>
    <!-- content pages -->
    <div>
    <!-- front side of page -->
    <div class="book-pages"></div>
    <!-- back side of page -->
    <div class="book-pages"></div>
    </div>
    <div>
    <div class="book-pages"></div>
    <div class="book-pages"></div>
    </div>
    <div>
    <div class="book-pages"></div>
    <div class="book-pages"></div>
    </div>
</div>

Usaremos regiones de CSS para que el texto del libro se transfiera a las páginas del libro. Pero primero necesitamos el texto del libro.

<span id="book-content">
    blah blah blah ...
</span>

Ahora que escribimos el libro, definamos el flujo CSS. Estoy usando el carácter + como marcador de posición de prefijo del proveedor. Lo reemplazo por -webkit- para navegadores WebKit, -moz- para Firefox, etcétera:

#book-content {
    +flow-into: book-text-flow;
}
.book-pages {
    +flow-from: book-text-flow;
}

Ahora, el contenido del intervalo #book-content irá a los divs .book-pages. Sin embargo, es un libro bastante malo. Para conseguir un libro más libresco, debemos embarcarnos en una misión. Nuestro viaje conducirá por el puente arcoíris de las transformaciones CSS hasta el reino de los relojes de JavaScript. En los pasillos de los hadas mecanistas, liberaremos épicas magias de transición y obtendremos las legendarias tres claves que controlan la interfaz del inframundo.

El guardián del puente arcoíris nos imparte la sabiduría de usar selectores estructurales elegantes para que podamos convertir nuestra estructura de libro HTML en un formato más similar a un libro:

html {
    width: 100%;
    height: 100%;
}
body {
    /* The entire body is clickable area. Let the visitor know that. */
    cursor: pointer;
    width: 100%;
    height: 100%;
    /* Set the perspective transform for the page so that our book looks 3D. */
    +perspective: 800px;
    /* Use 3D for body, the book itself and the page containers. */
    +transform-style: preserve-3d;
}
.book {
    +transform-style: preserve-3d;
    position: absolute;
}
/* Page containers, contain the two sides of the page as children. */
.book > div {
    +transform-style: preserve-3d;
    position: absolute;
}
/* Both sides of a page. These are flat inside the page container, so no preserve-3d. */
.book > div > div {
    /* Fake some lighting with a gradient. */
    background: +linear-gradient(-45deg, #ffffff 0%, #e5e5e5 100%);
    width: 600px;
    height: 400px;
    overflow: hidden;
    /* Pad the page text a bit. */
    padding: 30px;
    padding-bottom: 80px;
}
/* Front of a page */
.book > div > div:first-child {
    /* The front side of a page should be slightly above the back of the page. */
    +transform: translate3d(0px, 0px, 0.02px);
    /* Add some extra padding for the gutter. */
    padding-left: 40px;
    /* Stylish border in the gutter for visual effect. */
    border-left: 2px solid #000;
}
/* Back of a page */
.book > div > div:last-child {
    /* The back side of a page is flipped. */
    +transform: rotateY(180deg);
    padding-right: 40px;
    border-right: 2px solid #000;
}
/* Front cover of the book */
.book > div:first-child > div:first-child {
    /* The covers have a different color. */
    background: +linear-gradient(-45deg, #8c9ccc 0%, #080f40 100%);
    /* Put a border around the cover to make it cover the pages. */
    border: 2px solid #000;
    /* And center the cover. */
    margin-left: -1px;
    margin-top: -1px;
}
/* Back cover of the book */
.book > div:last-child > div:last-child {
    background: +linear-gradient(-45deg, #8c9ccc 0%, #080f40 100%);
    border: 2px solid #000;
    margin-left: -1px;
    margin-top: -1px;
}

Al crear así un estilo en forma de papel para nuestro HTML, llegamos a las puertas de billones de engranajes del reino de JavaScript. Para pasar por la puerta, debemos convertir el libro plano en un volumen adecuado. Para agregar un poco de volumen al libro, desplazamos ligeramente cada página en el eje z.

(function() {
var books = document.querySelectorAll('.book');
for (var i = 0; i < books.length; i++) {
    var book = books[i];
    var pages = book.childNodes;
    for (var j = 0; j < pages.length; j++) {
    if (pages[j].tagName == "DIV") {
        setTransform(pages[j], 'translate3d(0px, 0px, ' + (-j) + 'px)');
    }
    }
}
})();

Lanzar magia de transición para impresionar a los dueños de las hadas no es la más difícil de las invocaciones. Sin embargo, los resultados hacen que las páginas de nuestro libro animen su giro de una manera fluida.

.book > div {
    +transition: 1s ease-in-out;
}

Por último, para que las páginas realmente des vueltan, necesitamos vincular los eventos mismos a nuestra causa.

(function(){
    // Get all the pages.
    var pages = document.querySelectorAll('.book > div');
    var currentPage = 0;
    // Go to previous page when clicking on left side of window.
    // Go to the next page when clicking on the right side.
    window.onclick = function(ev) {
        if (ev.clientX < window.innerWidth/2) {
        previousPage();
        } else {
        nextPage();
        }
        ev.preventDefault();
    };
    var previousPage = function() {
        if (currentPage > 0) {
        currentPage--;
            // Rotate the page to closed position and move it to its place in the closed page stack.
        setTransform(pages[currentPage], 'translate3d(0px,0px,' + (-currentPage) + 'px) rotateY(0deg)');
        }
    };
    var nextPage = function() {
        if (currentPage < pages.length) {
            // Rotate the page to open position and move it to its place in the opened stack.
        setTransform(pages[currentPage], 'translate3d(0px,0px,' + currentPage + 'px) rotateY(-150deg)');
        currentPage++;
        }
    };
})();

Con eso, adquirimos la tecnología de "libros" y podemos evacuar las torres de cristal del inframundo y dejar atrás su resplandor cegador y los feroces incendios nucleares de Achenar, la gran estrella azul del nexo del inframundo. Regresamos triunfantes a nuestros hogares con los libros en lo alto de la cabeza, listos para la inevitable cascada de desfiles y celebraciones en nuestro honor.

Puedes ver un ejemplo en línea aquí y obtener la fuente completa de los ejemplos. Si no tienes regiones CSS en tu navegador, el ejemplo se verá bastante roto. En ese caso, puedes probar este ejemplo.