Introduction
Suite a mon article expliquant que HTML5 allait devenir le Golden Language pour faire du B2C, il me fallait réagir et vous proposer un post sur le sujet!
Ce billet est a destination des développeurs .NET/XAML souhaitant passer sur tout ce qui gravite autour de HTML5.
Le but sera de concevoir une sorte de “home” a la Windows 8, avec des tuiles pour afficher les derniers billets d’un flux RSS, avec version pour mobile/tablette ainsi qu’une version offline et Windows 8.
Mais pour bien commencer, je vous recommande de regarder cette vidéo pour avoir une vue d’ensemble de ces langages:
Rendez vous des Experts - Découverte de HTML5
Fonctionnalité et design
Le but de cette application va être de créer un site d’agrégation:
- Un design métro, avec un fond avec photo aléatoire
- Une compatibilité IE10/Mozilla/Chrome/Tablette iPad et Android
- La possibilité d’ajouter la page en favoris, favicon and co
- Deux box, l’une présentant les flux RSS de mes deux blogs
- L’autre étant des liens vers projets et autres
- Le tout dans des tuiles animées en CSS3
- Version offline et stockage des articles hors ligne
- Utilisation d’un maximum de features HTML5
- Version spéciale compilée Windows 8 (sera l’objet d’un autre poste)
- Version optimisée pour téléphone avec Media Queries et KendoUI
- Utilisation d’un max de Framework du moment comme LESS/Knockout.js and co
- Consommation de toutes les données sur un service Node.js
Développement
Création de la structure de la page en HTML5
La première chose que je vais réaliser, est la structure de la page. HTML5 apporte son lot de nouvelles balises comme <header>, <nav> et bien d’autres mais cela ce comporte comme des divs.
Ma page HTML5 va ressembler à cela:
La structure reste simple: un header, un footer et au milieu un contenu représenté par une bullet liste.
Le header prendra cette forme:
Le footer, tout aussi simple:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14 <header id="headertop"> <div id="backgroundtitle"> <img src="Assets/images/logo.png" /> </div> <div id="title"> <img src="Assets/images/logo2.png" /><label> Home</label></div> <div id="loginbox"> <div id="username"></div> </div> </header>
Et au milieu, une bullet liste, avec pour chaque petites tuiles une <div>:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5 <footer id="bottomfooter"> <div id="plusmenu"> <img src="Assets/images/heart.png" /></div> </footer>
Organisation de la page avec CSS3
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11 <div id="box3"> <nav class="navitems , secondnavitems"> <header class="headerclass">Projects</header> <ul> <li> <div class="box"> <div class="image"><img src="" alt="" /></div> <div class="nomapp">Blog</div> </div> </li> //etc...
Avant de commencer, je vous conseil vivement de travailler avec LESS. LESS est un framework JS ou un compilateur CSS permettant d’améliorer le langage. Il est désormais possible de déclarer des variables, faire des conditions, de l’héritage… Pour l’exploiter plusieurs solutions: soit en JavaScript, qui se chargera au runtime de “compiler” le CSS. Soit en utilisant un outil qui le fera avant la publication du site. Si vous êtes sous Visual Studio, une extension existe: Dotless mais j’ai un petit faible pour http://crunchapp.net/.
Le CSS3 apporte son lot de nouveautés très appréciables.
J’ai eu l’occasion d’en utiliser quelques unes:
Importation de font
Personnalisation d’une scrollbar
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7 @principalfont: Segoe UI; @font-face { font-family:@principalfont; src: url('../Font/SEGOEUI.TTF'); }
Utilisation des animations
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 ::-webkit-scrollbar { width: 12px; background-color:Gray; } ::-webkit-scrollbar-track { -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3); border-radius: 10px; } ::-webkit-scrollbar-thumb { border-radius: 10px; -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.5); }
Sélection de contrôles et états
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8 .anim { -webkit-animation: monanim 80s infinite; -moz-animation: monanim 80s infinite; -ms-animation: monanim 80s infinite; -o-animation: monanim 80s infinite; animation: monanim 80s infinite; }
Utilisation de Box pour aligner les éléments
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9 #username:hover { opacity: 1; } .navitems > ul > li:nth-child(3n) > div { background-color: #db532b; }
Transformations 3D pour simuler un effet tilt:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 #site { display: box; /* not supported by IE9, use table if we want IE support */ box-orient:horizontal; box-pack:left; box-align:top; /* Firefox */ display:-moz-box; -moz-box-orient:horizontal; -moz-box-pack:left; -moz-box-align:top; /* Safari and Chrome */ display:-webkit-box; -webkit-box-orient:horizontal; -webkit-box-pack:left; -webkit-box-align:top; /* futur IE10, that's sucks */ display: -ms-box; }
Résultat avec le CSS:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13 .tilt { -webkit-transition: -webkit-transform 0.1s linear; -webkit-transform: rotate3d(20, 100, 0, -20deg); -moz-transition: -moz-transform 0.1s linear; -moz-transform: rotate3d(20, 100, 0, -20deg); -o-transition: -o-transform 0.1s linear; -o-transform: rotate3d(20, 100, 0, -20deg); -ms-transition: -ms-transform 0.1s linear; -ms-transform: rotate3d(20, 100, 0, -20deg); transition: transform 0.1s linear; transform: rotate3d(20, 100, 0, -20deg); }
Quelques codes JavaScript ont du être ajoutés pour mettre en forme certaines choses, par exemple pour coller la barre de défilement par rapport a la div <content>:
Avant, après
A ce stade, l’intégralité du code se trouve ici:
http://julien.dollon.net/html5/beta/
Vous pouvez le tester exclusivement avec Mozilla/Chrome/IE11 car mon composant Box n’est supporté que par les derniers navigateurs.
Capter le scroll de la souris et le clavier
J’ai voulu que le scroll de la souris provoque non pas le défilement en vertical de la page, mais en horizontal.
Pour cela il a fallut capter MouseWheel de la souris, trouver le sens, annuler la propagation de l’évènement pour annuler le défilement de la fenêtre et simuler le défilement de manière horizontal sur la div principale.
Il peut être aussi intéressant de capter les évènements du clavier pour reproduire la même action. Forcement comme dab’ chaque navigateurs implémentent les évènements comme ils le veulent…
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 var iScroll = 0; var lastScroll = 0; $('#content').bind('mousewheel', function(event, delta) { $(this).scrollLeft(100 * -delta + (100 * iScroll)); //Si a la fin if (!($(this).scrollLeft() == lastScroll)) { iScroll += -delta; } lastScroll = $(this).scrollLeft(); event.stopPropagation(); });
Proposer un bouton “fullscreen”
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14 $(document).bind('keydown', function (event) { event = event || window.event; var key = event.keyCode ? event.keyCode : event.which ? event.which : event.charCode; switch (key) { case 37: // left arrow key ScrollAnimated(5); break; case 39: // right arrow key ScrollAnimated(-5); break; } event.stopPropagation(); });
Voici un petit script pour mettre le site en fullscreen.
Création d’une tuile avec Adobe Edge
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 function InitFullScreenButton() { $("#pluscentre").click(function () { //Chrome/Safari if (document.body.webkitRequestFullScreen != null) document.body.webkitRequestFullScreen(); //Mozilla else if (document.body.mozRequestFullScreen != null) document.body.mozRequestFullScreen(); //Opera else if (document.body.oRequestFullScreen != null) document.body.oRequestFullScreen(); //IE11 else if (document.body.msRequestFullScreen != null) document.body.msRequestFullScreen(); else if (document.body.requestFullscreen != null) document.body.requestFullscreen(); else alert("Your bullshit browser doesn't support the fullscreen features"); });
Pour la tuile, il existe deux solutions.
Dans un premier temps, je vais utiliser Edge en Preview de Adobe afin de vous montrer les capacités de ce logiciel. Edge est une sorte de Expression Blend version HTML5/JavaScript (jQuery pour être plus précis).
L’avantage de cet outil est qu’il ressemble fortement a Blend. L’inconvénient est que les animations ne sont pas faites pour être éditées par la suite, c’est pourquoi, dans une second temps, je préfère opte pour une tuile faite main.
Apres avoir lancé Edge, je crée un nouveau projet et je commence par dessiner un petit rectangle représentant ma tuile:
![]()
Je configure mon rectangle (qui est en réalité une div) grâce au panneau de gauche.
Changement de curseur par une main.
Positionnement en haut a gauche.
Mise en place du “Clip” pour que tout ce qui déborde de la div ne s’affiche pas.
Changement de la couleur de fond.
J’insère ensuite un texte et une image avec cette hiérarchie:
![]()
Puis j’anime la div appelée secondRect en enregistrant son positionnement en Y. Pour cela, il faut faire un clique droit sur secondRect->Add Key Frame->Translate(Y).
Puis grâce au storyboard viewer du bas, j’anime le tout.
Il faut ensuite mettre un peu d’easing animation pour rendre le tout plus naturel.
Ainsi qu’un trigger pour capter la fin de l’animation et la relancer.
Création de la tuile “a la main”
Pour cela, j’utilise les animations CSS3, c’est a dire un KeyFrame definissant une translation sur une division.
En HTML, la tuile est représentée comme ceci:
L’objectif étant d’avoir ceci (3 états, la durée de l’animation variant suivant la tuile):
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4 <div class="box"> <div class="imageXL"><img src="assets/images/windows.png" alt="" /></div> <div class="nomappXL">Kinect</div> </div>
L’animation CSS3 est définie de cette manière:
La div “mère” est clipped avant de ne pas voir l’image déborder lors de la translation:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 @keyframes tuileTranslation { 0% { transform: translateY(0em); } 25% { transform: translateY(-3.75em); } 75% { transform: translateY(-7.5em); } 100% { transform: translateY(0em); } }
Et le JavaScript s’occupe d’initialiser l’image de la div pour la mettre en “background” (plus facilement manipulable pour moi). Il lance aussi l’animation CSS3 avec un temps “aléatoire”:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7 .box > .image { width:80px; height:80px; margin: auto; clip:rect(15px, auto, auto, auto); }
Mise en place d’un fond METRO
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 $(".imageXL").each(function () { /* animation for tiles with random duration */ var randomnumber = Math.floor(Math.random() * 6) + 1; $(this).css('animation', 'tuileTranslation ' + (10 + randomnumber).toString() + 's infinite') .css('-moz-animation', 'tuileTranslation ' + (10 + randomnumber).toString() + 's infinite') .css('-webkit-animation', 'tuileTranslation ' + (10 + randomnumber).toString() + 's infinite') .css('-ms-animation', 'tuileTranslation ' + (10 + randomnumber).toString() + 's infinite') .css('-o-animation', 'tuileTranslation ' + (10 + randomnumber).toString() + 's infinite') .next(".nomappXL") .css('animation', 'tuileTranslation ' + (10 + randomnumber).toString() + 's infinite') .css('-moz-animation', 'tuileTranslation ' + (10 + randomnumber).toString() + 's infinite') .css('-webkit-animation', 'tuileTranslation ' + (10 + randomnumber).toString() + 's infinite') .css('-ms-animation', 'tuileTranslation ' + (10 + randomnumber).toString() + 's infinite') .css('-o-animation', 'tuileTranslation ' + (10 + randomnumber).toString() + 's infinite'); /* initilization of the background of the div depending of the image in it */ $(this).find("img:first").css("visibility", "hidden"); var imageUrl = $(this).find("img:first").attr("src"); $(this).css('background-image', 'url(' + imageUrl + ')'); });
Le but est d’afficher de manière random, un léger fond en mouvement.
Nous allons utiliser les animations et transformations CSS compatibles avec les derniers browser du marche (sauf IE9 –> voir IE10).
Mise en place du code HTML:
La div “fond” représente l’image à afficher, le reste du site étant place dans “site”.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10 <body onload="MaMethod();"> <div id="container"> <div id="fond"> <img src="D3B_2156q.jpg" id="img" alt=""/> </div> <div id="site"> <!-- Site ici --> </div> </div> </body>
Body va appeler une fonction JavaScript permettant d’attribuer au chargement de la page, une classe CSS à “fond” qui déclenchera l’animation.
La partie CSS reste simple:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6 <script> function MaMethod() { var div = document.getElementById("fond"); div.setAttribute("class", "anim"); } </script>
Le résultat:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40 body { padding: 0; margin: 0; background-color: black; overflow-y: hidden; } #fond > img { opacity: 0.5; position: fixed; } .anim { animation: monanim 60s infinite; } @keyframes monanim { 0% { transform: translateY(0em); transform: translateX(0em); } 25%{ transform: translate(-15em,-5em); } 75% { transform: translate(-15em, -10em); } 100% { transform: translate(0em, 0em); } } #site { background-color: transparent; width: 2000px; height: 700px; opacity: 0.5; }
http://julien.dollon.net/html5/fond/index.htm
Création du service Node.js
Introduction
Il y a dix ans, quand j’ai comence le JavaScript, la première chose qu’on m’a appris: Le JS tourne cote client.
Et bien plus maintenant! Node.js est l’un des nombreux Framework permettant de faire des services avec JavaScript.
Je conseil l’editeur en ligne si vous voulez tester/debuguer du node.js: http://c9.io/
Les avantages d’utiliser Node.js sont:
Les désavantages:
- Si vous avez du code JS a porter cote serveur, c’est très simple
- Ici pas notre cas
- Si vous êtes un pro du JS/dev web et que vous ne connaissez pas bien un langage d’Homme (
) comme Java ou C#, node.js vous sauve la vie
- Si vous voulez faire un service léger et rapide, restfull, avec CouchDB ou autre, un truc code a l’arrache from scratch c’est pas mal.
Création du service
- C’est du JavaScript. Autant cote client je comprends l’utilisation d’un langage dynamique est intéressant, mais la cote serveur c’est risque des erreurs dans tous les sens
- L’API est clairement instable, et en plus manque d’optimisation (du style réutilise pas le thread courant pour re-exécuter un service etc…)
- Manque de librairies, comment accéder a mon SQL Server? A mon API S3 ou Azure? etc… Les APIs dispos sont très maigres
Le service doit être capable de retourner:
Le tout en JSON avec un service RESTFULL.
- La dernière vidéo du compte Soumow de youtube
- Les 5 derniers articles de mes blogs perso et pro
- Mes 6 contacts réseaux sociaux
- Mes 5 derniers projets
Je pourrais utiliser un routeur de requête type Journey, mais j’ai voulu me débrouiller de A a Z.
Je commence donc par initialiser mon fichier JavaScript:
Mon objet handle contient des pointeurs de fonctions vers les méthodes a appeler.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10 var http = require("http"); var url = require("url"); var querystring = require("querystring"); var handle = { }; handle["/"] = rootCall; handle["/lastarticles"] = lastArticles; handle["/lastprojects"] = lastProjects; handle["/lastvideo"] = lastVideo; handle["/socialnetworks"] = socialnetworks;
Je démarre ensuite un serveur sur le port 8888 et je route le tout (merci au livre sur Node.js).
Résultat, quand j’appelle l’url /socialnetworks, la fonction socialnetworks est appelée.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 function route(handle, pathname, request, response) { console.log("About to route a request for " + pathname); if (typeof handle[pathname] === 'function') { handle[pathname](request, response); } else { console.log("No request handler found for " + pathname); writeFast(response, "Error, no method found"); } } http.createServer(function (request, response) { // var pathname = url.parse(request.url).pathname; console.log("Request for " + pathname + " received."); route(handle, pathname, request, response); }).listen(8888);
De même pour les autres fonctions du service.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13 function socialnetworks(request, response) { response.writeHead(200, { "Content-Type": "text/json" }); var socials = new Array(); socials.push(new DataContractElement("Facebook", "fb.png", "", "https://www.facebook.com/julien.dollon")); socials.push(new DataContractElement("Twitter", "twitter.png", "", "https://twitter.com/#!/juliendollon")); socials.push(new DataContractElement("Google+", "google.png", "", "https://plus.google.com/110722800174049431616/posts")); socials.push(new DataContractElement("Linkedin", "linkedin.png", "", "http://www.linkedin.com/profile/view?id=7903132&trk=tab_pro")); socials.push(new DataContractElement("Resume", "", "", "http://julien.dollon.net/dollon_en.pdf")); socials.push(new DataContractElement("MVP Profil", "", "", "http://www.microsoft.com/france/communautes/annuaire/acteurs.aspx?auteur=Dollon")); response.write(JSON.stringify(socials)); response.end(); }
Pour le téléchargement des divers flux rss j’ai utilise la librairie Future qui permet de faire des ForeachAsync et des .Then() plutôt pratique.
La méthode permettant de retrouver la dernière video YouTube nécessite de faire une requête GET a l’API Youtube.
Mise en place du service
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 var options = { host: "gdata.youtube.com" , path: "/feeds/api/users/" + account + "/uploads?v=2" }; //make the request server side http.get(options, function (resp) { //console.log("hi",response); response.writeHead(200, { 'Content-Type': 'text/plain' }); var pageData = ""; resp.setEncoding('utf8'); //stream the data into the response resp.on('data', function (chunk) { pageData += chunk; }); //write the data at the end resp.on('end', function () { response.write(pageData); response.end(); }); });
Node.js est supporte par Azure mais comme ca coute un bras je vais opte pour un déploiement standard sur mon serveur perso avec IIS7.
Ce lien nous explique comment héberger un service node.js dans IIS:
http://www.amazedsaint.com/2011/09/c...ng-app-on.html
En gros il faut:
Voici le résultat:
- Installer node.js
- Installer ce binaries special IIS 7.X https://github.com/tjanczuk/iisnode
- Installer le module de URLRewrite de IIS http://www.iis.net/download/urlrewrite
http://home.dollon.net/node/service/personalservice.js/
Comme en Silverlight et WCF, il faut initialiser le clientaccesspolicy.
Attention aux failles de sécurité avec une “*” en cas d’injection de code XSS.
Typage du JavaScript et mode strict
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4 response.writeHead(200, { 'Content-Type': 'text/plain', 'Access-Control-Allow-Origin' : '*' });
A ce stade, J’ai commence a utiliser du JS par ci par la mais je n’ai pas fait attention au best practices.
Tout d’abord en terme d’architecture mais j’en parlerai a l’étape suivante.
Mais aussi en terme de typage et propreté du code.
Il existe un mode en JavaScript (ECMAScript 5) qui permet d’être dans un contexte plus stricte que celui de d’habitude. Cet environnement impose certaines règles de développement et désactives des fonctionnalités de JS pouvant être dangereuse.
Pour l’utiliser il faut rajouter:
Au niveau du scope du code a protéger.
Code : Sélectionner tout - Visualiser dans une fenêtre à part <code>"use strict";</code>
Désormais, si j’utilise une variable non déclarée, j’aurai une erreur dans la console JavaScript:
Plus d’infos sur le mode strict ici:
http://hacks.mozilla.org/2011/01/ecm...-in-firefox-4/
Une deuxième chose qui existe et qui améliore le code JavaScript est TypedJS.
TypedJS permet d’annoter nos fonctions avec un commentaire précisant le type de retour et des params.
Ce commentaire est lu par des le lancement dans une console JS de “TypedJS.run_tests()”.
A noter que TypedJS supporte Node.js.
Mise en place de l’architecture avec Knockout.js
Depuis le début, je n’utilise aucune architecture. Normal cote front end, puisque que ce soit en MVVM en Silverlight ou JS, tant que le code touche des aspects purement graphiques, je le laisse dans la vue.
Sauf que la, je vais avoir du code pour consommer mon service! Je décide donc de partir sur Knockout.js.
Un seul lien pour l’apprendre: http://learn.knockoutjs.com/
Knockout fournis un framework MVVM avec tout ce qui tourne autours comme le Binding, Observables…
Pour l’installer, tout est explique ici: http://knockoutjs.com/documentation/installation.html
Notre ViewModel a besoin d’exposer:
Tous les liens auront le même model (attention au context “this” qui peut bouger):
- Une propriété représentant la vidéo
- Une collection de projets
- Une collection de réseaux sociaux
- Une collection d’articles
- Une propriété IsBusy pour dire que le chargement est en cours
- Une prop pour afficher si il y a une erreur
On crée le ViewModel, ici par exemple avec une propriété Observable qui sera l’url vers la vidéo youtube:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7 function DataContractElement(title, image, description, link) { var self = this; self.title = title; self.image = image; self.description = description; self.link = link; }
On instancie, dans le load de notre page, le ViewModel de cette façon:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 "use strict"; function principalViewModel() { var self = this; self.isBusy = ko.observable(true); self.videoUrl = ko.observable(""); self.error = ko.observable(""); self.projects = ko.observableArray([]); self.sociallinks = ko.observableArray([]); self.lastarticles = ko.observableArray([]); $.ajax({ type: "GET", url: lastvideourl, success: function (json) { var mapped = new DataContractElement( json[0].title, json[0].image, json[0].description, json[0].link); if (mapped.title == null) {//Bug Firefox mapped = new DataContractElement( JSON.parse(json)[0].title, JSON.parse(json)[0].image, JSON.parse(json)[0].description, JSON.parse(json)[0].link); } self.videoUrl(mapped.link); self.isBusy(false); }, error: function (xhr, textStatus, errorThrown) { self.error(xhr.responseText); } }); }
Et cote vue, on réalise un binding sur l’iframe de Youtube:
Code : Sélectionner tout - Visualiser dans une fenêtre à part ko.applyBindings(new principalViewModel());
De même pour la liste des articles, sauf que cette fois ci je travaille avec un observableArray et des templates.
Code : Sélectionner tout - Visualiser dans une fenêtre à part <iframe data-bind="attr: {src: videoUrl}"></iframe>
Je termine avec la capture de l’évènement clic et ouverture du lien dans une popup:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3 <ul data-bind="foreach: lastarticles"> <li>Name: <span data-bind="text: title"> </span> </ul>
Téléchargement des images
Code : Sélectionner tout - Visualiser dans une fenêtre à part <li data-bind="click: $parent.launchLink">
Dans la version non public du site, j’ai voulu utiliser le web worker (un thread JS grâce a HTML5) pour télécharger les images de fond.
Le problème est que très peu de navigateur le supporte, mais si vous souhaitez voir comment j’ai fais pour downloader une image, c’est par ici:
http://www.html5rocks.com/en/tutoria...lesystem-sync/
Splash screen
Tout d’abord, je souhaite dessiner mon logo.
Pour cela deux options s’offre a moi:
Le canvas, qui va générer un bitmap, c’est a dire quelque chose de “plat” et fixe en 2d tel une image. Par default on dessine en 2D mais WebGL permet de faire de la 3D. On l’utilise avec une balise en HTML5 et on pilote le dessin avec JavaScript.
Les points positifs: performant, possibiliter d’exporter le resultat en image.
De l’autre cote, nous avons un format vectoriel, SVG, base sur du XML.
Les points positifs: j’ai déjà au format Illustrator mon logo, facile a convertir en SVG, facilement animable.
Je decide donc de partir sur SVG. Je rajoute une progressbar (nouveauté HTML5) avec un petit skin CSS3 et un binding en MVVM sur la propriété isBusy:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 <div id="loadingpage" data-bind="visible: isBusy"> <!----> <svg version="1.1" id="svglogo" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/" x="0px" y="0px" width="500px" height="500px" viewBox="-96.46 -235.46 595 842" xml:space="preserve"> <defs> </defs> <polygon fill="#FFFFFF" stroke="#000000" stroke-miterlimit="10" points="115.874,202.54 115.874,65.874 180.54,1.207 180.54,360.54 0.707,180.707 47.707,133.707 "/> <polygon fill="#FFFFFF" stroke="#000000" stroke-miterlimit="10" points="214.54,360.54 395.207,180.874 214.54,1.207 214.54,91.874 304.874,180.874 214.54,271.207 "/> </svg> <div id="loadingElement"> <progress max="100" data-bind="value: pourcentage"> </progress> <span data-bind="text: pourcentage"></span> </div> </div>
Un hack en CSS?
A ce stade, on a presque fini… Mais on va pas se leurrer, seul peu de navigateurs le supporte.
Le constat est le suivant:
Normalement, je devrais faire plusieurs fichiers CSS et des hack JS pour que tout fonctionne sur tous les navigateurs… mais j’ai pas envi!
- Aucun pb pour Mozilla
- Aucun pb pour WebKit (Chrome, Safari)
- Opera chie dans la colle
- IE9 a des pbs aussi (mais IE10 fonctionne)
Bon je sais bien, on peut pas dire ca entreprise mais comme c’est mon site perso, je m’en fou.
Donc direction modernizr, un petit outil JS me permettant de tester les capacités d’un navigateur.
http://www.modernizr.com/
Je configure donc toutes les features dont j’ai besoin:
A ce stade, voici le rendu:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 function VerifyBrowserCapacities() { Modernizr.load(); var listOfNonSupported = []; for (var propertyName in Modernizr) { if (!Modernizr[propertyName]) listOfNonSupported.push(propertyName); } if (listOfNonSupported.length > 0) { var root = window.location.toString().toLowerCase().replace("index.html", ""); //alert(root.split('')[root.length - 1]); if (root.split('')[root.length - 1] != '/') root += '/'; //alert(listOfNonSupported.toString()); window.location = root + "notsupported.html?error=" + listOfNonSupported.toString(); } }
http://home.dollon.net/node/website3/
Utilisation de Media en CSS
Les media queries permettant de filtrer le CSS suivant le device.
Je vais l’utiliser pour imposer une résolution minimum.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 @minwidth: 800px; @minheight: 700px; @media screen and (max-width: @minwidth) or (max-height: @minheight) { .popuppage { .popuppageabstract; visibility:visible; } } @media screen and (min-width: @minwidth) and (min-height: @minheight) { .popuppage { .popuppageabstract; visibility:hidden; } }
De même, mes images de fonds ont une taille maximum de 2598x1417 + l’effet de translation. Sur de gros écrans ou sur un iPad, le résultat n’est donc pas terrible. Pour cela je désactive le background pour ces résolutions trop haute.
Une autre problématique, qui ne se résout pas avec le CSS mais que je met ici quand même: le viewport.
Il faut pouvoir empêcher les tablettes de pouvoir zoomer/dezoomer et forcer le scale de base de la page.
une autre chose importante, est d’imposer a l’utilisateur le mode landscape. Mon site rendant clairement mieux tout en longueur.
Code : Sélectionner tout - Visualiser dans une fenêtre à part <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1, maximum-scale=1, user-scalable=no"/>
Pour forcer l’utilisateur, je pourrai utiliser body[orient="landscape"] en CSS mais cette propriété ne marche pas partout.
J’utilise donc un petit hack JavaScript:
Version offline et stockage des articles hors ligne
Code : Sélectionner tout - Visualiser dans une fenêtre à part <body onload="OnLoad();" onorientationchange="updateOrientation();">
L’objectif de la version offline va être de pouvoir montrer mon beau site quand j’ai pas internet.
Je vais devoir mettre en cache, la page, les scripts, les images mais aussi les données venant du service.
Pour les données j’ai le choix entre les stockes dans un indexeddb ou dans le webstorage. Indexeddb n’est pas encore bien supporte par Safari, j’opte donc pour le second.
Plus d’infos sur ces types de storages ici:
http://stackoverflow.com/questions/6...dexed-database
Pour commencer, je vais mettre en cache tout le site grâce a l’applicationcache feature.
- Mise a jour du web.config pour gérer les fichiers manifest
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12 <configuration> <system.web> <httpHandlers> <add verb="GET,HEAD" path="*.appcache" type="System.Web.StaticFileHandler" /> </httpHandlers> </system.web> <system.webServer> <staticContent> <mimeMap fileExtension=".appcache" mimeType="text/cache-manifest" /> </staticContent> </system.webServer> </configuration>
- Création du fichier manifest qui décrit les éléments accessibles offline
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 CACHE MANIFEST # version 1 CACHE: Index.html notsupported.html favicon.ico Font/SEGOEUI.TTF Assets/less.css Assets/jquery-1.5.1.min.js Assets/jquery.mousewheel.min.js Assets/principalViewModel.js Assets/wallpaper/0.jpg Assets/wallpaper/1.jpg Assets/wallpaper/2.jpg Assets/images/book.png Assets/images/df.png NETWORK: *
- Eviter les conflits and client et browser caching en désactivant le caching du browser:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2 <meta http-equiv='Pragma' content='no-cache'> <meta http-equiv='Expires' content='-1'>Il faut maintenant stocker les données du service dans le storage.
- Tester!
Le webstorage propose deux types de stores: local et Session.
Local storage fonctionne comme un cookie, l’information pouvant être récupérer a tout moment alors que la session a une durée de vie plus limitée.
Pour mon cas, ca sera donc le localstorage (taille entre 2 et 10mo, amplement suffisant).
Désormais, même sans internet, ma page sera accessible.
Simplifier les fichiers JS et CSS
Me voici dans la phase avant déploiement: quelques petites choses sont encore a finaliser:
Ce compilateur de Google se décrit comme un compilateur qui compile du JS en JS, mais un JS plus rapide, plus optimisé.
- Compiler le CSS avec LESS pour le simplifier
- Eventuellement compiler le JavaScript avec https://developers.google.com/closure/compiler/
Pour faire un test:
http://closure-compiler.appspot.com/home
Pour ma part:
- Nettoyer le HTML (et si vous avez le courage, le faire passer W3C)
- Optimiser les performances
Résultats
Résultat sur PC
WTF WINDOWS ON A MAC!!!!
Grace au site http://browserling.com/ je peux émuler le site web sur tous les navigateurs.
Tous les derniers navigateurs de la famille IE, Chrome, Safari et Mozilla sont capables de l’afficher correctement.
Le site final:
home.dollon.net
Résultat sur tablette
Android
iPad
Windows 8
Conclusion
HTML5 pour le web est la, s’apprend assez vite et est un plaisir a utiliser.
Cependant, il n’est la que depuis peu, car seul les derniers navigateurs (encore parfois en beta) le supporte.
Internet Explorer est le navigateur un peu en retard par rapport aux autres. Mais le 10 corrige tout. C’est donc pour la sortie de IE10 RTM (fin 2012/début 2013) que je conseillerai le développement HTML5. D’ici la, nous avons le temps de monter en compétence.
HTML5: Oui!
Avant 2013? Non!
Billet original du blog de Julien Dollon
Partager