JavaScript orienté but

Dans cet exemple graphique avec Canvas on va voir comment une simple fonction fait de JavaScript un langage de programmation orienté but.

L'exemple montre une voiture se déplaçant sur une route, vers une destination, son but. Le principe de programmation le plus simple est de définir le but sous forme d'expression, et placer l'action qui tend vers ce but dans une itération.

Démonstration

Le but est de laisser la voiture avancer jusqu'au bord droit du cadre.

Code JavaScript

var car = new Image();
car.src="images/car-green.png";

var roadCanvas = document.getElementById("road");
var roadCtx = roadCanvas.getContext("2d");

function scenery() {
  roadCtx.fillStyle="white";
  roadCtx.rect(0,0,640,160);
  roadCtx.fill();
}

var xcar= 0;

car.onload=function() {
  roadCtx.drawImage(car, xcar, 60);
}	

var carInterval;
var stopFlag=false;
function startCar() {
  carInterval = setInterval(function() {
    scenery();
    roadCtx.drawImage(car, xcar, 60);
    xcar++;
    if(xcar >= 400 || stopFlag) {
      clearInterval(carInterval);
      return true;
    }	
  }, 20); 
}

Code HTML

<input type="button" value="Avancer" onClick="stopFlag=false;startCar()">
<input type="button" value="Stopper" onClick="stopFlag=true">

Eventuellement on peut ajouter une contrainte de temps, dans ce cas on cherche simplement à se rapprocher du but. Mais cela devient plus difficile à gérer, plus spécialement si l'on a plusieurs objets à animer en même temps.

La programmation orienté-but toutefois, va rendre les choses très simples. On définit une fonction "but" qui a pour arguments:

  1. La condition à remplir, le but.
  2. Un paramètre de durée maximale.
  3. Et l'action à accomplir de façon itérative pour se rapprocher du but.

Démonstration orientée but

Code source de la fonction "but"

var stopDuo=false;
var goal = function(condi, dur, actio) { 
    var iter = setInterval(function() {
      if(condi() || stopDuo) {
        clearInterval(iter); 
        clearTimeout(limiter);
        return;
      }
      actio();
    }, 0);
    if(dur == "&") dur = 2147483647;
    var limiter=setTimeout(function() { clearInterval(iter); }, dur);
}

Code JavaScript de l'animation

var orange = new Image();
orange.src="images/car-orange.png";

var green = new Image();
green.src="images/car-green.png";

var raceCanvas = document.getElementById("race");
var raceCtx = raceCanvas.getContext("2d");

var xgreen = 0;
var xorange = 0 ;

green.onload=function() {
  raceCtx.drawImage(green, xgreen, 220);
}

orange.onload=function() {
  raceCtx.drawImage(orange, xorange, 60);
}

function redraw() 
{
  raceCtx.fillStyle="white";
  raceCtx.rect(0,0,640,320);
  raceCtx.fill();
  raceCtx.drawImage(orange, xorange, 60);
  raceCtx.drawImage(green, xgreen, 220);
}

function startDuo() {
 goal( function() { return (xorange >= 400); }, "&", function() {
    redraw();
    xorange++;
 });
 goal( function() { return(xgreen >= 400); }, "&", function() {
    redraw();
    xgreen+=2;
 });
}

Code HTML

<input type="button" value="Avancer" onClick="stopDuo=false;startDuo()">
<input type="button" value="Stopper" onClick="stopDuo=true;">

Comme on peut le voir, tout ceci fonctionne de façon asynchrone. Les deux voitures avancent en même temps, indépendamment l'une de l'autre. Il est possible de définir une fonction but agissant de façon synchrone:

var goalSync=function(condi, dur, actio) { 
  stopFlag = false;
  if(dur == "&") dur = 2147483647;
  var limiter=setTimeout(function() { stopFlag=true; }, dur);
  while(stopFlag == false) {
    if(condi()) {
      clearTimeout(limiter);
      stopFlag=true;
      return;
    }
    actio();
  }
}

Dans ce cas les voitures avanceraient l'une après l'autre.

Les deux objets font partie du framework creationsitewebcasablanca.js utilisé par le compilateur Scriptol-JavaScript. Le langage dispose d'une syntaxe plus simple pour mettre en oeuvre l'orientation but.