Game in canvas !

f

Rebond : class

Class : Gravity


L'idée est de simuler la chute d'un objet.

La chute ne doit pas être linéaire mais accélérée.

Physique




Équation

A chaque itération, il suffit d'appliquer les équations suivantes :

    gravitySpeed += gravity;

    y += gravitySpeed;


Avec les valeurs de simulation suivantes, on voit clairement que durant :

  •  les dix premières itérations l'objet passe de la position 1 à 6 
  •  les dix itérations suivantes l'objet passe de la position 6  à 22
  •  de l'itération 60 à 70 l'objet passe de la position de 183 à 249


                    Frame 1 : y = 1 (valeur tronquée)
(index):186 Frame 2 : y = 1
(index):186 Frame 3 : y = 1
(index):186 Frame 4 : y = 1
(index):186 Frame 5 : y = 2
(index):186 Frame 6 : y = 3
(index):186 Frame 7 : y = 3
(index):186 Frame 8 : y = 4
(index):186 Frame 9 : y = 5
(index):186 Frame 10 : y = 6
(index):186 Frame 11 : y = 7
(index):186 Frame 12 : y = 8
(index):186 Frame 13 : y = 10
(index):186 Frame 14 : y = 11
(index):186 Frame 15 : y = 12
(index):186 Frame 16 : y = 14
(index):186 Frame 17 : y = 16
(index):186 Frame 18 : y = 18
(index):186 Frame 19 : y = 20
(index):186 Frame 20 : y = 22
...
(index): 186Frame 60 : y = 183
(index):186 Frame 61 : y = 190
(index):186 Frame 62 : y = 196
(index):186 Frame 63 : y = 202
(index):186 Frame 64 : y = 208
(index):186 Frame 65 : y = 215
(index):186 Frame 66 : y = 222
(index):186 Frame 67 : y = 228
(index):186 Frame 68 : y = 235
(index):186 Frame 69 : y = 242
(index):186 Frame 70 : y = 249

L'objet chute :

  • durant les dix premières itérations l'objet chute de ( 6 - 1) 5px, 
  • de l'itération 60, la chute sera de (249 - 183 ) 66px 
  • et à partir de l'itération 1000, l'objet chute à une très grande vitesse (or, nous savons qu'il existe une vitesse limite de chute qui est du au freinage des frottement)


Code


class GravityBloc extends AnimatedBloc {

  constructor(elt, {
    speed
  }) {
    super(elt, {
      speed
    });

    this.gravity = 0.1;
    this.gravitySpeed = 0;
  }

  static construct(elt, {
    speed = 0
  } = {}) {

    return new GravityBloc( elt, speed );
  }


  update() {

    super.update();
    
    this.gravitySpeed +=this.gravity;
    this.y += this.gravitySpeed;

  }

}


Animation : class

Pour obtenir un temps écoulé :

let startTime = new Date().getTime();
function main() {

 //pour avoir un temps de simu depuis le départ
   let currTime = new Date().getTime(),
       dt = ((currTime - startTime)/1000);
       
  update(dt);
  render();

  requestId = window.requestAnimationFrame(main);
}

temps quasi constant


function main() {

  let now = Date.now(),
    dt = (now - lastTime) / 1000.0;
       
  update(dt);
  render();
  lastTime = now;
  requestId = window.requestAnimationFrame(main);

}

on pourra écrire la classe suivante :

class Oxilo extends AnimatedBloc {

  constructor(elt, { speed }) {
    
    super(elt, {
      speed
    });
  }

  static construct(elt, { speed = 1 } = {}) {
    return new Oxilo(elt, { speed });
  }

  update(dt) {   
   this.x = Math.cos(2*Math.PI*(dt))*50;
   this.y = Math.sin(Math.PI*(dt))*50;
  }

}


animation : class

Élément de base

class AnimatedBloc {

  constructor(elt, {speed = 1} = {} ) {

    this.elt = elt;

    // initial CSS
    this.initPosition( speed );
  }

  initPosition( speed ) {

    if (this.elt) {
      const {
        left,
        top
      } = this.elt.getBoundingClientRect();

      // CSS
      this.cssX = left;
      this.cssY = top;

      this.x = 0;
      this.y = 0;
      
      this.speed = speed;
    }
  }

  render() {
    this.elt.style.cssText = `left:${this.x+this.cssX}px`;
    //ctx.drawImage(Resources.get(this.sprite), this.x, this.y);
  }
  
  update() {
    this.x = this.x + this.speed;
  }

}

Héritage

class BouncingBloc extends AnimatedBloc {

  constructor(elt, {speed = 3, at = 300 } = {} ) {
    super(elt, {speed});

    this.boundary = at;
  }


  update() {
    
    super.update();

    if (this.x >= this.boundary || this.x == 0) {
      this.speed *= -1;
      this.elt.classList.toggle("bouncing");
    }


  }

}

heritage : class

Passage par défault

class Player {

 constructor({
    keysMap = new Map([
      ["up", "ArrowUp"],
      ["right", "ArrowRight"],
      ["down", "ArrowDown"],
      ["left", "ArrowLeft"],
    ]),
    x = 20,
    y = 20,
    speed = 0.5,
  } = {}) {

    Object.assign(this, { touches, x, y, speed });
    ...
    this.moveX = 0;
    this.moveY = 0;
    

  }
  ...
}


https://es6console.com/jpcwjhve/

------------ Amélioration du code

Méthode static !

class Player {

  constructor( {keysMap, x, y, speed} ) {

    Object.assign(this, {
      keysMap,
      x,
      y,
      speed
    });

    this.moveX = 0;
    this.moveY = 0;

    ...

  }
  static create({
    keysMap = new Map([
      ["ArrowUp", "up"],
      ["ArrowRight", "right"],
      ["ArrowDown", "down"],
      ["ArrowLeft", "left"],
    ]),
    x = 100,
    y = 100,
    speed = 0.5,
  } = {}) {

    return new Player({keysMap, x, y, speed})

  }
...
}

appel

Player.create();

Player.create({
    keysMap: new Map([
      ["z", "up"],
      ["d", "right"],
      ["x", "down"],
      ["q", "left"],
    ]),
    speed: 2
  });
  
https://es6console.com/jpdtv6zx/

Game

itérator

Ne marche pas si appellé deux fois

const dateRange = {
  from: new Date(2018, 0, 23),
  to: new Date(2018, 3, 28),
  [Symbol.iterator]() {
    this.current = this.from

    return this
  },
  next() {
    if (this.current <= this.to) {
      this.current.setDate(this.current.getDate() + 1)

      return {
        done: false,
        value: new Date(this.current)
      }
    }

    return {
      done: true
    }
  }
}

const dateList = Array.from(dateRange);

dateList.forEach(date => {
    console.log(date.toString())
})


Getter et Setter d'une classe

La syntaxe set permet de lier une propriété d'un objet à une fonction qui sera appelée à chaque tentative de modification de cette propriété.

Exemple : 

class CodeSecret {

  constructor(num) {
    // invokes the setter
    this.code = num;

  }

  set code(num) {
   
    if ( !/^ISBN/.test(num)) {
      console.log("votre code doit commencer par ISBN");
      return;
    }
    this._code = num;

  }


  get code() {
    return this._code;
  }

}

let t = new CodeSecret("dD1");

https://es6console.com/jpbn7x38/


class : Action



class Prefix {


     constructor(pref) { // pas de fx arrow

          this.pref= pref;
     }

    addPref(tab) {

      return tab.map( ( {genre,nom} ) => {
       
        let pref = genre =='m' ? `${this.pref}` : `${this.pref}e`;
       
        return `${pref} ${this.upper(nom)}`
      })
    }

    upper(s){
       return s[0].toUpperCase() + s.slice(1);
    }
}

pers =  [
{nom: "Dupont",ville: "evry",genre: "f"},
{nom: "Brusel",ville: "belfort",genre: "m"}

];
let t = new Prefix("Cher");

console.log(t.addPref(pers));


Mélanger un tableau !

On veut mélanger les éléments d'un tableau :

["denis","gérard","nicolas","lolo"] 
["nicolas","gérard","lolo","denis"] 
idem en 1 

["denis","gérard","nicolas","lolo"] 
["denis","gérard","nicolas","lolo"] 
idem en 0 

["denis","gérard","nicolas","lolo"] 
["nicolas","lolo","denis","gérard"] 
TOP

Améliorer ce code :

test map !

const pers = [
    { nom: "Dupont", ville: "evry", sex: "f", born: 1903 },
    { nom: "Dupont", ville: "Paris", sex: "f", born: 2004 },
    { nom: "Brusel", ville: "belfort", sex: "h", born: 1930 }
];


const filles_callBack = pers.filter(function(p){
    return (p.sex === "f");
});


Comparer ces différentes écritures

const filles_callBackArrow = pers.filter(p => p.sex === "f");

const filles_callBackDest = pers.filter(function( {sex}){
    return (sex === "f");
});

const filles_callBackArrowDestruc = pers.filter( ({sex} )  => sex == "f" );


Pour chaque transformation donner le résultat sous forme graphique : 


exemple

const filles= pers.filter(function(p){
    return (p.sex === "f");
});

const dates = filles.map(function annee( p, index ) {
   return { 
        num : index,
        annee : p.born 
     };
});



en Action

    function ageObj({ born }) {return { born }}
    function ageVal({ born }) {return born}
    function ajout(p) {p["new"] = "hallo";}
    function ajout_2(p) {return p["new"] = "hallo";}
    function ageObjDestruct({ born :annee }) {return { annee }};

filles.map(function annee( p, index ) {
   return [p.nom,p.born]
});

filles.map(ajout);
 
filles.map(ajout_2);
 
filles.map(p=>p);
 
filles.map(p=>p["new"]="hallo");
 
filles.map(ageObj);
 
filles.map(ageVal);

filles.map(function({ born : annee }) {return { annee }});

filles.map(function({ born :annee, nom }) {return { nom,annee }});

filles.map(function ({ nom, ville }) {
    return {
        info: `${nom} habite ${ville}`
    }
});

https://goo.gl/G6u1Cz

Map en Action

  
Comment transformer :

[['eux', '   ', '   ' ],['A', 'B', 'C' ],['eux', '   ', '   ']]

en

[["eux", "A", "eux"],["   ", "B", "   "],["   ", "C", "   "]]


Aide

for of


let tabPers = 
[  
  {
    nom: "Dupont",
    sex : "f"},
  { 
    nom: "Brusel",
    sex : "h"},
  {
    nom: "Dupont",
    sex : "f"},  
];

Cas des pointeurs


for (let pers of tabPers){
  console.log(pers);
}




Cas de la déstructuration 

for (let nom } of tabPers){
  console.log(nom);
}

A chaque itération, nous aurons : 

{
    let [ FORMAL_PARAMETERS ] = [ ACTUAL_PARAMETERS ];
    {
         CODE
    }
}

ainsi

{
    let [ { nom } ] = [ { nom: "Dupont",sex:"f" } ];
    {
        console.log( nom );
    }
}

sera équivalent à 

{
    let [ { nom:nom } ] = [ { nom: "Dupont",sex:"f" } ];
    {
        console.log( nom );
    }
}

et 
{
    let [ {  nom } ] = [ { nom: "Dupont",sex:"f" } ];
    {
        console.log( nom );
    }
}







Tu pointes ou tu tires ?

Que veux dire filtrer un tableau ?

La modification d'un élément filtré modifie-t-elle le tableau initial ?


Visualize : outil d'aide

Voici un lien pour comprendre le clonage d'objets avec :

Object.assign( objDestination, objSource)


Visualisation


Les pointeurs et moi !

Voici des articles montrant le clonage d'éléments d'un tableau.



this et la fonction flechée

function normalise(){
let normaliseT = this.valeurs.map(function(v){
return v / this.nb;
});
console.log(`valeurs normalisées ${normaliseT}`);
}


const obj = {valeurs:[3,6,12], nb:3};
normalise.call(obj);

Solution à ce problème classique :

function normalise(){
    let normaliseT = this.valeurs.map(function(v){
        return v / this.nb;
    }.bind(this));
    console.log(`valeurs normalisées ${normaliseT}`);
}

const obj = {valeurs:[3,6,12], nb:3};
normalise.call(obj);


Avec une fonction fléchée : 


function normalise(){
    let normaliseT = this.valeurs.map(v => v / this.nb);
    console.log(`valeurs normalisées ${normaliseT}`);
}

const obj = {valeurs:[3,6,12], nb:3};
normalise.call(obj);


En action

lire un object !

Notez les différences d'affichage :

let u = {t:2,u:3};
  console.log("u=" + u);
  console.log(u);

  • "u=[object Object]"
  • [object Object] {
      t
    : 2,
      u
    : 3
    }
const propKeys = Reflect.ownKeys(res);

for (const propKey of propKeys) {
console.log([propKey, res[propKey]]);
}

itérator

function iterateOver(...args) {

  let [from, to, step=1] = [...args];


  const range = {


    [Symbol.iterator]() {
      this.current = from;
      return this;
    },

    next() {
      if (this.current <= to) {
        let char = this.current;
        this.current = String.fromCharCode(this.current.charCodeAt(0) + step);
        return {
          done: false,
          value: char
        };
      } else {
        return {
          done: true
        };
      }
    }

  }
  return range;
}



for (let num of iterateOver("A","W",5)) {
  console.log(num);
}

let t = Array.from(iterateOver("A","J"), c => c.toLowerCase());
console.log(t);

fichier source

fichier source

JS Bin on jsbin.com

Array.from + itérateur

let range = {
  from: "A",
  to: "W"
};

// 1. call to for..of initially calls this
range[Symbol.iterator] = function() {

  // 2. ...it returns the iterator:
  return {
    current: this.from,
    last: this.to,

    // 3. next() is called on each iteration by the for..of loop
    next() {
      // 4. it should return the value as an object {done:.., value :...}
      if (this.current <= this.last) {
let char = this.current;
this.current = String.fromCharCode(this.current.charCodeAt(0)+1);
        return { done: false, value: char };
      } else {
        return { done: true };
      }

    }
  };
};

for (let num of range) {
  console.log(num);
}

let t = Array.from(range,c=>c.toLowerCase());
console.log(t);

code


JS Bin on jsbin.com

Array.from : magic

t = Array.from(Fib)
console.log(t);

[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987]

fichier code

code

En action : 

let Fib = {
  [Symbol.iterator]() {
    let n1 = 1,
      n2 = 1;
    return {
      [Symbol.iterator]() {
        return this;
      },
      next() {

          let current = n2;
          n2 = n1;
          n1 = n1 + current;
          if (current <= 1000) { //sinon la boucle est infinie
            return {
              value: current,
              done: false
            };
          } else {
            return {
              done: true
            };
          }
          return {
            value: current,
            done: false
          };
        },
        return () {
          console.log(
            `stop`
          );
          return {
            done: true
          };
        }
    };
  }
};

t = Array.from(Fib)
console.log(t);

itérator :

Voici deux écritures de Fibonacci :

const Fibonacci = {
    n1: 1,
    n2: 1,
    [Symbol.iterator]() {
      return this;
    },
    next() {
      let current = this.n2;
      this.n2 = this.n1;
      this.n1 = this.n1 + current;
      return {
        value: current,
        done: false
      };
    },
    return (v) {
      return {
        value: v,
        done: true
      };
    }
  };

let Fib = {
  [Symbol.iterator]() {
    let n1 = 1,
      n2 = 1;
    return {
      [Symbol.iterator]() {
        return this;
      },
      next() {
          let current = n2;
          n2 = n1;
          n1 = n1 + current;
          return {
            value: current,
            done: false
          };
        },
        return () {
          console.log(
            `stop`
          );
          return {
            value: v,
            done: true
          };
        }
    };
  }
};
for (var v of Fib) {
  console.log(v);
  if (v > 50) break;
}

itérateur

let range = {
  from: 65,
  to: 90,
  [Symbol.iterator]() {
    return this;
  },
  next() {
    if (this.from <= this.to) {
      return {
        done: false,
        value: String.fromCharCode(this.from++)
      };
    } else {
      return {
        done: true
      };
    }
  },
};



// A-W
for (let num of range) {
  console.log(num); 
}

module

http://dupontcours.free.fr/ES6/module/

la décompostion

décomposition et renomage :

const cours = [
  {
    langue: "fr",
    niveau: {
      lu: 2,
      ecrit: 5,
      parle: 5
    },
    test: 35
  },
  {
    langue: "eng",
    niveau: {
      lu: 4,
      ecrit: 4,
      parle: 4
    },
    test: 100
  }
]

// sans renomage
for (let {langue, niveau: { ecrit} } of cours) {
  console.log(`langue : ${langue}
\t\tniveau de l'écrit = ${ecrit}`)

}

// on peut redéfinir les variables
for (let {langue: l, niveau: { ecrit: e } } of cours) {
  console.log(`langue : ${l}
\t\tniveau de l'écrit = ${e}`)
}

>
  • "langue : fr
        niveau de l'écrit = 5"
  • "langue : eng
        niveau de l'écrit = 4"