TP2 : un paint HTML5

De $1

Version de 00:46, 19 Avr 2024

cette version.

Revenir à liste des archives.

Voir la version actuelle

Introduction

Dans ce TP vous allez suivre un petit tutorial qui explique pas à pas comment écrire un programme de type "paint" en HTML5 + javascript. Il utilise le widget "canvas" de HTML5, qui permet de faire beaucoup de choses (vous le verrez en suivant les liens vers les démos et autres tutoriaux sur les canvas. On le présente comme un concurrent de flash...). Par la suite nous l'intégrerons à l'application facebook du TP1.

Snap3.jpg

Travail à faire

Un peu de lecture

  1. Parcourir les différents liens proposés dans la page des ressources HTML 5, s'attarder un peu plus longuement sur la partie consacrée au widget de type Canvas. Parcourir en particulier le premier lien avec un peu d'attention.

Un peu de codage

Vous avez le choix de l'éditeur pour les fichiers html et javascript, Netbeans est assez gros comme logiciel mais il supporte très bien la syntaxe HTML5 et javascript (avec auto-complétion, indentation automatique etc)

  1. Faites intégralement le tutorial http://dev.opera.com/articles/view/h...nvas-painting/, n'oubliez pas de télécharger les sources à l'aide d'un des premiers liens proposés dans le tutorial.
  2. Rajoutez de quoi dessiner un cercle,
  3. Rajoutez de quoi choisir la couleur du dessin (aide : vous pouvez soit utiliser un menu, soit un canvas dans lequel vous dessinez des rectangles colorés, soit carrément, vous piquez le colorPicker widget de cet exemple, vous devrez un peu plonger dans les sources...). Google est votre ami !
  4. Vous pourrez vous faire plaisir plus tard en ajoutant tout ce qui vous passe par la tête ! Par exemple, créer un truc comme celui-ci (cliquer l'image pour le tester)...

Snap4.jpg

Un peu de réflexion

  1. Vous devrez maintenant récupérer les évènements de dessin car nous allons plus tard les transférer sur le réseau (n'oubliez pas le fil rouge de ce cours : un pictionary multi-joueurs !), pour cela, vous ajouterez un gestionnaire d'événements sur la souris, et vous commencerez par écrire (en français) la description des dessins ("ligne droite dessinée entre (12,15) et (134, 145)", "cercle dessiné en (59, 37) de rayon 45", "dessin à main levé, path="(12, 16), (25, 45), (46, 67), etc...), ). Pour afficher ces phrases dans la page, vous utiliserez l'API du DOM de javascript, le mieux est de vous inspirer des exemples présents dans les sources que vous avez déjà vu (affichage de la liste des amis dans le TP 1, affichage interactif dans les transparents du cours d'aujourd'hui...)
  2. Une fois vos traces validées, vous stockerez ces événements de manière plus formelle sous la forme d'objets javascript (un array de points pour le dessin à main levé, etc.). Réflechissez à un format facilement transmissible sur le réseau et facilement décodable plus tard par le destinataire ! L'exemple "Canvas Painter" ci-dessus, bien que complexe, fait déjà ce travail.

Intégration de l'aspect "multi-participants"

Nous allons maintenant intégrer à notre projet un chat multi-participants utilisant l'api Web Socket. Pour ce faire, le code HTML5 à ajouter sera très simple, mais il faut avant tout installer un serveur web comprenant le protocole des Web Sockets. La page des ressources HTML5 recense des serveurs qui supportent cette feature (la liste est incomplète), mais pour ce TP nous allons nous servir d'un serveur embarqué très léger, qui a beaucoup de succès en ce moment, de par sa grande souplesse. Il s'agit du serveur Node.JS qui est écrit en Python, et qui permet d'écrire des applications web directement en javascript. il inclut l'interpréteur javascript le plus rapide du moment, nommé V8 (le même qu'il y a dans le navigateur Chrome). Le fait de pouvoir écrire des applications côté serveur en javascript est avantageux car les échanges de paramètres/valeurs de retour entre clients javascript s'exécutant dans le navigateur et appli serveur en javascxript sont naturelles.

Un serveur web embarqué n'est pas censé tenir une grosse charge, fournir des services évolués de maintenance/déploiement/sécurité, etc... Il se trouve à l'opposé de ce que l'on appelle des "serveurs d'application" comme JBoss, Glassfish, WebSphere, Oracle Application Server, etc... Ici on veut un serveur qui se lance en une seconde, qui occupe très peu de mémoire, etc... L'an dernier on avait utilisé comme serveur embarqué le serveur Grizzly (qui est inclut dans GlassFish) mais nous n'avons pas réussi simplement à faire tourner une implémentation des WebSocket dessus (pourtant une telle implémentation existe, dans les night builds, etc.. mais c'est encore très nouveau...)

Travail à faire : installer le serveur Node.JS et le tester !

Pour les linuxiens ou les macs OSiens, vous êtes sous un Unix, tout devrait rouler, enfin, je suppose. Nous avons testé sous mac OS et l'installation a pris 5 minutes. Pour les Windowsiens, le chemin est plus long, déjà il faut avoir installé cygwin, et ensuite il faudra passer une vingtaine de minutes à installer/builder la bête ! Voici les instructions :

  1. Pour info : le site officiel de Node.JS : nodejs.org
  2. Pour l'installer : suivre les instructions sur cette page. Pour les unixiens, ça devrait se faire sans douleur, suivez les instructions pour installer les packages binaries pré-compilés... Prenez la version 2.6 ou la version 3.1
  3. Pour les autres (ceux sous wiindows, qui COMME JE L'ESPERE, ONT DEJA INSTALLE CYGWIN COMME DEMANDE AVANT LE TP 1), il faut déjà avoir installé cygwin (voir http://cygwin.com), puis il faut suivre les instructions de cette page, prenez la version v0.2.6 lorsque vous aurez le choix, la 3.1 ne compile pas en ce moment sans bidouiller comme un malade !

Une fois que vous aurez réussi à installer le serveur, vous devriez avoir dans le PATH la commande 'node' exécutable. Elle permet d'exécuter simplement une application web. Par exemple, si vous créez un fichier test.js qui contient :

var http = require('http');
http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello World\n');
}).listen(8124, "127.0.0.1");
console.log('Server running at http://127.0.0.1:8124/');

et que vous sauvez quelque part (je l'ai mis dans ~/node_apps sous cygwin). Puis lancez (depuis cygwin ou un shell) la commande suivante :

node test.js

Vous venez de lancer un serveur qui répond à des requêtes http sur l'URL suivant : http://localhost:8124 Essayez donc d'ouvrir cet URL avec un navigateur web ! Cela doit afficher :

Snap5.jpg

Le serveur est lancé !

Snap6.jpg

Le navigateur a eu une réponse ! Vous voyez le petit code javascript du fichier test.js, c'est déjà un serveur ! C'est cela, la magie d'un serveur embarqué !

Test d'une application qui implémente un serveur supportant le protocole WebSocket

Nous allons maintenant rajouter à Node.JS le support pour le protocole des WebSockets (les URLs seront donc des ws://localhost:8xxx et pas des http://localhost:8xxx). Pour cela nous allons télécharger du code js qui implémente ce protocole. Il existe plusieurs implémentations, mais nous en avons choisi une qui fonctionne bien, et qui est relativement simple à mettre en oeuvre. Nous vous fournissons un zip prêt à l'emploi (mais croyez-moi on a bien galéré pour retrouver tous les morceaux sur github, il s'agit de cette implémentation : https://github.com/miksago !) :

Récuperez cette archive, désarchivez-là quelque part (par exemple, dans le même répertoire que là où vous aviez mis test.js), et faites un cd dans le sous-répertoire examples, puis lancez pour commencer la commande "node echo-server.js", cela va lancer un serveur sur le port 80, qui va juste gérer des connexions via web socket et faire l'echo à tous les clients connectés de ce que le serveur a reçu.

  1. Lancez la commande node echo-server.js,
  2. ouvez dans Chrome ou dans Opera ou Safari le fichier client.html du répertoire examples et cliquez sur le lien "send something",
  3. Ouvez un autre onglet ou une autre fenêtre sur le même fichier, cela doit ouvrir la même page web, cliquez à nouveau sur send something, toutes les pages doivent afficher l'ensemble des messages. On fait du broadcast ! ne cliquez pas sur "run spam", cela a fait exploser tous nos navigateurs (!)

Snap9.jpg

Snap8.jpg

 Regardez maintenant les sources du fichier client.html, oubliez le barratin de présentation et focalisez-vous sur la partie qui ouvre le web socket. Va variable "conn" est le web socket, et on lui attache 4 fonctions de callback. La plus importante est onmessage qui sera invoquée chaque fois qu'un message arrive.

var connect = function() {
  if (window["WebSocket"]) {
 
    host = "localhost:8000";
    conn = new WebSocket("ws://"+host+"/test");
    conn.onmessage = function(evt) {
      log(evt.data);
    };
    
    conn.onerror = function() {
      log("error", arguments);
    };
    
    conn.onclose = function() {
      log("closed");
    };

    conn.onopen = function() {
      log("opened");
    };
  }
};

 Pour envoyer un message, c'est encore plus simple (c'est la 4ème ligne la plus importante !)

document.getElementById("send").addEventListener("click", function(e) {
  if (conn) {
    setTimeout(function() {
      conn.send("test message");
      log("<"+conn.id+"> "+"test message");
    }, 0);
  }
  e.preventDefault();
  return false;
}, false);

 Quand au code du serveur, on croit rêver, c'est ultra-simple ! On crée un server (ligne 4), puis on ajoute des écouteurs (les paramètres "connection", "message", "error", "disconnected" représentant le type de message reçu). Regardez donc le code de l'écouteur lorsqu'on reçoit un message : conn.broadcast(...), qui renvoie le message à tous les clients connectés. Pour écrire à un seul client : conn.emit(...)

var sys = require("sys")
  , ws = require('../lib/ws/server');

var server = ws.createServer({debug: true});

// Handle WebSocket Requests
server.addListener("connection", function(conn){
  conn.send("Connection: "+conn.id);

  conn.addListener("message", function(message){
    conn.broadcast("<"+conn.id+"> "+message);
    
    if(message == "error"){
      conn.emit("error", "test");
    }
  });
});

server.addListener("error", function(){
  console.log(Array.prototype.join.call(arguments, ", "));
});

server.addListener("disconnected", function(conn){
  server.broadcast("<"+conn.id+"> disconnected");
});

server.listen(8000);

Maintenant regardons l'exemple du chat multi-participants :

Toujours dans le même répertoire :

  1. Lancez la commande "node chat-server.js",
  2. Ouvez dans vos navigateurs plusieurs onglets sur la page chat.html et commencez à chatter... Vous pouvez vous identifier en tapant /nick toto ou /nick tata dans les différentes fenêtres. Admirez le broadcast !
  3. Ouvrez donc la page du cours sur les WebSockets : http://slides.html5rocks.com/#web-sockets et ouvrez donc ws://localhost:8000 puis cliquez sur connecter et commencez à chatter, regardez ce qu'il se passe dans les onglets ouverts sur chat.html !

Snap10.jpg

Snap11.jpg

Snap12.jpg

Snap17.jpg

Voilà ! Tout le monde chatte gaiement !

Ajouter le chat dans le programme paint !

Ah ah !!!!! Là, mes enfants, c'est à vous de jouer !!!!!

  1. Pour commencer, intégrez tel quel un chat dans la page du logiciel paint et testez-le avec le même serveur que pour les tests précédents !!!!
  2. Envoyez les événements de dessin sous forme de texte, et vérifiez qu'ils transitent bien !
  3. Interprétez-les quand ils sont reçus et provoquez le dessin côté récepteur du dessin effectué par l'envoyeur. Vous aurez peut être besoin de modifier un peu le format de vos objets pour qu'ils soient plus faciles à manipuler.

Un peu de persistence

Nous allons maintenant sauvegarder nos dessins dans une base de donnée SQL du navigateur, le tout depuis du javascript ! Bien que cela ne fasse plus partie officiellement de la norme HTML5, la plupart des gros navigateurs intègrent une BD SQL légère et supportent l'api web SQL data storage (Chrome, Safari, Opera). Commencez par regarder l'exemple du cours et par le faire fonctionner : http://slides.html5rocks.com/#web-sql-db.

Sous chrome, si vous utilisez le raccourci ctrl-shift-i (sous windows, sous mac, faire command-alt-i, sous linux, cherchez donc !), vous verrez la console des développeurs webkit

 Snap2.jpg

 Regardez maintenant le source du transparent correspondant (vous pouvez le retrouver en faisant "view source" et en cherchant le mot-clé "SQL"). On trouve en particulier le code qui crée la base et la table :

/div[2]/div[5]/pre, line 1, column 10: EOF expected

Travail à faire

  • En vous inspirant de cet exemple, ajoutez dans votre programme paint de quoi sauvegarder la liste des évènements de votre dessin, par exemple, lorsqu'on clique sur un bouton ou sur un lien "save".
  • Vérifiez dans la console de chrome que vous avez bien créé la base, la table, et que les enregistrements sont bien présents.
  • Maintenant, il faut s'attaquer à la restauration ! Ajoutez un bouton load qui puisse "redessiner" le dessin sauvegardé.