Un rectangle de sélection en javascript

Sélectionnez à la souris la plage de la photo de gauche que vous voulez agrandir.
Maintenez MAJ enfoncée pour une sélection aussi haute que large.

                                   -


Et comment ça marche ?

Le rectangle de sélection est un <DIV> positionné en absolu au dessus de l'image, et matérialisé par une bordure de couleur (on peut aussi utiliser une image transparente).

Un premier clic sur l'image permet d'enregistrer les coordonnées du premier coin du rectangle. On utilise pour cela l'événement onmousedown (on peut ainsi laisser, si on le souhaite, le bouton de la souris enfoncé pendant la sélection).
Les coordonnées (x1 et y1) sont stockées dans deux boites de saisie d'un formulaire.
Le clic permet aussi de faire passer le paramètre "etat" de 0 à 1 (enregistré dans un input caché du formulaire).

A partir de là, il faut afficher le rectangle dès qu'on déplace la souris.
Un événement onmousemove permet de capter en continu la position de la souris, d'en renvoyer - lorsque le paramètre "etat" est égal à 1 - les coordonnées x2 et y2 dans deux autres boites de saisie de notre formulaire, et d'appeler la fonction trace() qui va se contenter d'affecter au <DIV> les valeurs y1 et x1 pour les propriétés top et left, et y2-y1 et x2-x1 pour height et width (en prenant soin de remettre de l'ordre dans les variables si nécessaire).

Ne reste plus qu'à figer le rectangle à l'occasion d'un nouveau clic ou du relachement du bouton de la souris. L'événement onmouseup, associé avec notre variable "etat" à 1 fait passer le paramètre "etat" à 2, ce qui inhibe le fonctionnement de onmousemove : le rectangle est immobilisé.
Dans notre exemple, on ne se contente pas d'afficher le rectangle et ses coordonnées, mais on utilise les données pour zoomer - toujours en javascript - sur la zone sélectionnée (cliquez ici pour voir le principe du zoom).

Deux points un peu délicats :
- les navigateurs modernes intègrent une fonction drag & drop qui permet de déplacer des images. Cette fonctionnalité interfère sur notre sélection et doit être neutralisée. On utilise les propriétés preventDefault et returnValue de l'évènement click.
- Le rectangle de sélection que nous affichons au dessus de l'image risque d'intercepter les évenements associés à l'image.
Plusieurs solutions possibles :
utiliser une gestion sophistiquée des événements associées à l'empilement d'objets
décaler la position du rectangle de sélection de quelques pixels par rapport au pointeur de la souris
gérer séparément les événements captés par le rectangle de sélection (ajouter aux coordonnées de la souris celles de la position du rectangle)
recouvrir l'image et le rectangle de sélection par une image transparente dont le rôle unique sera de capter les événements.



Le code correspondant :


<SCRIPT>
var sup;var gche;var larg;var hautr;


//tracer rectangle
function trace()
    {
    if(frm.x1.value!=''&&frm.y1.value!=''&&frm.x2.value!=''&&frm.y2.value!='')
        {
        gche=Math.min(frm.x1.value,frm.x2.value);
        sup=Math.min(frm.y1.value,frm.y2.value);
        larg=Math.abs(frm.x1.value-frm.x2.value);
        hautr=Math.abs(frm.y1.value-frm.y2.value);
        document.getElementById('rect').style.top=sup;
        document.getElementById('rect').style.left=gche;
        document.getElementById('rect').style.width=larg;
        document.getElementById('rect').style.height=hautr;
        }
    }

function si_click(evt) //click sur l'image
    {
    if(frm.x1.value!='')frm.etat.value++;
    //empecher drag & drop
    if (evt.preventDefault)evt.preventDefault();
    evt.returnValue = false;
    //reset au 3ème clic
    if(frm.etat.value>2){frm.etat.value=0;efface();}
    }


function si_move(evt) //mousemove sur l'image
    {
    if (typeof evt.offsetX != 'undefined')
        {
        x_souris = evt.offsetX;
        y_souris = evt.offsetY;
        }
    else if (typeof evt.layerX != 'undefined')
        {
        x_souris = evt.layerX;
        y_souris = evt.layerY;
        }

    if(frm.etat.value==0)
        {
        frm.x1.value=Math.round(x_souris*100)/100;
        frm.y1.value=Math.round(y_souris*100)/100;
        }

    if(frm.etat.value==1)
        {
        frm.x2.value = Math.round((Math.max(x_souris-2,frm.x1.value)+ Math.min(x_souris+2,frm.x1.value)-frm.x1.value)*100)/100;
        frm.y2.value = Math.round((Math.max(y_souris-2,frm.y1.value)+ Math.min(y_souris+2,frm.y1.value)-frm.y1.value)*100)/100;
        trace();
        }
    }


function si_move2(evt) //mousemove sur le rectangle
    {
    if (typeof evt.offsetX != 'undefined')
        {
        x_souris = evt.offsetX;
        y_souris = evt.offsetY;
        }
    else if (typeof evt.layerX != 'undefined')
        {
        x_souris = evt.layerX;
        y_souris = evt.layerY;
        }
    x_souris=x_souris+gche;
    y_souris=y_souris+sup;
    if(frm.etat.value==0) //on renseigne le premier point
        {
        frm.x1.value=Math.round(x_souris*100)/100;
        frm.y1.value=Math.round(y_souris*100)/100;
        }
    if(frm.etat.value==1) //on renseigne le deuxième point et on trace le rectangle
        {
        frm.x2.value = Math.round((Math.max(x_souris-2,frm.x1.value)+ Math.min(x_souris+2,frm.x1.value)-frm.x1.value)*100)/100;
        frm.y2.value = Math.round((Math.max(y_souris-2,frm.y1.value)+ Math.min(y_souris+2,frm.y1.value)-frm.y1.value)*100)/100;
        trace();
        }
    }


function affiche_resultat()
    {
    if(frm.x2.value!="" && (frm.x1.value!=frm.x2.value||frm.y1.value!=frm.y2.value))
        {
        frm.etat.value=2;
        droite=Math.max(frm.x1.value,frm.x2.value);
        bas=Math.max(frm.y1.value,frm.y2.value);
        ech=301/hautr;
        document.getElementById('im2').style.height=300*ech;
        document.getElementById('zaza').style.clip="rect("+(ech*sup)+"px,"+(ech*droite)+"px,"+(ech*bas)+"px,"+(ech*gche)+"px)";
        document.getElementById('zaza').style.top=-300-(ech*sup)+"px";
        document.getElementById('zaza').style.left=310-(ech*gche)+"px";
        }
    }


function efface()
    {
    frm.x1.value="";
    frm.y1.value="";
    frm.x2.value="";
    frm.y2.value="";
    frm.etat.value="0";
    document.getElementById('rect').style.width=0;
    document.getElementById('rect').style.height=0;
    document.getElementById('rect').style.top=0;
    document.getElementById('rect').style.left=0;
    document.getElementById('im2').style.height=300;
    document.getElementById('zaza').style.clip="";
    document.getElementById('zaza').style.top="-300px";
    document.getElementById('zaza').style.left="310px";
    }

</SCRIPT>



<!----------FORMULAIRE------------------------------------->

<FORM NAME='frm'>
<INPUT CLASS='inpt' TYPE='text' NAME='x1' onchange='trace();affiche_resultat();'>
<INPUT CLASS='inpt' TYPE='text' NAME='y1' onchange='trace();affiche_resultat();'>
-
<INPUT CLASS='inpt' TYPE='text' NAME='x2' onchange='trace();affiche_resultat();'>
<INPUT CLASS='inpt' TYPE='text' NAME='y2' onchange='trace();affiche_resultat();'>

<INPUT TYPE='hidden' NAME='etat' VALUE='0'>
</FORM>

<!----------L'IMAGE------------------------------------->

<DIV STYLE='position:relative;height:300px'>
<IMG SRC='bleuet.jpg' STYLE='position:absolute;top:0;left:0;height:300px;border:solid blue 1px;' onmouseup='affiche_resultat();' onmousedown='si_click(event);' onmousemove='si_move(event);'>
<DIV ID='rect' onmousedown='si_click(event);' STYLE='position:absolute;top:0;left:0;height:0px;width:0px;border:solid red 1px;' onmousemove='si_move2(event);'></DIV>
</DIV>