Skip welcome & menu and move to editor
Welcome to JS Bin
Load cached copy from
 
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
</head>
<body onload="init();">
  <canvas id="myCanvas" width=500 height=500>
    
  </canvas>
  <video id="myVideo" autoplay controls>
    <source src="http://mainline.i3s.unice.fr/mooc/mancity.mp4">
  </video>
  <button id="playpause">Play/Pause</button>
  <p>
    <button id="boutonAddArret">Marquer un arrêt</button>
</body>
</html>
 
#myCanvas {
  border: 2px solid black;
  background-image:url('https://download.tuxfamily.org/gimplovers/vignettes/tutoriels/textures/texture-crepi.jpg');
}
#myVideo {
   display:none; 
}
 
// Bonne pratique : on déclare le canvas en global
var canvas;
var ctx;
var video;
var mousepos;
var drawingArrow;
var startArrow = {};
var endArrow = {};
var tableauArrows = [];
var tableauDesTempsDarrets = [];
var prochainArretIndex = 0;
var sauteProchainArret = false;
function init() {
  // Appelée dès que la page est chargée...
  //console.log('page chargée');
  
  // Pointeur sur le canvas
  canvas = document.querySelector("#myCanvas");
  
  // Pour dessiner il faut récupérer un autre objet
  // qu'on obtient à partir du canvas : le contexte
  // graphique
  ctx = canvas.getContext("2d");
  // autre possibilité : "webgl" pour la 3D
  
 
  // l'élément video
  video = document.querySelector("#myVideo");
  // ecouteur sur l'évenement timeupdate
  video.addEventListener("timeupdate", videoTimeupdate, false);
    
  // Bouton playpause, écouteur
  var boutonPlayPause = document.querySelector("#playpause");
  var boutonAddArret = document.querySelector("#boutonAddArret");
  boutonPlayPause.onclick = function() {
    console.log("play / pause");
    boutonAddArret.disabled=false;
    if(video.paused) {
      video.play();
    } else {
      video.pause();
    }
   }
   
     // Bouton marquer un arrêt (va ajouter des trucs à afficher et forcer la pause)
    boutonAddArret.onclick = function() {
      console.log("add arrêt");
        // si la video n'est pas en pause on la pause
          video.pause();
        // mémoriser le temps
        // on utilise un tableau dans lequel on 
        // ajouter les temps où la vidéo devra
        // s'arrêter
        tableauDesTempsDarrets.push(video.currentTime);
        tableauDesTempsDarrets.sort(compareNombres);
        // on desactive le bouton tant qu'on a pas refait play
      boutonAddArret.disabled=true;
      // Si sauteProchainArret = true alors quand on cliquera
      // sur play/pause on ne s'arrêtera pas de suite
      sauteProchainArret = true;
    }
  function compareNombres(a, b) {
     return a - b;
  }
  
  // Ecouteurs de souris
    window.addEventListener("mousedown", traiteMousedown, false);
    window.addEventListener("mouseup", traiteMouseup, false);
    window.addEventListener("mousemove", traiteMousemove, false);
    anime();
} 
function videoTimeupdate(evt) {
  
  // On regarde s'il y a un prochain arrêt prévu
  if(prochainArretIndex !== -1){
    console.log("time = " + video.currentTime + " i = " + prochainArretIndex + " t = " + tableauDesTempsDarrets[prochainArretIndex]);
      if(video.currentTime > tableauDesTempsDarrets[prochainArretIndex]) {
        if(!sauteProchainArret) video.pause();
          sauteProchainArret = false;
      }
  }
  prochainArretIndex = chercheProchainArret(video.currentTime); 
}  
// on a [2, 3, 7, 9] et time = 6
// la bonne réponse est 3, l'index de la valeur 7 
function chercheProchainArret(time) {
  var index=-1;
  
  for(index=0; index < tableauDesTempsDarrets.length; index++) {
    var elem = tableauDesTempsDarrets[index];
    if(time <= elem) {
      //console.log("index trouvé : " + index)
      return index;
    }
  }
  // Si on a parcouru tout le tableau sans trouver
  if(index == tableauDesTempsDarrets.length)
  return -1;
}
function traiteMouseup(evt) {  
  //console.log("mouseup");
  drawingArrow = false;
  // on enregistre la flèche dans le tableau
  // des flèches à dessiner
  tableauArrows.push({x1 : startArrow.x, 
                      y1 : startArrow.y,
                      x2 : endArrow.x,
                      y2 : endArrow.y});
}
              
function traiteMousedown(evt) {  
  //console.log("mousedown");
  drawingArrow = true;
  startArrow.x = mousePos.x;
  startArrow.y = mousePos.y;
}
              
function traiteMousemove(evt) {
  //console.log("mousemove");
  mousePos = getMousePos(canvas, evt);
  endArrow.x = mousePos.x;
  endArrow.y = mousePos.y;
}
              
function getMousePos(canvas, evt) {
   // necessary to take into account CSS boundaries
   var rect = canvas.getBoundingClientRect();
   return {
      x: evt.clientX - rect.left,
      y: evt.clientY - rect.top
   };
}
// x, y, rx, ry, angle en degrés, largeur, couleur en hexa
var e = new EllipseAnimee(100, 100, 60, 30, 96.5, 7, "#6699CC");
// Si on appelle que drawPlain, on peut utiliser n'importe quelle
// syntaxe CSS pour la couleur
var e1 = new EllipseAnimee(130, 230, 120, 30,95, 15, "yellow");
e1.portionAngle = 2*Math.PI/3;
e1.vitesseAnimation = 0.3;
var e3 = new EllipseAnimee(220, 150, 60, 30, 95, 17, "#690CC");
e3.vitesseAnimation = 0.15;
e3.portionAngle = Math.PI;
function anime() {
  // 1 - On efface le canvas
  //ctx.clearRect(0, 0, canvas.width, canvas.height);
  
  // 1 - effacer les deux canvas : celui avec la video (en
  // fait on ne l'efface pas, on dessine l'image de la
  // video), et le front canvas, celui où on dessine
  ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
   
  
  // 2 - On dessine des trucs
  tete.draw();
  
  // dessiner les anciennes fleches
  tableauArrows.forEach(function(elem) {
         drawArrow(ctx, 
                   elem.x1, elem.y1, 
                   elem.x2, elem.y2, 
                   10, "red");
  });
  // dessiner la flèche en cours
  if(drawingArrow) {
    drawArrow(ctx, 
              startArrow.x, startArrow.y, 
              endArrow.x, endArrow.y, 
              10, "green");
  }
  // Test : dessiner une ellipse
  //drawEllipse(ctx, 100, 100, 100, 40, 4, "red");
    e.drawDegrade(ctx);
    e.update();
  
      e1.drawPlain(ctx);
  e1.update();
  e3.drawDegrade(ctx);
  e3.update();
  
  // 3 - on deplace des trucs
  tete.move(0.2, 0);
  
  // 3.5 on gère les interactions (avec l'utilisateur)
  // ou entre les objets.
  testCollisionsAvecMurs();
  // 4 - on recommence mais 60 fois par seconde
  requestAnimationFrame(anime);
}
// On fait un objet singleton pour le monstre
var tete = {
  x:100,
  y:100,
  largeur: 100,
  hauteur:100,
  vitesseX : 1,
  vitesseY : 3,
  
  draw: function() {
  // Bonne pratique :
  // Toujours sauvegarder le contexte quand
  // dans une fonction on modifie quelque chose
  // du contexte : couleur, épaisseur du trait etc.
  // Ou le repère dans lequel on travaille
  // et on le restaure à fin
  
  // sauver le contexte
  ctx.save();
  ctx.translate(this.x-10-this.largeur/2, this.y-10-this.hauteur/2);
  //ctx.rotate(0.2);
  //ctx.scale(0.5, 0.5);
  
  
  // Tete
  ctx.strokeRect(10, 10, this.largeur, this.hauteur);
  
  
  // yeux rouges
  ctx.fillStyle = "red";
  ctx.fillRect(30, 30, 10, 10);
  ctx.fillRect(75, 30, 10, 10);
  
  // petits yeux dans les gros yeux
  ctx.fillStyle = "black";
  ctx.fillRect(34, 35, 5, 5);
  ctx.fillRect(78, 32, 5, 5);
  
  // nez violet
  ctx.fillStyle = "purple";
  ctx.fillRect(53, 45, 10, 35);
  
  // bouche orange
  ctx.fillStyle = "orange";
  ctx.fillRect(38, 85, 40, 10)
  
  // dents noires
  ctx.fillStyle = "black";
  ctx.fillRect(44, 85, 5, 10);
  ctx.fillRect(54, 85, 5, 10);
  ctx.fillRect(64, 85, 5, 10);
  
  ctx.restore();
},
  
  move: function() {
     this.x += this.vitesseX;
     this.y += this.vitesseY;
  }
}
function testCollisionsAvecMurs() {
  // à droite
  if((tete.x + tete.largeur/2)>= canvas.width) {
    tete.vitesseX = -tete.vitesseX;
    // On repositionne au point de contact
    tete.x = canvas.width-tete.largeur/2;
  }
  // à gauche
  if((tete.x - tete.largeur/2) <=0) {
    tete.vitesseX = -tete.vitesseX;
    tete.x = tete.largeur/2;
  }
  
  // en bas
  if((tete.y + tete.hauteur/2)>= canvas.height) {
    tete.vitesseY = -tete.vitesseY;
    // On repositionne au point de contact
    tete.y = canvas.height-tete.hauteur/2;
  }
  
  if((tete.y - tete.hauteur/2) <=0) {
    tete.vitesseY = -tete.vitesseY;
    tete.y = tete.hauteur/2;
  }
}
function drawArrow(ctx, fromx, fromy, tox, toy, arrowWidth, color){
    //variables to be used when creating the arrow
    var headlen = 10;
    var angle = Math.atan2(toy-fromy,tox-fromx);
 
    ctx.save();
    ctx.strokeStyle = color;
     ctx.shadowColor = "Black";    // color
    ctx.shadowBlur = 10;         // blur level
    ctx.shadowOffsetX = 5;      // horizontal offset
    ctx.shadowOffsetY = 5;
  
    //starting path of the arrow from the start square to the end square
    //and drawing the stroke
    ctx.beginPath();
    ctx.moveTo(fromx, fromy);
    ctx.lineTo(tox, toy);
    ctx.lineWidth = arrowWidth;
    ctx.stroke();
 
    //starting a new path from the head of the arrow to one of the sides of
    //the point
    ctx.beginPath();
    ctx.moveTo(tox, toy);
    ctx.lineTo(tox-headlen*Math.cos(angle-Math.PI/7),
               toy-headlen*Math.sin(angle-Math.PI/7));
 
    //path from the side point of the arrow, to the other side point
    ctx.lineTo(tox-headlen*Math.cos(angle+Math.PI/7),
               toy-headlen*Math.sin(angle+Math.PI/7));
 
    //path from the side point back to the tip of the arrow, and then
    //again to the opposite side point
    ctx.lineTo(tox, toy);
    ctx.lineTo(tox-headlen*Math.cos(angle-Math.PI/7),
               toy-headlen*Math.sin(angle-Math.PI/7));
 
    //draws the paths created above
    ctx.stroke();
    ctx.restore();
}
// ellipses
function drawEllipse(ctx, cx, cy, l, h, lw, couleur) {
  ctx.save();
  ctx.beginPath();
  
  ctx.moveTo(cx, cy - h/2); // A1
  
  ctx.bezierCurveTo(
    cx + l/2, cy - h/2, // C1
    cx + l/2, cy + h/2, // C2
    cx, cy + h/2); // A2
  ctx.bezierCurveTo(
    cx - l/2, cy + h/2, // C3
    cx - l/2, cy - h/2, // C4
    cx, cy - h/2); // A1
 
  ctx.lineWidth = lw
  ctx.strokeStyle = couleur;
  ctx.stroke();
  ctx.closePath();
  
  ctx.restore();
}
/*
  centre : x et y
  rayon horizontal = r1
  rayon vertical = r2
  angle en degré, on tourne dans le sens des aiguilles d'une montre
  lt = largeur du trait
  couleur = couleur
  
  Pour dessiner : appeler la méthode draw(ctx)
  Pour animer : appeler update depuis une boucle d'animation
*/
function EllipseAnimee(x, y, r1, r2, angle, lt, couleur) {
  this.x = x;
  this.y = y;
  this.r1 = r1;
  this.r2 = r2;
  this.angle = angle*Math.PI/180;
  this.lt = lt;
  this.angleDepart = 0;
  this.portionAngle=Math.PI/3;
  this.vitesseAnimation = 0.07;
  this.couleur = couleur;
  
  this.drawPlain = function(ctx) {
    ctx.save();
    ctx.translate(this.x, this.y);
    ctx.rotate(this.angle);
    ctx.scale(this.r2/this.r1,1);
    
    ctx.lineWidth = lt;
    ctx.strokeStyle = this.couleur;
    ctx.beginPath();
    ctx.arc(0, 0, 
            this.r1, 
            this.angleDepart, 
            this.angleDepart + this.portionAngle, false);
    ctx.stroke();
    ctx.restore();
  }
  
  this.drawDegrade = function(ctx) {
    ctx.save();
        ctx.translate(this.x, this.y);
    ctx.rotate(this.angle);
    ctx.scale(this.r2/this.r1,1);
    var lum=0.05;
    for(var a=this.angleDepart; a < Math.PI*2+this.angleDepart-0.1; a+=0.05) {
      var px = 0 + this.r1 * Math.cos(a);
      var py = 0 + this.r1 * Math.sin(a);
      ctx.beginPath();
      if(lum == 0.05) {
        ctx.arc(px, py, 
            this.lt, 
            0, 
            Math.PI*2,
              false);
      } else {
        ctx.arc(px, py, 
            this.lt/2, 
            0, 
            Math.PI*2,
              false);
      }
      
      // changer 80 en autre chose pour que la variation de 
      // couleur soit plus forte/moins forte
      ctx.fillStyle = colorLuminance(this.couleur, lum);
      lum += 0.01;
      ctx.fill();
    }
    ctx.restore();
    
  }
  this.update = function() {
    this.angleDepart += this.vitesseAnimation;
  }
}
// hex = la couleur en hexadecimal ex : "#00FFFF"
// lum = une valeur entre -1 et +1
// si négatif = plus clair. Par ex lum=-0.5 = 50% plus
// clair. Voir https://www.sitepoint.com/javascript-generate-lighter-darker-color/
function colorLuminance(hex, lum) {
    // validate hex string
    hex = String(hex).replace(/[^0-9a-f]/gi, '');
    if (hex.length < 6) {
        hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2];
    }
    lum = lum || 0;
    // convert to decimal and change luminosity
    var rgb = "#", c, i;
    for (i = 0; i < 3; i++) {
        c = parseInt(hex.substr(i*2,2), 16);
        c = Math.round(Math.min(Math.max(0, c + (c * lum)), 255)).toString(16);
        rgb += ("00"+c).substr(c.length);
    }
    return rgb;
}
Output

You can jump to the latest bin by adding /latest to your URL

Dismiss x
public
Bin info
micbuffapro
0viewers