Web 2.0/HTML5 Rabat 2012-2013

De $1

Version de 23:21, 21 Nov 2024

cette version.

Revenir à liste des archives.

Voir la version actuelle

Introduction

Ce cours a pour but de vous donner  une première expérience de HTML5.

Supports utilisés en cours

 

CSS 2 + quelques bouts de 3

Dernier exemple étudié avec zoom, rotation, etc...

Snap2.jpg

TP1: installation de l'environnement de développement, premiers exercices

Installation des logiciels

Dans ce TP vous commencerez par installer les outils WebStorm et Netbeans 7.3 beta sur vos machines, vous assurez que vous disposez de navigateurs webs récents. 

Etude des exemples vus en cours

Prenez le temps de parcourir les exemples vus en cours, passez un peu de temps à regarder les sources, n'hésitez pas à les modifier pour voir... Passez au moins 30 minutes à explorer...

Ecriture d'un formulaire HTML5

Dans cet exercice, vous allez créer une page index.html qui contiendra un formulaire HTML5 que vous soumettrez à une Servlet par la suite, pour tester. On fera la partie Servlet plus tard... Pour le moment utilisez l'outil que vous voulez pour créer le formulaire HTML (webstorm, netbeans, etc).

Je vous conseille d'utiliser aussi jsbin.com pour valider de petits exemples. Avec le menu jsbin/create milestone vous pouvez sauvegarder plusieurs versions de vos travaux. Copiez collez les URLs sinon ils seront perdus.

Le formulaire devra permettre de saisir votre nom, prénom, age (avec type = number), date d'inscription, adresse (plusieurs champs: rue, code postal sur 6 chiffres avec attribut regexp et placeholder, ville, pays avec auto-complétion par champs <datalist>), email, home page, et un mot de passe dans deux champs séparés (pour valider).

Vous utiliserez au maximum les possibilité des formulaires HTML5, c'est-à-dire: les types spécialisés email, tel, url, date, etc. Vous utiliserez aussi des règles CSS pour mettre en rouge les champs non valides, les attributs pour indiquer des valeurs exemples et des regexps pour valider le code postal. 

Vous utilisez comme dans l'exemple présent sur la page des Exemples HTML5 vus en cours l'API de validation des formulaires pour vérifier que les passwords sont bien identiques.

Une fois que la page fonctionne, vous écrirez une Servlet ou un bout de code PHP qui récupère les éléments du formulaire et les affiche dans une page de réponse.

Le but de cet exercice est de vous faire jouer au maximum avec les formulaires HTML5.

Intégration de la géolocalisation

Vous rajoutere dans le formulaire un bouton permettant de récupérer la longitude et la latitude de la positon courante. Vous afficherez ces valeurs dans un champs du formulaire de l'exercice précédent qui se remplira automatiquement. Vous afficherez dans un second temps la position sur une carte google map.

Soumettez le formulaire et affichez la longitude et la latitude reçue par le serveur.

Maintenant, inspirez-vous de l'exemple proposé sur la page des exemples et qui utilise le reverse geocoder de google map pour transformer la longitude et la latitude en adresse. Utilisez cet exemple pour pré-remplir le formulaire avec une option "trouver l'adresse à partir de ma position".

Exercices avec la vidéo

 On veut maintenant pouvoir ajouter une image à son formulaire, sa photo... Soit en la choisissant via un bouton, soit en faisant un drag'n'drop. On veut voir une preview de  la photo dans la page. On verra plus tard comment l'envoyer au serveur.

On veut aussi pouvoir la capturer à partir de la vidéo.

Vous vous inspirerez des exemples pour intégrer une image dans votre formulaire.

Exercices avec le dessin dans un canvas

On veut pouvoir enregistrer une signature avec la souris en dessinant dans un canvas. Vous ajouterez à votre formulaire un canvas et du code javascript pour pouvoir dessiner une signature à l'intérieur. Regardez les exemples pour vous en inspirer.

Regardez les différents tutoriaux pour voir comment récupérer le contenu du canvas sous la forme d'une image.

Rajouter des styles via CSS pour bien présenter la page de formulaire

Aidez-vous des exemples CSS2 donnés au début de ce TP pour styler votre formulaire et lui donner une apparence un peu plus sympathique. Notamment, j'aimerais bien que vous utilisiez des bordures pour séparer les différentes parties du formulaire: la partie adresse/géolocalisation, la partie nom, prénom, password, email, la partie image/signature, etc.

Par la suite, dans le TP2 nous reprendrons la page de ce formulaire pour utiliser les nouveaux tags de structure de HTML5 (<footer><header><article><section> etc.)

TP2 : découverte d'un logiciel de dessin

 

Télécharger l'archive à étudier

Toutes les sources de ce TP sont dans  cette archive (4Mbytes). Dézippez-la, vous devriez avoir un répertoire "Paint_HTML5_Multi_Tutorial". Et à l'intérieur des sous-répertoires step1, step2, etc... qui correspondent à chaque étape dans ce document.

Ouvrez donc ce répertoire avec l'outil WebStorm (menu file/open directory). Vous devriez donc pouvoir étudier et exécuter facilement les diverses versions du petit programme de dessin sur lequel nous allons travailler.

Step 1 : le paint le plus simple !

Allez dans le répertoire"step 1". Vous pouvez tester cet exemple en ouvrant paint.html dans votre navigateur.

Remarquez ces lignes en tête du fichier :

<!DOCTYPE HTML>
<!-- Note : in HTML5 the DOCTYPE can be memorized by a normal human being -->
<!-- Common mistake : if we put a comment before the doctype element, Internet Explorer will not
     consider the document as a HTML5 document and the canvas tag for example,
     will not be displayed ! -->

Le DOCTYPE est le tout nouveay ultra-simple DOCTYPE de HTML5 DOCTYPE, yeah !

Si vous ecrivez n'importe quoi avant la déclaration du DOCTYPE, IE9 ne considérera plus le fichier comme du HTML5, et cet exemple ne fonctionnera pas ! L'élément<canvas> de la page sera ignoré. Les autres navigateurs cependant tolèrent qu'on puisse avoir des choses avant le DOCTYPE, pas la série des Internet Explorers...  

Ensuite, dans le fichier, on délcare une feuille de style CSS et quelques scripts JavaScript. Oui, dans cette partie du TP on utilise jQuery pour la manipulation du DOM et pour la gestion des événements. HTML5 proposes la méthode document.querySelector(...) qui supporte des sélécteurs du DOM similaires à ceux de jQuery mais c'est plus long à écrire que $(...),  etjQuery utilises querySelector en interne...

<link rel="stylesheet" href="css/paint.css" />

<script src="js/jquery-1.7.2.min.js"></script>
<script src="js/utils.js"></script>
<script src="js/paint.js"></script>

Le fichier paint.js contient le code de la partie "dessiner quand la souris bouge" du programme de paint, alors que utils.js contient juste une fonction utilitaire pour récupérer correctement la position de la souris dans le <canvas>.

Lorsque le document est entièrement chargé (et le DOM prêt), on crée un objet Paint, qui contient la majeure partie des fonctionnalités écrites en JavaScript. Oui, ici on fait du JavaScript objet. On verra qu'en JavaScript, une fonction est un objet, une variable un attribut et une fonction dans une fonction: une méthode !

Le $(document).ready(function() {.....}); est une manière d'exécuter un bout de code uniquement quand le DOM est prêt. C'est une méthode proposée par jQuery pour remplacer les <body onload=...> et autres document.addActionListener("load", init());...

<script type="text/javascript">
// Run when the DOM is ready
$(document).ready(function () {
  // Create the pseudo object which will handle the main canvas
  paint = new PaintObject("canvasMain");
});
</script>

Le paramètre ("canvasMain") est l'Id du tag <canvas> que vous trouverez un peu plus loin dans la page, dans la partie "HTML" :

<canvas id="canvasMain">
    <p>Canvas tag not supported by your browser</p>
</canvas>

Regardons maintenant le code de l'objet PaintObject dans le fichier paint.js ... Tout d'abord on voir qu'on récupère une référence sur le canvas, et aussi sur le contexe associé à ce canvas. Ce contexte contient les méthodes et propriétés pour dessiner dans le canvas.

function PaintObject(canvas) {
    // get handle of the main canvas, as a DOM object, not as a jQuery Object.
    var mainCanvas = $("#"+canvas).get(0);
    var mainContext = mainCanvas.getContext('2d');
    ...

On ajoute aussi un écouteur pour les mouvements souris sur le canvas (là c'est fait avec jQuery) :

// Bind event on the canvas
$("#canvasMain").mousemove(this.mouseMoveListener);

this.mouseListener est une méthode de PaintObject qui dessine une ligne à chaque déplacement souris, depuis la position précédente à la position courante :

// Mouse motion listener for drawing lines
    this.mouseMoveListener = function (event) {
        // we delegate the computation of the mouse position
        // to a utility function (in utils.js) as this is not so trivial
        // we must take into account insets, etc.
        var mousePos = getMousePos(mainCanvas, event);

        // Let's draw some lines that follow the mouse pos
        if (!started) {
            previousMousePos = mousePos;
            started = true;
        } else {
            mainContext.beginPath();
            mainContext.moveTo(previousMousePos.x, previousMousePos.y);
            mainContext.lineTo(mousePos.x, mousePos.y);
            mainContext.closePath();
            // draws as wireframe the drawing orders between beginPath() and closePath()
            mainContext.stroke();

            previousMousePos = mousePos;
        }
    };

Remarquez que pour dessiner une ligne on déclare un chemin avec mainContext.beginPath(), on se place le curseur à une position pour "poser le crayon" (moveTo), on trace une ligne depuis cette position jusqu'à une autre (lineTo), et on ferme le chemin (closePath). Le chemin est dessiné réellement par l'appel de mainContext.stroke(). "stroke" = en mode fil de fer, "fill" = en mode remplissage. Dans notre cas on dessine en fil de fer et l'apparence est définie par la valeur courante de mainContext.strokeStyle, par défaut c'est la couleur noire.

Attention : si vous oubliez de fermer le chemin (pas de closePath), alors à chaque mouvement de la souris on redessine le chemin en entier et ce chemin contiendra toutes les lignes depuis le début ! En effet: mainContext.stroke() dessine tous les ordres de dessin depuis l'appel de  context.beginPath() ... si un beginPath() est appelé alors qu'un chemin a déjà été commencé, il est ignoré.

Remarquez aussi comment nous calculons la taille du canvas en fonction de la taille de la fenêtre du navigateur. On aurait pu le faire à l'aide d'une règle CSS avec des pourcentages sur les propriétés width et height du canvas, mais malheureusement cela "grossit" aussi le contenu du canvas et produisant des gros carrés comme lorsqu'on grossit une image. Ainsi il est préférable d'ajuster la taille du canvas depuis du JavaScript :

// Canvas doesnt scale well with '%' in CSS so we use a little trick.
// We give them the size of one of their parent node which can be scalable.
mainCanvas.height = $("#content")[0].clientHeight;
mainCanvas.width = $("#content")[0].clientWidth;

Step 2 : ajout des événements mouseUp, mouseMove, mouseDown, création d'un objet pour le dessin

Allez dans le dossier "step 2". Vous pouvez tester cet exemple en ouvrant paint.html dans votre navigateur.

Cette fois-ci on ne dessine que lorsque le bouton de la souris est enfoncé.

On a aussi séparé le code qui effectue le dessin du code d'initialisation du PaintObject. Le fichier drawingtools.js contiendra dorénavant les "objets de dessin" comme le crayon, lignes, rectangles, cercles etc... alors que paint.js s'occupe juste de créer les contextes, déclarer les événements etc.

Encore une fois on utilise jQuery pour les événements (paint.js):

// Create the drawing tool
var drawingTool = new pencilDrawingTool(); 

// Bind events on the canvas
$("#canvasMain").mousedown(drawingTool.mouseDownListener);
$("#canvasMain").mousemove(drawingTool.mouseMoveListener);
$("#canvasMain").mouseup(drawingTool.mouseUpListener);

Voici le contenu du fichier drawingtool.js avec simplement in outil qui dessine en mode "crayon". Remarquez les trois écouteurs pour les événements souris:

// Previous position of the mouse
var previousMousePos;

// Drawing tool object
function pencilDrawingTool() {
    this.mouseDownListener = function (event) {
        paint.started = true;
        previousMousePos = getMousePos(paint.getFrontCanvas(), event);
    };

    this.mouseMoveListener = function (event) {
        // we delegate the computation of the mouse position
        // to a utility function as this is not so trivial
        var mousePos = getMousePos(paint.getMainCanvas(), event);

        // Let's draw some lines that follow the mouse pos
        if (paint.started) {
            paint.getMainContext().beginPath();
            paint.getMainContext().moveTo(previousMousePos.x, previousMousePos.y);
            paint.getMainContext().lineTo(mousePos.x, mousePos.y);
            paint.getMainContext().closePath();
            paint.getMainContext().stroke();
        }
        previousMousePos = mousePos;
    };

    this.mouseUpListener = function (event) {
        paint.started = false;
    }
};

Step 3 : ajout d'un second canvas transparent superposé au canvas principal, pour dessiner des "lignes élastiques"

Allez dans le répertoire "step 3" directory. Vous pouvez tester cet exemple en ouvrant paint.html dans votre navigateur.

Cet exemple montre une utilisation très intéressante des canvas: leur utilisation en "overlay" ou si vous préférez, comme des "calques photoshop". En utilisant des propriétés CSS de positionnement relatif et en donnant à la propriété z-index de chaque canvas une valeur différente on peut choisir lequel est devant l'autre. Par défaut les canvas sont transparents. Si on efface une partie d'un canvas à l'aide de la méthode  context.clearRect(x, y, width, height) alors on pourra "voir à travers cette partie".

On va utiliser cet effet pour dessiner des lignes "élastiques" (des lignes qui suivent la souris) dans un canvas transparent, le "front canvas", placé devant le canvas normal (le "main canvas").

Pour animer la ligne qu'on trace quand la souris bouge, on efface le canvas, on dessine la ligne, on efface le canvas, on dessine la ligne à la nouvelle position, etc... 

Adding a new canvas on top of the previous one

This is done in paint.js :

// Prepare a second canvas on top of the previous one, kind of second "layer" 
 var frontCanvas = document.createElement('canvas');
 frontCanvas.id = 'canvasFront';

 // Add the temporary canvas as a second child of the mainCanvas parent.
 mainCanvas.parentNode.appendChild(frontCanvas);

// Get the context for drawing in the canvas
var frontContext = frontCanvas.getContext('2d');

this.getFrontCanvas = function () {
        return frontCanvas;
}

this.getFrontContext = function () {
        return frontContext;
}

We added a line drawing tool in drawingtools.js

We declared an array that will hold the different drawing tools (pencil, line, etc) :

var DEFAULT_TOOL = 'pencil';
...
// Create the drawing tool
var drawingTool = new setOfDrawingTools[DEFAULT_TOOL]();

And the event binding has been modified in order to call the current drawing tool mouse listener method :

// bind events. We use a function multiplexEvent that will call the proper listeners
// methods of the currentTool.
this.bindMultiplexEvents = function () {
    $("#canvasFront").mousedown(this.multiplexEvents);
    $("#canvasFront").mousemove(this.multiplexEvents);
    $("#canvasFront").mouseup(this.multiplexEvents);
}

// if currentTool is pencil, and event.type is mousemove, will
// call pencil.mousemouve(event), if currentTool is line and
// event.type is mouseup, will call line.mouseup(event) etc.
this.multiplexEvents = function (event) {
    drawingTool[event.type](event);
}

...notice that in the line drawing tool (drawingtools.js) we draw only in the front canvas, and before we clear the front canvas :

// the Line Drawing Tool Object
setOfDrawingTools.line = function () {
    this.mousedown = function (event) {
        paint.started = true;
        previousMousePos = getMousePos(paint.getFrontCanvas(), event);
    };

    this.mousemove = function (event) {
        var mousePos = getMousePos(paint.getFrontCanvas(), event);
        if (paint.started) {
            // Clear front canvas before drawing the elastic line in a new position
            paint.getFrontContext().clearRect(0, 0, paint.getFrontCanvas().width, 
                                                    paint.getFrontCanvas().height);

            paint.getFrontContext().beginPath();
            paint.getFrontContext().moveTo(previousMousePos.x, previousMousePos.y);
            paint.getFrontContext().lineTo(mousePos.x, mousePos.y);
            paint.getFrontContext().stroke();
            paint.getFrontContext().closePath();
        }
    };

    this.mouseup = function (event) {
        paint.started = false;
    }
};

 In the paint.html file, we added some spans in order to select the current tool (l

<div id="drawCommands">
    <h6>Pick a tool</h6>
    <span id="line">line</span>
    <span id="pencil">pencil</span>
</div>

And in paint.js we detect clicks on them :

// Handle the drawing tools menu. The selected entry value can be 'Pencil',
// 'Line' etc.
this.changeDrawingTool = function () {
    // this.id is the id of the selected menu item
    drawingTool = new setOfDrawingTools[this.id]();
}

// Bind the changeDrawingTool function onClick to every menu items.
$("#drawCommands").find("span").click(this.changeDrawingTool);

Small problem : how can we make the line stay on the drawing when we draw another line ?

Step 4 : draw the front canvas on the main canvas on mouse release when drawing lines

Go into the step 4 directory. You may try the example by opening paint.html in your browser.

This time when we release the mouse button the line is drawn and will stay in the main drawing, even if we draw other elastic lines.

We just draw the front canvas on the the main canvas on mouseUp events.

In paint.js we added a drawFrontCanvasOnMainCanvas() method on the PaintObject :

this.drawFrontCanvasOnMainCanvas = function () {
    mainContext.drawImage(frontCanvas, 0, 0);
    // after copying the front canvas content, we clear it !
    frontContext.clearRect(0, 0, frontCanvas.width, frontCanvas.height);
}

Notice that we use the drawImage() call from a canvas context object. Indeed, we can draw a canvas on a canvas !

And we call this method from the mouseUp() listener method of the line drawing tool :

// The Line Drawing Tool Object
setOfDrawingTools.line = function () {
    ...
    this.mouseup = function (event) {
        paint.started = false;
        paint.drawFrontCanvasOnMainCanvas();
    }
};

Step 5 : adding a rectangle and a Circle tool

Go into the step 5 directory. You may try the example by opening paint.html in your browser.

This time, the same way we did the line tool, we added two new tools : one for drawing elastic rectangles and one for drawing elastic circles.

You may look at these tools in drawingtools.js.

Now that we have a small framework, you can try to add your own tools for drawing ellipses, images etc.

Step 6 : playing with stroke and fill styles, lineWidth, HTML5 new input types (color, range)

Go into the step 6 directory. You may try the example by opening paint.html in your browser.

This time we added new HTML5 input types in the graphic user interface :

<ul>
  <li>Fill color:
      <!-- if running opera you may try : <input id="fillColor" type="color" 
           value="FFFFFF" /> instead of this jscolor input field -->
      <input id="fillColor" class="color" value="FFFFFF" />
   </li>
   <li>Stroke color:
       <!--  if running opera you may try :<input id="strokeColor" type="color" 
             value="FFFFFF" /> -->
       <input id="strokeColor" class="color" value="000000" />
   </li>
   <li>
       <!-- works only in Chrome and Opera, will display a standard input text in Firefox -->
        Stroke size: <input id="strokeSize" type="range" 
                            min="0" max="20" value="1" step="0.5" 
                            style="position:relative;top:6px;" />
   </li>
   <li>
       Fill shapes : <input id="fillShapes" type="checkbox" checked />
   </li>
</ul>

We used a <input type="range"... /> for selecting the line width. This will work in Opera, Webkit based browser. It is not yet supported by Firefox, but will work nevertheless if you enter a numeric value.

For color selection we used a "polyfill", a <input class="color" /> that works with the jscolor.js library. If you are running Opera, the HTML5 <input type="color" /> will work and display a nice color chooser natively. Just modify the HTML code and open in Opera.

In paint.js we handle the values of these fields in order to keep the contexts for both canvases (mainCanvas and frontCanvas) up to date :

// Handle the color menus
    mainContext.strokeStyle = frontContext.strokeStyle = "#" + $("#strokeColor").val();
    $("#strokeColor").change(function() {
        mainContext.strokeStyle = frontContext.strokeStyle = "#" + $("#strokeColor").val();
    });

    mainContext.fillStyle = frontContext.fillStyle = "#" + $("#fillColor").val();
    $("#fillColor").change(function() {
        mainContext.fillStyle = frontContext.fillStyle = "#" + $("#fillColor").val();
    });

    // handle the stroke size
    mainContext.lineWidth = frontContext.lineWidth = $("#strokeSize").val();
    $("#strokeSize").change(function() {
        mainContext.lineWidth = frontContext.lineWidth = $("#strokeSize").val();
    });

    var fillShapes = true;
    // handle the check box that specifies if we fill shapes
    //this.fillShapes = $("#fillShapes").attr('checked');
    $("#fillShapes").change(function(){
        fillShapes = $(this).attr("checked");
    });

Like that, all the calls to stroke() or fill() using any of the contexts will draw a shape using the selected corresponding color.