Pages

Closure encore et encore

JS Bin

JS Bin
Vous pouvez écrire vous même du code pour animer vos pages (exemple). Mais aujourd'hui, de nombreuses bibliothèques proposent des effets d'animation (exemple).



Introduction

Etudier le code permettant de rendre un effet sur un élément.

function effet( elem ) {

    // init : Start for example the slide down or the opacity at 0
    fx(0);

    // Show the element (but you can't see it, since the height or opacity is 0
    show( elem );


    // We are going to do a 20 frame animation that takes
    // place over one second
    for ( var i = 0; i <= 100; i += 5 ) {
        // A closure to make sure that we have the right i
        (function(){
            var pos = i;

            // Set the timeout to occur at the specified time in the future
            setTimeout(function(){

                // Set the new height  or opacity of the element
                fx(pos);

            }, ( pos + 1 ) * 10 );
        })();
    }
}

Exemple

Dans le code précédant remplaçons fx() par  elem.style.height.

function slideDown( elem ) {
    // Start the slide down at  0
    elem.style.height = '0px';

    // Show the element (but you can't see it, since the height is 0)
    show( elem );

    // Find the full, potential, height of the element
    var h = fullHeight( elem );

    // We are going to do a 20 frame animation that takes
    // place over one second
    for ( var i = 0; i <= 100; i += 5 ) {
        // A closure to make sure that we have the right i
        (function(){
            var pos = i;

            // Set the timeout to occur at the specified time in the future
            setTimeout(function(){

                // Set the new height of the element
                elem.style.height = (( pos / 100 ) * h ) + "px";

            }, ( pos + 1 ) * 10 );
        })();
    }
}

Fade-in

On cherchera à remplacer  fx par une fonction permettant de créer un effet d'apparition.


        (function(pos){
            setTimeout(function(){
                elem.style.height = (( pos / 100 ) * h ) + "px";
                elem.style.width = (( pos / 100 ) * h ) + "px";
                elem.style.opacity=  pos / 100 ;
            }, ( pos + 1 ) * 10 );
        })(i);


Vous pouvez voir ce code en action (ici).

Objet Message : in action

Pour afficher dans un jeu des messages de début ou de fin de jeu, nous pouvons définir un objet.

L'idée principale est de regrouper en une partie commune "create" l'ensemble des fonctions à répéter pour chaque message.

var Screen = {
        welcome: function() {
            // Setup base values
            this.text = 'SORT the Buddles';
            this.textSub = 'find the criteria';
            this.textColor = 'red';
            // Create screen
            this.create();
        },

        gameover: function() {
            this.text = 'We have a Winner';
            this.textSub = 'Winner !';
            this.textColor = 'red';

            this.create();
        },

        create: function() {
            // Background
            ctx.fillStyle = 'black';
            ctx.fillRect(0, 0, canvas.width, canvas.height);

            // Main text
            ctx.fillStyle = this.textColor;
            ctx.textAlign = 'center';
            ctx.font = '40px helvetica, arial';
            ctx.fillText(this.text, canvas.width / 2, canvas.height / 2);

            // Sub text
            ctx.fillStyle = '#999999';
            ctx.font = '20px helvetica, arial';
            ctx.fillText(this.textSub, canvas.width / 2, canvas.height / 2 + 30);
        }
    };

Collision simple : how to



Principe


Mettons en place la détection de collision entre deux éléments du jeu. 

Pour simplifier notre recherche, nous disons qu'il y a collision si le rond noir rentre dans le carré noir.
Les figures sont dessinées sur l'image d'illustration suivante.

Les coordonnées du point noir sont les coordonnées de la princesse {girl.x, girl.y} et les coordonnées du carré sont les coordonnées du joueur plus un delta {player.x-deltaX, player.y+deltaY}.

La précision est contrôlable en fonction des valeurs du delta qui augmente ou diminue la zone du carré noir.

Sur la figure suivante, le rond noir est dans le carré, la condition de collision est vrai, la princesse va se transformer !

Code

Pour écrire notre programme, nous pouvons faire appelle à la fonction

     function xBetween(x,a,b){
         if(x>a && x<b)
             return true;
         else
             return false;
     }

function checkCollisions(){
allGirls.forEach(function(girl) {
             if(xBetween(girl.x,
                         player.x-deltaX,
                         player.x+deltaY) && 
                xBetween(girl.y,
                         player.y-deltaX,
                         player.y+deltaY)){
                 //collision do something
}
});
}


Game : how to


Pour réaliser un jeu, nous devons nous rappeler la conception des livres flip.

L'illusion est presque parfaite, seul le personne bouge dans un décor figé.


Pour obtenir ce résultat, nous avons page par page redessine entièrement le décor sur chaque page.


La logique de création sera :
>tourner la page
bouger le personnage
dessine le personnage
dessine le décor
> tourner la page
bouger le personnage
dessine le personnage
dessine le décor

Passons à notre programme de jeu.

La logique précédente va se traduire par les lignes de code suivantes

function main() {
        update();
        render();
        win.requestAnimationFrame(main);
};

La fonction requestAnimationFrame est chargée de tourner la page. La fonction main est la fonction de callback appelée avant que le navigateur effectue une nouvelle animation. 

Cette logique de base varie peu. Elle peut être isolée dans un fichier dit "engine".

remarque : Ce fichier deviendra vite une véritable bibliothèque contenant des outils de création et de gestion de votre jeu. Il doit exister aujourd'hui une bonne trentaine de bibliothèque de jeu.


https://github.com/dupontdenis/Game-move.git


Pour aller plus loin  :

Pour tenir compte des variations de fréquences, nous introduisons un delta time.

Les fonctions de base

JS Bin

les fonctions de base

'#'+'0123456789abcdef'.split('').map(function(v,i,a){ return i>5 ? null : a[Math.floor(Math.random()*16)] }).join('');

Est ce un code secret ?

Pour le comprendre : Décomposons le code en étape

  1. const values = '0123456789abcdef';
  2. const tab = values.split('');
  3. let random = tab.map((v,i,a) => i>5 ? null : a[Math.floor(Math.random()*16)]);
  4. let colorRandom = '#'+random.join("");
Lig. 3 transforme les 6 premiers éléments du tableaux en des nombres <16.

En action

Il vous suffit ensuite de copiez la valeur de colorRandom et de tapez dans votre console
  1. document.body.style.background = "#848250"


Start game !

JS Bin

https://github.com/dupontdenis/Game-useGlobal.git

Flex : A la loupe

Cas d'augmentation-

p:nth-of-type(2n+1) {//bleu
  flex : 1 1 25%; 
}

p:nth-of-type(2n) {//rouge
  flex : 1 1 50%; 
}

voir -> fichier test

Dans ce cas, la somme des pourcentage est inférieur à 100% (la taille du conteneur), il y a augmentation ; les chiffres rouges sont pris en compte.

Chaque bloc va augmenter proportionnellement de la même valeur.

Ainsi, si le body fait 1000 px, nous aurions eu le bloc bleu (25%) à 250 px et le bloc rouge (50%) à 500 px. Il y a une augmentation de 1000 - ( 250 + 500) = 250 px à répartir.

Cette augmentation de 250 px va se répartir sur 1/1+1 pour le bloc bleu et 1/1+1 pour le bloc rouge.

Nous pouvons appliquer la formule :

nouvelle taille = taille + ( augmentation * proportion)

Ainsi, pour le bloc bleu

nouvelle taille = 250 + ( 250  *1/2 ) = 375

Et, pour le bloc rouge

nouvelle taille = 500 + ( 250  *1/2 ) = 625




Autre cas 


p:nth-of-type(2n+1) {//bleu
  flex : 4 1 25%; 
}

p:nth-of-type(2n) {//rouge
  flex : 1 1 50%; 
}

Dans ce cas encore, la somme des pourcentages est inférieur à 100% (la taille du conteneur), il y a un étirement ; les chiffres rouges sont pris en compte.

Chaque bloc va augmenter proportionnellement de la même valeur.

Ainsi, si le body fait 1000 px, nous aurions eu le bloc bleu (25%) à 250 px et le bloc rouge (50%) à 500 px. Il y a une augmentation de 1000 - ( 250 + 500) = 250 px à répartir.

Cette augmentation de 250 px va se répartir sur 4/1+4 pour le bloc bleu et 1/1+4  pour le bloc rouge.

Nous pouvons appliquer la formule :

nouvelle taille = taille + ( augmentation * proportion)

Ainsi, pour le bloc bleu

nouvelle taille = 250 + ( 250  *4/5 ) = 450

Et, pour le bloc rouge

nouvelle taille = 500 + ( 250  *1/5) = 550



Fichier de test

Flex : A la loupe

Reprenons l'écriture avec des pourcentages

body {
  display : flex;
  flex-flow: row nowrap;
}

p:nth-of-type(2n+1) {//blue
  flex : 1 4 100%; 
}

p:nth-of-type(2n) {//red
  flex : 1 1 50%; 
}

Nous allons tenter de comprendre la situation.

fichier

Notons tout d'abord que la propriété nowrap impose que les blocs soient sur une ligne.

body {
  ...
  flex-flow: row nowrap;
}

Notons ensuite que le bloc blue fait 100% et que le bloc Rouge 50%, nous allons donc devoir les réduire.

Réécrivons notre code pour avoir un cas plus simple.

p:nth-of-type(2n+1) {//blue
  flex : 1 1 100%; 
}

p:nth-of-type(2n) {//red
  flex : 1 1 50%; 
}

Comme il y a réduction, nous nous intéressons aux valeurs rouge, Chaque bloc va se réduire proportionnellement de la même valeur.

Ramenons les pourcentages à des proportions :

100% -> 2
50%   -> 1

Le bloc bleu va donc compter pour 2 sur un total de 3
Le bloc rouge va compter pour 1 sur un total de 3

Ainsi, si le body fait 1000px, nous aurions eu le bloc bleu (100%) à 1000px et le bloc rouge (50%) à 500px. Il y a une réduction de 1000-(1000+500) = 500px.

Cette réduction de 500px va se répartir sur 2/3 pour le bloc bleu et 1/3 pour le bloc rouge.

Nous pouvons appliquer la formule :

nouvelle taille = taille - ( réduction * proportion)

Ainsi, pour le bloc bleu

nouvelle taille = 1000 - ( 500  *2/3 ) = 666

Et, pour le bloc rouge

nouvelle taille = 500 - ( 500  *1/3 ) = 333

Donner une taille au body de 1000px et vérifié le résultat.

←Fichier→



Cas suivant

Modifions le cas précédent en changeant la valeur de réduction du bloc bleu.

p:nth-of-type(2n+1) {//blue
  flex : 1 2 100%; 
}

p:nth-of-type(2n) {//red
  flex : 1 1 50%; 
}

Comme il y a réduction, nous nous intéressons aux valeurs rouge, Chaque bloc va se réduire non plus proportionnellement mais le bloc bleue va se réduire de fois plus de la même valeur.

Ramenons les pourcentages à des proportions :

100% -> 2
50%   -> 1

En prenant en compte le facteur de réduction.

Le bloc bleu va donc compter pour 2*2 sur un total de (2*2+1)
Le bloc rouge va compter pour 1 sur un total de (2*2+1)

Ainsi, si le body fait 1000px, nous aurions eu le bloc bleu (100%) à 1000px et le bloc rouge (50%) à 500px. Il y a une réduction de 1000-(1000+500) = 500px.

Cette réduction de 500px va se répartir sur 4/5 pour le bloc bleu et 1/5 pour le bloc rouge.

Nous pouvons appliquer la formule :

nouvelle taille = taille - ( réduction * proportion)

Ainsi, pour le bloc bleu :

nouvelle taille = 1000 - ( 500  * 4/5 ) = 600

Et, pour le bloc rouge :

nouvelle taille = 500 - ( 500  * 1/5 ) = 400


→fichier←

Finalement pour les valeurs
p:nth-of-type(2n+1) {
  flex : 1 4 100%;
}

p:nth-of-type(2n) {
  flex : 1 1 50%;
}

Les proportions seraient de 4*2/4*2+1 et de 1/9. Vérifiez avec le premier fichier test.

Flex : à la loupe

Nous allons étudier en détail le comportement FLEXIBLE des blocs en fonction de la taille de leur conteneur.

Prenons le cas de deux paragraphes contenus dans un bloc (le body).

Et donnons à chaque paragraphe les valeurs de flexibilité suivantes :

body {
  display : flex;
  flex-flow: row nowrap; //nowrap = une ligne !
}
p:nth-of-type(2n+1) { //bleu
  flex : 5 5 400px;
}

p:nth-of-type(2n) { //rouge
  flex : 1 1 400px;
}

Le fichier de test ( lien sur le fichier ) permet de faire varier la taille du conteneur pour observer la flexibilité des blocs.


Nous allons examiner trois cas :
  1. La place totale du conteneur est  <  à la somme de la taille de chaque bloc.
  2. La place totale du conteneur est  >  à la somme de la taille de chaque bloc.
  3. Egalité entre le contenu et le contenant

Cas 1 : Conteneur plus petit que le contenu !

Prenons par exemple un conteneur (body) de taille 400px.

Nous sommes dans le cas 1, où la taille du body ne permet pas de placer les deux blocs avec leur taille initiale de 400px.

Il faut donc réduire  la taille de chaque bloc.

Valeur de réduction : -400px(taille P1)-400px(taille P2)+400px(taille body) =  -400px.

Nous devons réduire les blocs(Pi) de 400px au total.

Les 400px(de chaque bloc) doivent être réduits mais proportionnellement.

Nous prenons le second coefficient de la propriété flex qui indique la valeur de réduction.

p:nth-of-type(2n+1) { //bleu
  flex : 5 5 400px;
}

p:nth-of-type(2n) { //rouge
  flex : 1 1 400px;
}

Le bloc bleu va se réduire de 5/(5+1) * réduction = 5/6 * 400 = 333.
Sa taille sera donc de 400-333 = 66.

Le bloc rouge va se réduire de 1/(1+5) * réduction = 1/6 * 400 = 66.
Sa taille sera de 400-66=334.

En action : 
a l'aide du fichier test, modifier la taille du body à 400px, et vérifier que les valeurs calculées correspondent à la simulation.

Cas 2 : Conteneur plus grand que le contenu !

Prenons par exemple un body de taille 1000px.

Nous sommes dans le cas 2, où la taille du body permet de placer les deux blocs avec leur taille initiale de 400px et de les agrandir.

Valeur de l'augmentation : -400px(taille B1)-400px(taille B2)+1000px(taille body) =  +200px.

Il faut donc les augmenter de 200px, mais proportionnellement aux valeurs indiquées par la première valeur de la propriété de flex.

p:nth-of-type(2n+1) { //bleu
  flex : 5 5 400px;
}
p:nth-of-type(2n) { //rouge
  flex : 1 1 400px;
}

Le bloc bleu va augmenter de 5/(1+5) * augmentation = 5/6*200 = 166
Sa taille sera donc de 400 + 166 = 566.

Le bloc rouge va augmenter de 1/(1+5) * augmentation = 1/6*200 = 33
Sa taille sera donc de 400 + 33 = 433.





Cas 3 : body = 800px


Remarque

si les valeurs de flex sont :

p:nth-of-type(2n+1) { //bleu
  flex : 5 5;
}
p:nth-of-type(2n) { //rouge
  flex : 1 1;
}

Nous aurons simplement à répartir 800px (la taille du body) en 5/5+1 *800px pour le bloc1 et 1/5+1 *800px pour le bloc 2.





Flex : en action

JS Bin

Sublime Text (suite)

           Sublime Text est un éditeur de texte générique (et non un IDE) développé par Jon Skinner, en C++ et Python. Il est aujourd'hui un des éditeurs les plus utilisés avec notamment Notepad++.

Le logiciel est payant (mais utilisable en version "Unregistered"), il prend en charge 44 langages de programmation majeurs (ex: html/css/javascript/php/...).


Sublime Text est très personnalisable, il vous est par exemple possible d'adapter l'interface de votre programme jusqu'à changer la couleur de la balise de votre code. 

Afin de rendre votre programmation la plus ergonomique possible, vous pouvez utiliser des raccourcis très pratiques !

Voici quelques exemples :



  • Aller directement à un symbole (ex:#) : Windows : Control + R  |  Mac : ⌘ + R
    • Splitter l'éditeur en deux parties : Windows : Alt + Maj + 2  |  Mac : ⌘ + ⌥ + 2
          


    Sublime Text possède un package manager (comprendre un gestionnaire d'extensions pour le programme) qui est téléchargeable librement à l'adresse suivante :

    https://sublime.wbond.net/installation


    Il vous faudra par la suite copier (Ctrl+C) l'instruction fournie en prenant soin de choisir la version que vous possédez (Sublime Text 2 ou 3) 




    Puis la collez dans Sublime Text comme ceci :
    1. Cliquez sur "View" dans la barre de menu
    2. Cliquez sur "Show Console"
    Vous verrez alors apparaître une fenêtre en bas du programme.


    Collez y votre instruction précédemment copiée. Appuyez sur Entrée et relancez votre programme.

    Voilà, vous avez réussi à installer le Package Control !


    Le + de l'article !


    Après avoir installé le Package Control, il vous est possible d'installer l'un des plugins les plus populaires : Live Reload

    Live Reload vous permet de pouvoir rafraîchir par exemple une page HTML que vous codez sur Sublime Text. Nul besoin d'aller tapoter votre touche F5 pour mettre à jour l'affichage de votre page modifiée. Il vous suffit juste de sauvegarder votre code HTML, Live Reload s'occupe du rafraichissement automatique de votre navigateur.

    Pour installer Live Reload, procédez comme ceci :
    1. Ouvrez Sublime Text
    2. Cliquez sur "Tools" puis sur "Commande Palette"
    3. Saisissez dans la recherche qui s'affiche : "pci"
    4. Appuyez sur Entrée
    5. Saisissez "Live Reload" et appuyez sur Entrée




    6. Redémarrez votre Sublime Text
    7. Tapez dans Google : "Live Reload" et choisissez :

    8. Ajoutez l'extension en cliquant sur "Gratuit"
    9. Retournez dans Google Chrome, accédez à : Menu (3 barres) > Plus d'outils > Extensions
    10. Cherchez l'extension LiveReload
    11. Cochez : "Autoriser l'accès aux URL de fichiers"
    Voilà, à vous de jouer !










    Flex : en action menu

    Partons de la situation suivante →voir fichier, puis ajoutons

    nav {
     ...
       justify-content: space-between;
    }

    fichier

    Améliorons la situation en ajoutant du style (de haut vol)

     nav > a {
     ...
        white-space: nowrap;
        text-overflow: ellipsis;
        overflow: hidden;
     }

    fichier

    Ajoutons le comportement flex

    nav > a {
     ...
      text-align: center;
      flex : 1
    }

    Pensez à redimensionner votre page :
    fichier

    Flex : en action

    Notez la différence de comportement de ces deux exemples :

    -> fichier -> fichier
    Notez aussi cette astuce !
    footer {
        margin-top : auto;
    }
    -> fichier

    Voir exemple complet : où l'on remarque que l'input "print" est toujours en bas du conteneur grace à la propriété margin-top !

    -> fichier

    Speudoclassical subclass

    voici comment créer des sous classes en JavaScript.


    Nous allons tenter de comprendre ce code.

    Recopie

    L'idée est simple, nous voulons écrire qu'un taxi "ressemble" à une voiture.

    Pourquoi ne pas écrire le code suivant.


    L'idée est simple, un taxi a les mêmes propriétés qu'une voiture donc recopions simplement ces propriétés dans le constructeur.

    Si l'idée semble ici séduisante, il faut penser que le nombre de propriétés à recopier ainsi peut être important et qu'un cas de modification d'une propriété, nous aurons à répéter cette modification en deux endroits. C'est bien pour cette raison que nous voulons éviter la recopie.

    instancier Car

    Pourquoi ne pas créer une instance de Car dans le constructeur.


    Cette solution pose un certains nombre de problèmes. 
    Coté mémoire, nous créons une instance de Car à chaque fois que nous appellerons la fonction Taxi. Mais, en appelant new Car() dans le constructeur de cette façon, this dans le constructeur Car sera une nouvelle instance de Car et non de Taxi. 

    Rappelons nous que le langage en interne crée à chaque appel de new un objet this.


    Mais, il n'y a pas de lien (mis à part le nom) entre ces deux objets this. Il y a bien deux objets distincts.

    Il serait tentant d'écrire le code suivant. Mais, l’interpréteur du langage ne le permet pas et de toute façon il y aurait encore deux objets this.


    Appel de la fonction

    Pourquoi ne pas appeler simplement la fonction Car dans le constructeur. Encore une fois, la réponse est simple, l'objet this dans car ne représente pas une instance de Taxi mais l'objet global. Puisque la fonction est simplement invoquée dans le contexte global.


    Finalement, il suffirait de passer à cette fonction Car le this instance de taxi.
    La solution utilise donc l'appel à la fonction call.

    Il est parfois difficile d'appréhender son utilisation mais c'est en fait assez simple. Elle a pour rôle de faire correspondre les deux this qui au départ représente deux objets différents ! 

    Une bonne explication est donnée sur la vidéo 11'30





    Ainsi c'est le this de Taxi auquel on ajoutera une propriété pos. Il n'y aura donc aucun objet supplémentaire crée ni de recopie de propriétés.

    Superclass et Subclass

    Reprenons l'exemple de la functional Class.


    Un problème classique est de définir des catégories de Voiture ayant des spécificités bien particulières.

    Il faut a tout pris éviter de recopier le code comme le montre la figure suivante.


    Il est clair que nous devons limiter la recopie de code commun. Par exemple la fonction move est dupliquée.

    Nous allons définir une fonction (la super Fonction) qui regroupe le code commun. Nous créons ensuite d'autres fonction (les sous Fonction) qui décorent la super fonction. Mais, nous pouvons utiliser le mot classe à la place de fonction. Puisque la super classe a pour rôle de générer un grand nombre d'objets.





    3 closures

    setTimeout :


    Array


    return function


    Speudoclassical pattern

    Revenons un instant sur le code de prototype class.


    Nous voyons que chaque fois que nous voulons définir une fonction class, il faut créer un objet avec Object.create et le retourner.

    Modifions le code précédant en introduisant this à la place de obj.


    Et finalement puisque nous devons pour chaque class créer et retourner cette objet pourquoi ne pas l'intégrer directement dans le Langage et ainsi ne plus l'écrire dans le code. Pour valider ce type d'écriture (pour ne pas le confondre avec un autre type d'objet à modifier) il suffira d'appeler la fonction avec l'opérateur new.

    Finalement, nous écrirons


    Examinons une dernière fois ce code


    En Partie 1, nous spécifions les parties spécifiques de chaque objet. Ces différences seront écrire à l'intérieur du constructeur.

    En partie 2, nous aurons le code commun à partager entre tous les objets instanciés de la classe. Ces similarités seront stockées dans l'objet prototype de la classe.

    Prototype Class


    Reprenons l'exemple de functional-classes



    La fonction Car, crée un objet copie les propriétés et retourne un objet.

    Nous avons vue la différences entre la fonction extend et l'utilisation de object.create.

    Nous allons utiliser cette délégation pour transformer notre code. Il suffit de remplacer avantageusement  extend par Objet.create.