Introduction aux Pointer Events ou comment simplifier l’usage du tactile dans vos sites web pour tous les navigateurs

Aujourd’hui, la gestion du tactile (ou « touch » en anglais) est devenu obligatoire sur vos sites web. C’est un passage obligé si vous voulez que votre site soit bien adapté aux nombreux périphériques tactiles du marché : tablettes, smartphones et la panoplie de nouveaux PC tactiles. Ce n’est pas pour autant quelque chose de forcément simple à mettre à œuvre. Entre les différentes implémentations disponibles et l’obligation de continuer à supporter la souris en parallèle, il faut parfois écrire du code un peu rébarbatif à réaliser. J’aimerais ainsi vous présenter dans cet article notre approche appelée MSPointers, une technologie multi-touch émergeante permettant de mieux gérer ce scénario. J’aimerais aussi vous parler des solutions de support multi-navigateurs grâce entre autres à un polyfill que nous avons écrit en France pour rendre le support cette technologie moins complexe. Grâce à tout cela, vous devriez être en mesure de commencer à expérimenter des solutions multi-touch universelles sur vos sites web.

Avant toute chose, il faut savoir qu’il y a plusieurs modèles aujourd’hui disponibles sur le web. Ainsi, pour supporter tous les navigateurs, en plus de nos MSPointers, il vous faut jeter un œil au modèle de touch iOS en plus du bien connu modèle W3C pour gérer la souris. Cela fait donc 3 manières différentes à gérer pour l’instant dont une seule de véritablement standardisée : la souris. Heureusement, il y a une volonté grandissante de standardisation sur le tactile puisque ce dernier est devenu de plus en plus critique. En septembre dernier, Microsoft a ainsi soumis le modèle MSPointers auprès du W3C pour une standardisation et nous sommes passés depuis le 19 février au statut du Last Call Draft : https://www.w3.org/TR/pointerevents, ce qui est très bon signe. Par ailleurs, notre équipe MS Open Tech a également publié un 1er prototype pour supporter les Pointer Events au sein de Webkit.

Il faut donc s’intéresser au modèle Pointer Events en faisant abstraction de la part de marché des périphériques le supportant. Je pense que l’approche de Microsoft sur la gestion des différents types d’interactions est vraiment différente de ce qu’il se passe sur le web actuellement et mérite que l’on s’y attarde pour avoir un aperçu de ce que pourrait devenir rapidement ce futur standard. La différence principale avec les autres modèles est que le développeur va écrire son code de manière unique en visant une abstraction de toutes les formes d’entrées via ce que nous appelons un pointeur (« Pointer »). Un pointeur peut représenter n’importe quelle forme d’interaction avec l’écran : le pointeur d’une souris, un stylet, un ou plusieurs doigts. Ainsi, vous n’aurez plus besoin de perdre votre temps à coder pour chacune des formes de manière séparée.

Durant cet article, nous allons commencer par analyser le comportement d’applications fonctionnant au sein d’Internet Explorer 10 qui expose les évènements MSPointers. Nous verrons ensuite les solutions permettant de continuer à supporter tous les navigateurs tout en visant le modèle des Pointer Events. C’est quand même là bien le but du web : écrire du code pour tous les navigateurs.

Dans un second temps, nous verrons alors comment bénéficier du service de reconnaissance de gestuelles propriétaire à IE10. Ce service vous permettra de coder les fameuses « gestures » en JavaScript de manière extrêmement simple. Pour finir, comme Windows 8 et Windows Phone 8 partage le même moteur IE, le code et les concepts présentés ici seront identiques pour chacune des 2 plateformes. De la même manière, tout ce que vous apprendrez dans cet article vous permettra de réaliser exactement les mêmes choses au sein des Windows Store apps écrite en HTML5 puisque, à nouveau, c’est toujours le même moteur derrière : IE10.

Les concepts

L’idée derrière l’objet MSPointer est donc de vous permettre d’adresser la souris, le stylet et les périphériques tactiles en utilisant une seule base de code via un pattern identique à celui que vous connaissez déjà pour gérer la souris. En effet, la souris, un stylet et un point de contact tactile ont des propriétés en commun : vous pouvez déplacer un pointeur avec eux et vous pouvez aussi cliquer sur un élément avec. Il suffit alors d’adresser les scénarios communs avec exactement le même code. Le modèle de Pointers s’occupera alors d’agréger ces propriétés communes et de les exposer de manière similaire à ce que vous connaissez déjà aujourd’hui avec les évènements souris.

Les évènements communs les plus évidents sont bien sûr : MSPointerDown , MSPointerMove & MSPointerUp qui se calquent directement sur les évènements souris équivalents. Vous aurez ainsi des coordonnées en X et Y sur l’écran disponibles au sein de ces évènements.

Mais vous avez aussi des évènements plus spécifiques comme : MSPointerOver , MSPointerOut , MSPointerHover ou MSPointerCancel

clip_image001

Il y a également des cas où vous allez souhaiter gérer le tactile de manière différente de la souris afin de fournir une ergonomie adaptée à votre utilisateur en fonction de sa façon d’interagir avec votre site. Par ailleurs, grâce aux écrans multipoints, un utilisateur peut facilement zoomer ou faire tourner un élément à l’aide de 2 doigts ou plus. De son côté, un stylet peut fournir des informations de pression sur l’écran qu’une souris ne peut vous renvoyer. Le modèle de Pointer Events s’occupera à nouveau d’agréger ses différences de manière à vous permettre facilement d’écrire du code spécifique à chaque usage.

Note : pour tester les exemples qui vont suivre, il serait bien entendu préférable de disposer d’un écran tactile branché sur un périphérique Windows 8/RT ou sur un téléphone Windows Phone 8. Cependant, vous avez quelques solutions alternatives :

1. Vous aurez un 1er niveau d’expérience tactile via le simulateur Windows 8 disponible gratuitement avec la suite de développement Visual Studio 2012 Express. Pour plus de détails à ce sujet, rendez-vous dans cet article : Tester et déboguer les Touch Events d’IE10 et votre Responsive Web Design grâce au simulateur de Windows 8 et VS 2012
2. Utilisez un service de test multi-navigateurs comme BrowserStack pour tester IE10 même si vous n’avez pas de machines Windows 7/8 à votre disposition. Vous pouvez vous servir gratuitement du service BrowserStack pendant 3 mois, grâce à l’opération menée par l’équipe d’Internet Explorer sur modern.IE.

Par ailleurs, je vous conseille en complément de cet article cette excellente session des Microsoft Techdays 2013: HTML5, JavaScript et CSS3: Concevoir des interfaces tactiles à destination de Windows 8 et du web (WEB202) animée par Philippe Didiergeorges et Etienne Margraff.

Gérer les évènements tactiles simples

Etape 1 : ne rien faire d’autre que d’ajouter une ligne de CSS

Ok, commençons par les bases. Vous pouvez très facilement reprendre n’importe lequel de votre code JavaScript exploitant les évènements souris et il fonctionnera tel quel avec les stylets et périphériques tactiles sous Internet Explorer 10. En effet, IE10 lance les évènements souris en dernier ressort si vous ne gérez pas directement les Pointer Events dans votre code. C’est pour cela que vous pouvez cliquer sur un bouton ou n’importe lequel des éléments d’une page web avec vos doigts même si le développeur n’avait jamais imaginé que quelqu’un le fasse un jour ainsi. Bref, n’importe quel code s’abonnant aux évènements mousedown et/ou mouseup fonctionnera sans aucune modification du tout. Mais qu’en est-il de mousemove ?

Pour le savoir, passons en revue le comportement par défaut en analysant le résultat de cet exemple de code :

 <!DOCTYPE html>
<html>
<head>
    <title>Touch article sample 1</title>
</head>
<body>
    <canvas id="drawSurface" width="400px" height="400px" style="border: 1px dashed black;">
    </canvas>
    <script>
        var canvas = document.getElementById("drawSurface");
        var context = canvas.getContext("2d");
        context.fillStyle = "rgba(0, 0, 255, 0.5)";

        canvas.addEventListener("mousemove", paint, false);

        function paint(event) {
            context.fillRect(event.clientX, event.clientY, 10, 10);
        }
    </script>
</body>
</html>

Ce code s’occupe simplement de dessiner des carrés bleus de 10 pixels par 10 pixels en suivant les mouvements de la souris au sein d’un canvas HTML5. Pour le tester, il vous suffit donc de bouger le curseur de la souris à l’intérieur de la boîte délimitée par les pointillés. Si vous avez un écran tactile, essayez d’interagir avec le canvas pour découvrir par vous-même le comportement actuel :

 

 

 

 

 

 

Exemple 0 : comportement par défaut si vous ne faîtes rien

Résultat : seul MouseDown/Up/Click fonctionnent au touch

Vous voyez ainsi que lorsque vous bougez la souris au sein du canvas, cela dessine des séries de petits carrés bleus. Mais si vous utilisez l’écran tactile à la place, vous ne pourrez uniquement afficher un seul carré bleu à l’endroit précis où vous taperez le canvas avec l’un de vos doigts. Dès que vous allez essayer de bouger votre doigt à l’intérieur du canvas, le navigateur va naturellement faire défiler le contenu de la page puisque c’est le comportement qu’on lui demande d’avoir par défaut.

Il nous faut donc indiquer au navigateur de changer de comportement et de rediriger les évènements tactiles vers votre code JavaScript plutôt que d’essayer les interpréter lui-même. Pour cela, il vous suffit de cibler les éléments de votre page qui ne doivent plus réagir au comportement par défaut en appliquant cette règle CSS :

 -ms-touch-action: auto | none | manipulation | double-tap-zoom | inherit;

Vous avez différentes valeurs possibles en fonction de ce que vous souhaitez filtrer ou non. Vous trouverez la signification de ces valeurs dans cet article : Guidelines for Building Touch-friendly Sites

Le cas d’usage typique est le contrôle de cartographie que vous insérez dans votre page. Vous voulez alors en général laisser l’utilisateur se déplacer dans la carte avec les gestuelles classiques du touch (pour zoomer et se déplacer) mais garder le comportement par défaut sur le reste de la page. Dans ce cas, il faut simplement appliquer la règle ci-dessus avec la valeur none uniquement sur le conteneur HTML embarquant la cartographie.

Dans notre cas, il faut ajouter ce bloc de CSS :

 <style>
    #drawSurface
    {
        -ms-touch-action: none; /* Désactive tous les comportements tactiles par défaut, comme le défilement et le zoom */
    }
</style>

Cela nous donne le résultat suivant :

 

 

 

 

 

 

Exemple 1: juste après avoir ajouté -ms-touch-action: none 

Résultat: comportement par défaut du navigateur annulé et MouseMove fonctionne mais uniquement avec 1 doigt à la fois

Maintenant, si vous bougez l’un de vos doigts à l’intérieur du canvas, il se comportera comme un pointeur de souris. C’est cool non ? Mais vous devriez rapidement vous poser alors la question suivante : pourquoi cela ne suit-il qu’un seul de mes doigts ? Tout simplement car nous tombons actuellement dans le cas de dernier ressort justement. IE10 se contente de mapper l’un de vos doigts au pointeur de la souris. Et autant que je sache, on utilise qu’une seule souris à la fois. Donc 1 souris === 1 point de contact tactile maximum en utilisant cette approche. Bon, alors, comment vraiment gérer le multipoints alors ?

Etape 2 : utilisez les évènements MSPointer en lieu et place de ceux de la souris

Prenez n’importe lequel de votre code existant et remplacer vos abonnements aux évènements « mousedown/up/move » par « MSPointerDown/Up/Move » et votre code deviendra alors directement compatible avec le support multi-touch d’IE10 !

Par exemple, dans l’exemple précédent, il suffit de changer cette ligne de code :

 canvas.addEventListener("mousemove", paint, false);

par celle-ci :

 canvas.addEventListener("MSPointerMove", paint, false);

Et vous obtiendrez alors ce résultat :

 

 

 

 

 

 

Exemple 2: utilisation de MSPointerMove à la place de mousemove

Résultat: le multi-touch fonctionne

Vous pouvez désormais dessiner autant de séries de carrés que votre écran ne supporte de points de contacts et/ou que vous n’avez de doigts ! Encore mieux, le même code marche pour gérer le tactile, la souris et le stylet. Cela veut dire par exemple que vous pouvez dessiner une série de carrés à la souris tout en en dessinant d’autres en même temps avec vos doigts. Clignement d'œil

Mais si vous souhaitez aller plus loin et changer le comportement de votre code en fonction du type d’entrée, il faut alors tester la valeur de la propriété pointerType. Par exemple, imaginez que vous souhaitez dessiner des séries de 10x10 pixels rouges pour le tactile, 5x5 pixels verts pour le stylet et 2x2 pixels bleus pour la souris. Vous devez alors remplacer le gestionnaire précédent (la fonction paint) par celui-ci :

 function paint(event) {
    if (event.pointerType) {
        switch (event.pointerType) {
            case event.MSPOINTER_TYPE_TOUCH:
                // Un écran tactile est utilisé
                // On dessine en rouge un carré de 10
                context.fillStyle = "rgba(255, 0, 0, 0.5)";
                squaresize = 10;
                break;
            case event.MSPOINTER_TYPE_PEN:
                // Un stylet est utilisé
                // On dessine en vert un carré de 5
                context.fillStyle = "rgba(0, 255, 0, 0.5)";
                squaresize = 5;
                break;
            case event.MSPOINTER_TYPE_MOUSE:
                // Une souris est utilisée
                // On dessine en blue un carré de 2
                context.fillStyle = "rgba(0, 0, 255, 0.5)";
                squaresize = 2;
                break;
        }

        context.fillRect(event.clientX, event.clientY, squaresize, squaresize);
    }
}

Et vous pouvez tester le nouveau résultat ici :

 

 

 

 

 

 

Exemple 2b: test de pointerType pour différencier tactile/stylet ou  souris

Résultat: Vous pouvez changer le comportement de votre code pour chacune des entrées mais depuis 2a le code ne marche maintenant uniquement dans IE10

Si vous faîtes partis des chanceux à disposer d’une machine supportant les 3 types d’entrées (comme le Sony Duo 11, la Microsoft Surface Pro ou la tablette Samsung que certains d’entre nous avions eu à la BUILD2011), vous serez en mesure de voir les différences visuelles entre les 3.

Bon, tout cela est bien sympathique mais nous avons désormais un problème de taille avec ce code. Il gère désormais bien tous les types d’entrées au sein d’IE10 mais ne marche pas du tout dans les navigateurs ne supportant pas encore le modèle de Pointer Events comme IE9, Chrome, Firefox, Opera & Safari !

Etape 3 : détection de fonctionnalité pour fournir une expérience alternative

Comme tout bon développeur web qui se respecte, vous savez déjà surement que la meilleure approche pour supporter l’ensemble des navigateurs est de faire de la détection de fonctionnalités. Dans notre cas, cela consiste à tester cette fonctionnalité :

 window.navigator.msPointerEnabled

Faites attention cependant à ce test. Il vous indiquera uniquement si le navigateur actuel supporte les MSPointers. Il ne vous dira pas si un écran tactile est disponible ou pas. Pour tester la fonctionnalité tactile, il vous faut en plus vérifier msMaxTouchPoints.

En conclusion, pour avoir un code fonctionnel supportant à la fois le modèle MSPointer d’IE10 et proposant une expérience alternative dégradée vers le pointeur de souris pour les autres navigateurs, vous avez besoin du code suivant :

 var canvas = document.getElementById("drawSurface");
var context = canvas.getContext("2d");
context.fillStyle = "rgba(0, 0, 255, 0.5)";
if (window.navigator.msPointerEnabled) {
    // Si les Pointer events sont supportés.
    canvas.addEventListener("MSPointerMove", paint, false);
}
else {
    canvas.addEventListener("mousemove", paint, false);
}

function paint(event) {
    var squaresize = 2;
    if (event.pointerType) {
        switch (event.pointerType) {
            case event.MSPOINTER_TYPE_TOUCH:
                context.fillStyle = "rgba(255, 0, 0, 0.5)";
                squaresize = 10;
                break;
            case event.MSPOINTER_TYPE_PEN:
                context.fillStyle = "rgba(0, 255, 0, 0.5)";
                squaresize = 5;
                break;
            case event.MSPOINTER_TYPE_MOUSE:
                context.fillStyle = "rgba(0, 0, 255, 0.5)";
                squaresize = 2;
                break;
        }
    }

    context.fillRect(event.clientX, event.clientY, squaresize, squaresize);
}

A nouveau, vous pouvez vérifier le résultat ici :

 

 

 

 

 

 

Exemple 3: détection de la fonctionnalité msPointerEnabled pour fournir une dégradation

Résultat: expérience complète au sein d’IE10 et comportement classique à la souris dans les autres navigateurs

L’exemple 3 fonctionnera alors en multipoints, à la souris et au stylet dans IE10 et uniquement à la souris dans IE9, Chrome, Firefox, Opera, Safari.

Etape 4 : supporter tous les modèles de touch

J’imagine que vous aimeriez bien aller plus loin qu’une simple expérience souris dégradée pour les navigateurs autre qu’IE10. Pour cela, il vous faut alors supporter les 3 modèles dont je parlais au début : Touch Events d’Apple, évènements souris W3C et MSPointer en attendant sa standardisation finale. Il y a 2 manières de mettre cela en place :

1 – Ecrire du code pour adresser tous les types d’évènements en parallèle comme expliqué (en anglais) dans cet article de l’équipe IE : Handling Multi-touch and Mouse Input in All Browsers

2 – Ajouter simplement une référence à Hand.JS, une librairie de polyfill en JavaScript conçue par mon ami David Catuhe comme il l’explique dans son article ici : HandJS a polyfill for supporting pointer events on every browser

En effet, David a écrit cette superbe librairie vous permettant de viser la spécification Pointer Events du W3C. Comme je vous l’ai indiqué en début d’article, Microsoft a soumis son modèle MSPointer Events au W3C pour standardisation.

Cependant, bien que déjà au statut de « Last Call », la spécification Pointer Events n’est pas encore un standard. Mais grâce à cette librairie, vous pouvez déjà implémenter dans votre code ce futur standard et être prêt lorsqu’elle deviendra adoptée par tous les navigateurs modernes. En attendant, la librairie de David s’occupera de propager les Pointer Events vers la forme actuellement propriétaires MSPointer Events dans IE10, vers les Touch Events pour les navigateurs implémentant le modèle d’Apple, voir même vers les évènements souris en dernier ressort pour les anciens navigateurs ! Je vous invite donc à lire son article pour savoir comment cela fonctionne.

Pour avoir une idée de comment utiliser cette librairie, je me suis amusé à m’en servir dans cet article : Creating an universal virtual touch joystick working for all Touch models thanks to Hand.JS qui montre comment écrire un joystick tactile virtuel utilisant les Pointer Events. Grâce à Hand.JS, il fonctionne dans IE10 sur Windows 8/RT, Windows Phone 8, iPad/iPhone et sur les périphérique Android/Firefox OS avec la même base de code !

Reconnaitre des gestuelles

Maintenant que nous savons comment fonctionnent les bases du multi-touch, regardons comment reconnaître des gestuelles simples comme taper un élément ou le maintenir puis ensuite des gestuelles plus avancées comme déplacer ou agrandir/rétrécir un élément.

IE10 fournit un objet nommé MSGesture qui va bien nous aider pour ce travail. Notez d’ailleurs que cet objet est spécifique à IE10 et ne fait pas partie de notre soumission au W3C. Combiné avec l’élément MSCSSMatrix (notre equivalent à WebKitCSSMatrix), vous serez en mesure de construire des expériences multi-touch très intéressantes le tout de manière fort simple. MSCSSMatrix représente en effet une matrice 4x4 homogène permettant un accès au DOM par script aux fonctionnalités CSS 2-D et 3-D. Mais avant de jouer avec cela, commençons naturellement par les bases de la reconnaissance de gestuelles (ou « gesture » en anglais).

Ce qu’il faut retenir en premier lieu est qu’il est d’abord nécessaire de s’abonner à l’évènement MSPointerDown. Ensuite, à l’intérieur du handler prenant en charge MSPointerDown, vous devrez choisir quels sont les pointeurs que vous souhaitez envoyer à l’objet MSGesture pour qu’il s’occupe de détecter certaines gestuelles spécifiques. Ce dernier renverra alors l’un de ces évènements : MSGestureTap, MSGestureHold, MSGestureStart, MSGestureChange, MSGestureEnd, MSInertiaStart. En effet, l’objet MSGesture travaille sur un jeu de pointeurs en entrée et s’occupe ensuite d’appliquer un algorithme de reconnaissance de gestuelles par-dessus. La seule chose que vous devez faire est donc de choisir/filtrer les pointeurs devant faire partie de la gestuelle (basé sur leurs identifiants, coordonnées à l’écran, etc… à vous d’imaginer la logique qui vous correspond). L’objet MSGesture s’occupera alors magiquement du reste.

Exemple 1 : gérer la gestuelle de maintien (hold gesture)

Nous allons voir ici comment maintenir un élément (un simple élément DIV contenant une image en arrière-plan). Cette gestuelle est souvent utilisée pour simuler les actions contextuelles à l’élément de la même manière que le bouton droit de la souris. Une fois que notre élément sera maintenu, nous allons ajouter des petits triangles aux quatre coins pour indiquer à l’utilisateur qu’il a bien sélectionné l’élément. Ces coins seront générés dynamiquement en créant 4 DIV ajoutés au-dessus de chacun des coins de l’image. Ensuite, grâce à quelques astuces CSS à base de transformation 2D et gradients linéaires, nous obtiendrons la chose suivante :

image

La séquence sera donc la suivante :

1 – abonnement aux évènements MSPointerDown & MSPointerHold au-dessus de l’élément HTML qui vous intéresse

2 – création d’un objet MSGesture qui visera le même élément HTML

3 – au sein du gestionnaire de MSPointerDown, ajout à l’objet MSGesture la prise en charge des différents PointerID que l’on souhaite surveiller (tous ou un sous-ensemble d’entre eux en fonction de ce que vous souhaitez réaliser)

4 – au sein du gestionnaire MSPointerHold, vérification au sein des détails fournis dans l’évènement si l’utilisateur a juste démarré la gestuelle de maintien (via le flag MSGESTURE_FLAG_BEGIN). Si oui, on ajoute les coins spéciaux, sinon, on les retire.

Cela nous amène au code suivant :

 <!DOCTYPE html>
<html>
<head>
    <title>Touch article sample 5: simple gesture handler</title>
    <link rel="stylesheet" type="text/css" href="toucharticle.css" />
    <script src="Corners.js"></script>
</head>
<body>
    <div id="myGreatPicture" class="container" />
    <script>
        var myGreatPic = document.getElementById("myGreatPicture");
        // Création d’un nouveau MSGesture qui surveillera l’élément DOM myGreatPic
        var myGreatPicAssociatedGesture = new MSGesture();
        myGreatPicAssociatedGesture.target = myGreatPic;

        // Vous devez d’abord vous abonner à MSPointerDown pour pouvoir
        // avoir accès à des évènements gestuelles plus complexes 
        myGreatPic.addEventListener("MSPointerDown", pointerdown, false);
        myGreatPic.addEventListener("MSGestureHold", holded, false);

        // Une fois l’évènement levé, on se contente d’envoyer tous les pointeurs vers MSGesture
        function pointerdown(event) {
            myGreatPicAssociatedGesture.addPointer(event.pointerId);
        }

        // Cet évènement sera levé par l’objet MSGesture
        // en fonction des pointeurs fournis pendant l’évènement MSPointerDown
        function holded(event) {
            // La gestuelle commence, on ajoute les coins
            if (event.detail === event.MSGESTURE_FLAG_BEGIN) {
                Corners.append(myGreatPic);
            }
            else {
                // L’utilisateur a relâché son doigt, la gestuelle se termine
                // On retire donc les coins
                Corners.remove(myGreatPic);
            }
        }

        // Pour éviter d’avoir l’équivalent d’un menu contextuel affiché   
        // comme sur un click droit "right click" sur MSPointerUp, 
        // on annule le comportement du navigateur par défaut
        myGreatPic.addEventListener("contextmenu", function (e) {
            e.preventDefault();    
        }, false);
    </script>
</body>
</html>

Et voici le résultat :

Essayez de juste taper l’élément ou de cliquer dessus à la souris, rien ne se passe. Touchez le et maintenez seulement 1 doigt au-dessus de l’image ou effectuez un click gauche long dessus : les coins apparaissent. Relâchez votre doigt, les coins disparaissent bien.

Touchez et maintenez 2 ou plus de doigts sur l’image, rien ne se passe. En effet, la gestuelle de maintien est uniquement levée que lorsqu’un seul doigt maintient l’élément.

Note : les bords blancs, les coins et l’image de fond sont définis par CSS dans le fichier toucharticle.css. Corners.js s’occupe simplement de créer les 4 DIVs (avec la fonction append) et les place au-dessus de l’élément dans chacun des coins à l’aide de classes CSS appropriées.

Malgré tout, il reste un comportement avec lequel je ne suis pas très satisfait. Une fois que vous maintenez l’image, dès que vous bougez votre doigt ne serait-ce que très légèrement, le drapeau MSGESTURE_FLAG_CANCEL est levé et récupéré par le gestionnaire d’évènement qui retire aussitôt les coins. Je préférais retirer les coins uniquement lorsque l’utilisateur relâche son doigt n’importe où au-dessus de l’image ou aussitôt que son doigt sorte de la zone délimitée par l’image en elle-même. Pour effectuer cela, nous allons retirer les coins uniquement après les évènements MSPointerUp ou MSPointerOut. Cela nous donne le code suivant à la place :

 var myGreatPic = document.getElementById("myGreatPicture");
var myGreatPicAssociatedGesture = new MSGesture();
myGreatPicAssociatedGesture.target = myGreatPic;

myGreatPic.addEventListener("MSPointerDown", pointerdown, false);
myGreatPic.addEventListener("MSGestureHold", holded, false);
myGreatPic.addEventListener("MSPointerUp", removecorners, false);
myGreatPic.addEventListener("MSPointerOut", removecorners, false);

function pointerdown(event) {
    myGreatPicAssociatedGesture.addPointer(event.pointerId);
}

function holded(event) {
    if (event.detail === event.MSGESTURE_FLAG_BEGIN) {
        Corners.append(myGreatPic);
    }
}

// On retire désormais les coins que sur Pointer Up ou Out
function removecorners(event) {
    Corners.remove(myGreatPic);
}

myGreatPic.addEventListener("contextmenu", function (e) {
    e.preventDefault();    
}, false);

Qui fournit le comportement que je cherchais vraiment à obtenir :

Exemple 2 : gestion des déplacements, rotation et mise à l’échelle

Maintenant que vous savez presque tout, vous allez voir que mettre à l’échelle, déplacer ou tourner un élément est l’enfance de l’art grâce à quelques lignes de code. Vous devez tout d’abord vous abonner à l’évènement MSGestureChange. Cet évènement va vous envoyer les différents attributs comme définis dans la documentation de l’objet MSGestureEvent comme les valeurs actuellement positionnées sur votre élément HTML.

Encore mieux, par défaut, l’objet MSGesture fournit un algorithme inertiel automatique. Cela signifie que vous pouvez prendre n’importe quel élément HTML et le jeter à travers l’écran en utilisant vos petits doigts et l’animation naturelle sera gérée pour vous.

Pour finir, afin de refléter ces changements envoyés par MSGesture, vous devez bien entendu bouger l’élément vous-même. La manière la plus simple consiste à appliquer une transformation CSS parfaitement calquée sur les valeurs de rotation, mise à l’échelle et déplacement fournies par la gestuelle reconnue. Pour cela, utilisez l’élément MSCSSMatrix.

En conclusion, si vous souhaitez mettre en place toutes ces gestuelles sympathiques et modernes dans l’exemple précédent, abonnez vous à l’évènement ainsi :

 myGreatPic.addEventListener("MSGestureChange", manipulateElement, false);

Puis utilisez le gestionnaire suivant :

 function manipulateElement(e) {
    // Décommenter le code suivant si vous voulez désactiver l’algorithme d’inertie automatique 
    // fournit par le mécanisme de reconnaissance de gestuelles
    // if (e.detail == e.MSGESTURE_FLAG_INERTIA)
    // return;

    // On récupère la dernière transformation CSS appliquée à l’élément
    var m = new MSCSSMatrix(e.target.currentStyle.transform); 
    e.target.style.transform = m
    .translate(e.offsetX, e.offsetY) // On bouge l’origine au centre de la gestuelle
    .rotate(e.rotation * 180 / Math.PI) // On applique la rotation
    .scale(e.scale) // La mise à l’échelle
    .translate(e.translationX, e.translationY) // Le déplacement
    .translate(-e.offsetX, -e.offsetY); // Et on remet l’origine à sa place initiale
}

Ce qui donne le résultat final suivant :

Essayez de déplacer et lancer l’image dans la zone à fond noir avec 1 ou plusieurs doigts. Essayez également de zoomer ou faire tourner l’image avec 2 ou plusieurs doigts. Le résultat est vraiment agréable et le code pour le réaliser extrêmement simple puisque toute la complexité a été prise en charge nativement par IE10.

Liens directs vers tous les exemples

Si vous n’avez pas d’écran tactile ou si vous n’avez pas de moyens simples de tester IE10 (malgré le site Modern.IE), n’hésitez pas à jeter un œil à la session Microsoft Techdays 2013 : HTML5, JavaScript et CSS3: Concevoir des interfaces tactiles à destination de Windows 8 et du web (WEB202) pour voir l’ensemble en action.

Sinon, voici la liste des exemples embarqués dans les iframes de cet article:

- Simple touch sample 0 with nothing done 
- Simple touch sample 1 with CSS -ms-touch-action 
- Simple touch sample 2a with basic MSPointerMove implementation 
- Simple touch sample 2b with pointerType differentiation 
- Simple touch sample 3 with MSPointers and mouse fallback 
- MSGesture sample 1: MSGestureHold handler 
- MSGesture sample 1b: MSGestureHold handler 
- MSGesture sample 2: MSGestureChange 

Quelques ressources associées :

- W3C Pointer Events Specification

- HandJS a polyfill for supporting pointer events on every browser : la librairie JavaScript de polyfill de David Catuhe

- Pointer and gesture events

- Go Beyond Pan, Zoom, and Tap Using Gesture Events

- IE Test Drive Browser Surface qui a grandement inspiré mes démos

- N’hésitez pas à essayer des expériences tactiles dans IE10. Vous en trouverez un joli florilège ici : https://www.internetexplorer.fr/touchgallery . Je vous conseille notamment :

  - Contre Jour , un excellent jeu en HTML5 accompagné de son retour d’expérience à lire en français ici : Behind The Scenes !

  - Atari Arcade Games , la suite de jeux Atari portés en HTML5 grâce à CreateJS.

Logiquement, avec tous les détails que je vous ai partagés dans cet article ainsi que les ressources associées, vous devriez être en mesure de commencer à implémenter le modèle de Pointer Events dans vos sites web & vos applications pour le Windows Store. Vous avez désormais l’opportunité de facilement augmenter l’expérience utilisateur de vos sites sous Internet Explorer 10.

David