Drag'n drop dans un canvas

Cliquer & déplacer/glisser un objet dans un canvas en javascript


Un canvas permet de faire de nombreuses choses, dessiner, écrire, … et piloter le tout en javascript.
En particulier, on peut gérer les événements liés à la souris, par exemple déjà récupérer les coordonnées d'un click.
Ensuite, en gérant convenablement les événements onmousedown, onmousemove et onmouseup on peut assez facilement créer des objets "draggable" en
  • récupérant la position de la souris au click (onmousedown)
  • si la position correspond à celle de l'objet, on déclenche l'événement onmousemove qui appelle une fonction Move
  • cette dernière fonction efface l'objet, puis le redessine à chaque nouvelle position de la souris, tant que le click de la souris n'est pas relˆché, événément onmouseup
  • on désactive alors l'écoute de l'événement onmousemove


Résultat / Affichage


Programmation en html/javascript

Définition du canvas en html…

<div id="overcnv" style="width:400px;height:400px;border:2px solid blue">
  <canvas id="canvas"></canvas>
</div>

…puis le code javascript pour dessiner, animer, glisser

<script>
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
const Width=document.getElementById("overcnv").clientWidth;
const Height=document.getElementById("overcnv").clientHeight;
document.getElementById("canvas").width=Width;
document.getElementById("canvas").height=Height;

const bz=Width/8;
ctx.clearRect(0, 0, Width, Height);

    // Objet dessiné:
function Bloc(X,Y) {
  ctx.strokeStyle = "black";ctx.lineWidth=3;
  ctx.fillStyle = "lightblue";
  ctx.beginPath();
  ctx.roundRect(X-bz/2,Y-bz/2,bz,bz,10);
  ctx.stroke();ctx.fill();
}
XB=100;YB=200;
Bloc(XB,YB);

function MouseClickDown(e){
  // Coordonnées du click, qui va ensuite lancer le déplacement
  Xclick=e.pageX-canvas.offsetLeft;Yclick=e.pageY-canvas.offsetTop;
  // On ne lance le déplacement onmousemove que si
  // le click est dans l'objet
  if (Math.abs(Xclick-XB)<bz/2 && Math.abs(Yclick - YB)<bz/2) {
    document.getElementById("canvas").style.cursor="grabbing";
    canvas.onmousemove = Move;
  }
}

function Move(e){
  ctx.clearRect(0,0,Width,Height);
  XB=e.pageX-canvas.offsetLeft;YB=e.pageY-canvas.offsetTop;
  Bloc(XB,YB);
}

function MouseClickUp(e){canvas.onmousemove = null;}
	 
canvas.onmousedown = MouseClickDown;
canvas.onmouseup = MouseClickUp;
</script>

Mapping entre mousedown/mousemove/mouseup et touchstart/touchmove/touchend

Ce programme répond aux commandes d'une souris: onmousedown, onmouseup et onmousemove, et ne va pas forcément réagir à des commandes tactiles.
Je relaie ici un moyen simple de faire correspondre les événements tactiles aux événements de la souris, provenant de JavaScript mapping touch events to mouse events.
On peut rajouter pour cela dans le javascript les quelques lignes:

 function touchHandler(event) {
     var touches = event.changedTouches,
         first = touches[0],
         type = "";
     switch(event.type) {
         case "touchstart": type = "mousedown"; break;
         case "touchmove":  type = "mousemove"; break;        
         case "touchend":   type = "mouseup";   break;
         default: return;}

     var simulatedEvent = document.createEvent("MouseEvent");
     simulatedEvent.initMouseEvent(type, true, true, window, 1, 
                                   first.screenX, first.screenY, 
                                   first.clientX, first.clientY, false, 
                                   false, false, false, 0/*left*/, null);

     first.target.dispatchEvent(simulatedEvent);
     event.preventDefault();
 }

 function init() {
     document.getElementById("overcnv").addEventListener("touchstart", touchHandler, true);
     document.getElementById("overcnv").addEventListener("touchmove", touchHandler, true);
     document.getElementById("overcnv").addEventListener("touchend", touchHandler, true);
     document.getElementById("overcnv").addEventListener("touchcancel", touchHandler, true);
}
 canvas.onmousedown = MouseClickDown;
 canvas.onmouseup = MouseClickUp;
 init();
On applique ce mapping seulement au canvas, dans la fonction init() et ainsi, on ne désactive aussi que le comportement tactile normal (via event.preventDefault();) dans le canvas et pas dans le reste de la page (les liens continuent de fonctionner, on peut encore scroller sur la page, …)


Voir aussi:
LongPage: h2: 4 - h3: 2