Feuilleter un livre avec l'API canvas - partie 1

Il y a souvent un événement déclencheur qui me pousse à vouloir m’approprier une certaine technologie. En ce qui concerne HTML5, cela a été la découverte du site 20 things I learned about browsers and the web qui m’a littéralement mis la tête à l’envers. J’avais pourtant déjà vu un certain nombre d’effets sympathiques sur différents sites, mais il y avait là une alchimie vraiment unique entre un contenu intéressant, des fonctionnalités d’avant-garde et une mise en scène électrisante.

Evidemment, ce qui frappe en premier, c’est cette impression de tourner les pages d’un livre et je m’étais promis de prendre le temps de comprendre ce mécanisme un jour.

Hors, il y a quelques temps j’ai découvert qu’un des auteurs avait écrit un tutoriel sur le sujet. Malheureusement je l’ai trouvé beaucoup trop rapide sur les points réellement difficiles. Je vous propose donc mon propre tutoriel, une démarche pas à pas qui je l’espère vous permettra de comprendre le détail de cet effet. J’ai également remanié le code de l’article pour illustrer les différentes étapes et le rendre plus compréhensible.

La base

Le contenu

Au niveau HTML, tout est très simple:

  • Le livre lui-même correspond à un div dont l’identifiant est book
  • Chaque page du livre est contenue dans une section. Nous aurons 4 pages, donc 4 sections, dans la démo.

Notons que comme le contenu du livre est dans le HTML, ce contenu sera accessible et indexable par les moteurs de recherche. Canvas ne sera utilisé que pour créer l’effet, mais on ne dessinera sur lui aucun texte.

Cela donne le code suivant:

<div id="book">
    <section>
        <h2>History</h2>
        <p>...</p>
    </section>
    <section>
        <h2>Usage</h2>
        <p>...</p>
    </section>
    <section>
        <h2>Reactions</h2>
        <p>...</p>
    </section>
    <section>
        <h2>Support</h2>
        <p>...</p>
    </section>
</div>

Les styles

Nous souhaitons donner l’impression de feuilleter un livre. Nous aurons besoin d’une image représentant un livre ouvert: book.png. De plus, comme les contenus des différentes pages vont se superposer, nous utiliserons une autre image comme fond de page: paper.png. Cela évitera que nous voyons par transparence le contenu des pages se trouvant sous la page courante.
L’image suivante vous donnera une idée des images utilisées et de leur agencement:

Agencement des images utilisées

De plus, nous voulons mettre en place le livre au centre de l’écran. L’un dans l’autre, nous arrivons à la feuille de styles suivante:

#book {
    background: url("book.png") no-repeat;
    position: absolute;
    width: 830px;
    height: 260px;
    left: 50%;
    top: 50%;
    margin-left: -400px;
    margin-top: -125px;
}

section {
    background: url("paper.png") no-repeat;
    display: block;
    width: 400px;
    height: 250px;
    position: absolute;
    left: 415px;
    top: 5px;
    overflow: hidden;
}

Le comportement

Les pages sont pour l’instant superposées, et nous voyons celle qui apparait en dernier dans le HTML. Ce que nous allons faire dans un premier temps, c’est:

  1. remettre les pages dans l’ordre naturel d’apparition en leur assignant un z-index approprié
  2. permettre à l’utilisateur de passer d’une page à une autre en cliquant sur la droite (page suivante) ou la gauche (page précédente) du livre
    Voici le code qui permet de faire cela:
// Dimensions du livre
var BOOK_WIDTH = 830;
var BOOK_HEIGHT = 260;

// Dimensions d'une page
var PAGE_WIDTH = 400;
var PAGE_HEIGHT = 250;

// Page actuellement affichée
var currentPage = 0;

// Coordonnées de la souris
var mouse = { x: 0, y: 0 };

// Gestion des effets en cours
var flips = [];

var book = document.getElementById( "book" );
var pages = book.getElementsByTagName( "section" );

// Organisation de la profondeur des pages et création du tableau de gestion des effets
for( var i = 0, len = pages.length; i < len; i++ ) {
    pages[i].style.zIndex = len - i;

    flips.push( {
        // Element du DOM concerné par l'effet
        page: pages[i]
    } );
}

document.addEventListener( "click", mouseClickHandler, false );

function mouseClickHandler( event ) {
    mouse.x = event.clientX - book.offsetLeft;
    mouse.y = event.clientY - book.offsetTop;

    // On s'assure que le pointeur de la souris est à l'intérieur du livre
    if (0 < mouse.x && mouse.x < BOOK_WIDTH && 0 < mouse.y && mouse.y < BOOK_HEIGHT) {
        if (mouse.x < PAGE_WIDTH && currentPage > 0) {
            currentPage--;
            flips[currentPage].page.style.width = PAGE_WIDTH + "px";
        }
        else if (mouse.x >= PAGE_WIDTH && currentPage < flips.length - 1) {
            flips[currentPage].page.style.width = "0px";
            currentPage++;
        }
    }
}

Remarquez que nous avons créé un tableau d’objets nommé flips (to flip = feuilleter). Ce tableau ne parait encore pas très utile, vu que les objets contenus n’ont qu’une propriété page, mais ces objets seront étoffés par la suite.
En outre, notez que nous modifions la visibilité des pages en jouant sur leur propriété width. Cela peut paraitre un choix bizarre, il pourrait en effet sembler plus naturel de simplement modifier la propriété display. Mais nous souhaitons au final que l’effet soit progressif, et cette manière de procéder prépare en fait la suite des événements.
Maintenant, tout est en place pour que le show puisse réellement commencer dans le prochain article. Accrochez vos ceintures !

comments powered by Disqus