19
avril

Drag & drop avec contraintes

Par Joan Gazel, le 19 avril 2013

Nombre de commentaire : 1.

Ce premier article concerne le développement HTML+JavaScript. Nous allons voir comment mettre en place un drag & drop d’un élément, limité à une certaine zone de l’écran.

La librairie utilisée pour le drag & drop est scriptaculous (librairie qui s’appuie sur prototype, une solution alternative à JQuery). La côte de popularité de prototype était très haute à la fin des années 2000 et décline depuis, mais scriptaculous reste très agréable à utiliser comme solution d’animation JavaScript.

Cela étant dit, commençons par mettre en place les différents éléments dont nous allons avoir besoin dans notre page :

  • – le body de la page (bien sûr)
  • – un div fond qui servira de boîte à l’intérieur de laquelle on autorisera le drag & drop
  • – une img bouton qui sera l’élément que l’on pourra déplacer
  • – enfin une zone de supervision qui nous affichera la position actuelle du bouton à l’intérieur de sa boîte (ce sera le div moniteur)
<!DOCTYPE html>
<hmtl>
<head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
<script src="./js/prototype/prototype.js"></script>
<script src="./js/scriptaculous/scriptaculous.js"></script>
</head>
<body>
<div id="fond">
<img id="bouton" src="./images/logo.png"/>
</div>
<div id="moniteur">
X : <input id="pos_x" name="x" type="text" value="0" readonly="readonly"/>
| Y : <input id="pos_y" name="y" type="text" value="0" readonly="readonly"/>
</div>
</body>

Pour le moment, il n’y a rien de particulier. Ajoutons un peu de style à cette page :

body {
background-color:#ccc;
width:100%;
height:100%;
margin:0;
padding:0;
font-family:Tahoma,Arial,sans-serif;
}
#fond {
margin-top:40px;
background-color:#66c4fc;
box-shadow: 0px 0px 10px #888888;
position:relative;
width:600px;
height:600px;
margin-left:auto;
margin-right:auto;
}
#bouton {
border:1px #999 solid;
border-radius:5px;
cursor:move;
}
#moniteur {
width:300px;
background-color:white;
margin-left:auto;
margin-right:auto;
margin-top:20px;
padding:4px;
text-align:center;
border-radius:10px;
color:#333;
}
#moniteur input {
width:100px;
}

On obtient alors la page suivante :

blog-article1-1

  • – un fond de page gris
  • – une zone de déplacement bleue centrée (avec ombrage)
  • – un bouton représenté par le logo Imnet (avec des coins légèrement arrondis)
  • – une zone de visualisation de la position actuelle (x,y) du bouton dans sa zone de déplacement

Il ne reste plus désormais qu’à rajouter le déplacement effectif de l’objet. Pour cela on utilise la notion de Draggable : quand un élément est draggable il est déplaçable.

Rendons notre bouton déplaçable :

Event.observe(document, "dom:loaded", function() {
new Draggable('bouton');
});

On veut rendre notre bouton déplaçable dès la fin du chargement de la page : on utilise pour cela l’évènement dom:loaded du document pour appeler la fonction de création d’un nouveau draggable en lui passant l’id html de notre objet (ici bouton).

De fait, le bouton est désormais déplaçable librement. Or on voulait le contraindre à rester dans la zone bleue. Pour cela on va légèrement enrichir notre code JavaScript :

  • – on rajoute d’abord une fonction JavaScript qui contraint un nombre à être compris dans un intervalle :
    /*Fonction de transformation de 'n' pour
    qu'il soit compris entre 'lower' et 'upper'
    */
    function constrain(n, lower, upper) {
    if (n > upper) return upper;
    else if (n < lower) return lower;
    else return n;
    }
    
  • – on modifie ensuite la création de notre draggable pour spécifier le paramètre snap qui va nous permettre de spécifier une fonction à utiliser en temps réel au cours du déplacement pour modifier et rectifier sa position afin de le contraindre à rester dans sa boîte :
    new Draggable('bouton', {
    snap: function(x, y, draggable) {
    var elt = draggable.element.getDimensions();
    var parent = draggable.element.up().getDimensions();return [
    constrain(x, 0, parent.width - elt.width),
    constrain(y, 0, parent.height - elt.height)
    ];
    }
    });
    

C’est mieux ! Désormais on ne peut plus sortir le bouton de sa boîte.

Il ne reste plus qu’une seule fonctionnalité à ajouter : mettre à jour les coordonnées du bouton à jour dans les deux zones prévues pour cela à la fin du déplacement. Pour cela on modifie encore une fois la création de notre objet Draggable en spécifiant cette fois la fonction à appliquer à la fin du déplacement par le paramètre onEnd :

new Draggable('bouton', {
snap: function(x, y, draggable) {
var elt = draggable.element.getDimensions();
var parent = draggable.element.up().getDimensions();

return [
constrain(x, 0, parent.width - elt.width),
constrain(y, 0, parent.height - elt.height)
];
},
onEnd:function( draggable, event )
{
// calcul de la position du curseur de la souris dans le div parent
var parentPosition = $('bouton').up().viewportOffset();
var eltPosition = $('bouton').viewportOffset();

$('pos_x').value = (''+((eltPosition['left'] - parentPosition['left'])));
$('pos_y').value = (''+((eltPosition['top'] - parentPosition['top'])));
}
});

Et voilà !

Vous pouvez voir le résultat sur cette page.

Remarque : le code ci-dessus utilise la version 1.7.1 de Prototype qui comporte un bug avec Internet Explorer 9 et 10 (mais pas les versions précédentes). Pour que le bouton soit bien visible en semi-transparence pendant son déplacement, il est nécessaire d’appliquer la correction décrite dans ce blog dans le fichier prototype.js (ce qui a été fait sur le fichier prototype.js utilisé dans cet exemple).

L’ensemble des fichiers utilisés dans cet article peuvent être récupérés en suivant ce lien.



Un commentaire pour “Drag & drop avec contraintes”

  1. Nice blog here! Also your web site loads up fast! What host are you using? Can I get your affiliate link to your host? I wish my site loaded up as fast as yours lol