11526 sujets

JavaScript, DOM et API Web HTML5

Bonjour,
Je viens vers vous car j'ai un problème que je n'arrive pas à résoudre.
Je suis en train de créer une interface pour zoomer une image, pouvoir la déplacer et aussi placer des marqueurs et des zones dessus. Pour les marqueurs et les zones, j'utilise la librairie raphael.js.

On peut zoomer avec la molette de la souris, avec les touches + et - du clavier et avec les boutons + et - sur l'image.
Tout fonctionne nickel jusqu'à ce que j'affiche mes marqueurs et zones : lorsque je zoome, les marqueurs et les zones ne restent pas positionnés à leur place, ils se décalent.

Pour voir un peu mieux de quoi je parle, voici une démo :
Démo qui bug

Au survol de l’œil du chat (celui de gauche sur l'image), vous verrez une zone bleue qui l'entoure.
Vous pouvez également choisir l'option "Oeil droit" dans la liste déroulante pour afficher un marqueur sur l’autre œil.
Ensuite, zoomez et vous constaterez le problème. Le marqueur et la zone ne restent pas sur les yeux.

Ce qui est étrange, c'est que la zone et le marqueur se repositionnent bien sur les yeux quand je suis en zoom maximum.
Et une fois en zoom maximum, je peux dézoomer et rezoomer autant que je veux, plus rien ne se décale.
Mais si j'actualise l'image, le problème ressurgit bien entendu.

A force de bidouillage, j'ai pu constater que cela pouvait venir de la largeur de la carte. En effet, si j'élargis le "canvas" suffisamment, le décalage ne se produit plus, comme sur cet exemple.
Démo qui fonctionne

J'en déduis que la fonction zoom est mal codée quelque part.

Je vous donne le lien du pack complet avec tous les fichiers, ce sera peut-être plus simple que de lire le code sur ce topic (que je mets quand même ci-dessous) :
Pack complet

Merci d'avance pour votre aide. Smiley smile

Javascript :

Les variables "largeur_carte" et "hauteur_carte" sont définies dans le fichier html.

var realWidth;
var realHeight;
var zoom = -1; 
var offsetX = 0;
var offsetY = 0;
var arr = new Array();
var lieux = new Array();
var map = Raphael("map", largeur_carte, hauteur_carte);
var temps = 0
var mem_x=-1
var mem_y=-1
map.canvas.setAttribute('preserveAspectRatio', 'xMidYMin'); 

// --- Paramètres de base ---
var zoom_max = 20; // amplitude de zoom maximum 
var directions_step = 100; // nombre de pixel de déplacement à chaque clic de flèches du clavier
var zoom_on=1; // Active le zoom (0 ou 1)

// --- Paramètres des pastilles ---
var taille_pastille = 25 ; 
var taille_icone = '30px' ; 
var taille_pastille_all = 25 ;
var taille_icone_all = '30px' ;
var couleur_pastille = '#000' ;
var couleur_icone = '#fff' ;
var couleur_pastille_focus = 'red' ;
var couleur_icone_focus = '#000' ; 
var icon_dot=new Array(
	'\uf3c5'	/* Pastille */
); 

// --- Paramètres des zones au démarrage ---
var opacite_zone_init = 0; // opacité
var contours_zone_init = 0; // épaisseur des contours
var couleur_contours_zone_init = '#000'; // couleur des contours

// --- Paramètres des zones au survol ---
var opacite_zone_survol = 0.5; // opacité 
var contours_zone_survol = 1; // épaisseur des contours 
var couleur_contours_zone_survol = '#000'; // couleur des contours
var delai_zone_survol = 300; // temps d'animation en ms

// --- Paramètres des zones quand on quitte le survol ---
var opacite_zone_survol_off = 0; // opacité 
var contours_zone_survol_off = 0; // épaisseur des contours 
var couleur_contours_zone_survol_off = '#000'; // couleur des contours


/*
????????????????????????????????????????????????????????????????????????????????????????????????
?//////////////////////////////////////////////////////////////////////////////////////////////?
?////////////////////////////////////////   PARAMETRES   //////////////////////////////////////?
?////////////////////////////////////////      DES       //////////////////////////////////////?
?////////////////////////////////////////     ZONES      //////////////////////////////////////?
?//////////////////////////////////////////////////////////////////////////////////////////////?
????????????????????????????????????????????????????????????????????????????????????????????????
*/

// Paramètres des zones (vue initiale)

setSize(0);
var wrapper = map.circle();
var dot = map.text();
var style = {
    fill: "black",
    stroke: couleur_contours_zone_init,
    "fill-opacity": opacite_zone_init,
    "stroke-width": contours_zone_init,
    "stroke-linejoin": "round",
    cursor: "pointer",
};


// Paramètres des zones (au survol et au clic)

for (var regionName in zones) {
    var obj = map.path(zones[regionName].path);
    arr[obj.id] = regionName;
    obj.attr(style);
    obj.attr({
        fill: zones[arr[obj.id]].bord
    });
    obj.hover(
        function() {
            if (viewAll == 0) {
                this.animate({
                    stroke: couleur_contours_zone_survol,
                    "fill-opacity": opacite_zone_survol,
                    "stroke-width": contours_zone_survol,
                }, delai_zone_survol);
                this.toFront();
            }
        },
        function() {
                this.animate({
                    fill: zones[arr[this.id]].bord,
                    "fill-opacity": opacite_zone_survol_off,
                    "stroke-width": contours_zone_survol_off,
                    stroke: couleur_contours_zone_survol_off
                }, delai_zone_survol);
                this.toBack();
        });
    obj.click(function() {
        if (viewAll ==0) {
            nom_article = zones[arr[this.id]].name.toLowerCase().replace(/ /g, '_').replace(/'/g, '_').replace(/:/g, '-');
            $('#txt').html("<h2>" + zones[arr[this.id]].name + "</h2>" +
                zones[arr[this.id]].txt);
        }
    });
}
window.addEventListener("resize", setSize(zoom));

var viewAll = 0;


/*
????????????????????????????????????????????????????????????????????????????????????????????????
?//////////////////////////////////////////////////////////////////////////////////////////////?
?////////////////////////////////////////   RECHERCHE   ///////////////////////////////////////?
?////////////////////////////////////////      DE       ///////////////////////////////////////?
?////////////////////////////////////////     LIEUX     ///////////////////////////////////////?
?//////////////////////////////////////////////////////////////////////////////////////////////?
????????????????????????????????????????????????????????????????????????????????????????????????
*/

// Recherche et affichage des items

search.onchange = function(e) {
    displayLieux($("#search").val());
}

function displayLieux(search)
{
    dot.remove();
	wrapper.remove();
    $('[id^=iconID').remove();
	$('[id^=wrapperID').remove();
    var x = y = 0;
    var txt = "";
    tys = typeof search
    if ( Number(search)==search ) i = search;
    else
    {
        i=0;
        while (i<items.length && items[i].name != search)
        {
            i++;
        }
    }
    if (i<items.length)
    {
            x = items[i].x;
            y = items[i].y;

            nom_article = items[i].name.toLowerCase().replace(/ /g, '_').replace(/'/g, '_').replace(/:/g, '-');
            $('#txt').html("<h2>" + items[i].name + "</h2>" +
                items[i].txt);
            {
                var PlaceImage = new DonnePlaceObjet(document.getElementById("map"));
                var ratio = largeur_carte / realWidth;
                offsetX = x / ratio - (screen.availWidth - PlaceImage.Left) / 2;
                offsetY = y / ratio - (screen.availHeight - PlaceImage.Top) / 2;
                moveMap(0);
            }
    }
    if (x != 0) {
		wrapper = map.circle(x,y,taille_pastille).attr({
			fill:couleur_pastille,
			stroke:'none'
		});
		dot = map.text(x,y,'\uf3c5').attr({
			fill: couleur_icone,
			stroke: '#000',
			'stroke-width':'0',
			'font-size': taille_icone,
			'font-weight': '300',
			'font-family':'FontAwesome'
		});
    }
}


/*
????????????????????????????????????????????????????????????????????????????????????????????????
?//////////////////////////////////////////////////////////////////////////////////////////////?
?////////////////////////////////////////   DEPLACEMENT   /////////////////////////////////////?
?////////////////////////////////////////      DE LA      /////////////////////////////////////?
?////////////////////////////////////////      CARTE      /////////////////////////////////////?
?//////////////////////////////////////////////////////////////////////////////////////////////?
????????????????????????????????????????????????????????????????????????????????????????????????
*/

// Commandes clavier

$(document).keydown(function(e) {
    switch (e.which) {
        case 37: // flèche gauche
            moveMap(8);
            break;
        case 38: // flèche haut
            moveMap(1);
            break;
        case 39: // flèche droite
            moveMap(3);
            break;
        case 40: // flèche bas
            moveMap(5);
            break;
		case 107: // +
				zoom_buttons(0.5)
				break;
		case 109: //-
			zoom_buttons(-0.5);
			break;
    }
});


function moveMap(dir) {
    var step = directions_step;
    var ratio = largeur_carte / realWidth;
    switch (dir) {
        case 1:
            offsetY = offsetY - step;
            break;
        case 3:
            offsetX = offsetX + step;
            break;
        case 5:
            offsetY = offsetY + step;
            break;
        case 8:
            offsetX = offsetX - step;
            break;
    }
    if (offsetX > (realWidth * (zoom - 1) / zoom))
        offsetX = (realWidth * (zoom - 1) / zoom);
    if (offsetX < 0)
        offsetX = 0;
    max_h=hauteur_carte/largeur_carte*document.getElementById("map").offsetWidth*zoom-window.innerHeight;
    if (offsetY > max_h)
        offsetY = max_h;
    if (offsetY < 0)
        offsetY = 0;
    $("#map").css('background-position', -offsetX + 'px ' + -offsetY + 'px');
    map.setViewBox(offsetX * ratio, offsetY * ratio, largeur_carte, hauteur_carte, false);
if (isNaN(offsetX) || isNaN(offsetY) || isNaN(zoom))
{
	offsetX=0;
	offsetY=0;
}
}


// Drag and Drop

var appui;
var appui_modif = 0;

$("#map").mouseup(function(e) {
 //   appui = 0;
    if (appui_modif == 1)
    {
        appui_modif = 0;
    }
    else
    {}
});
$("#map").mousemove(function(e) {
    if ((e.buttons&1)!=1)
    {
        appui = 0;
    }
    else
    {
        if (appui==1)
        {
            glisse_carte(e.pageX,e.pageY)
        }
        else
        {
            mem_x = e.pageX;
            mem_y = e.pageY;
            appui=1;
        }
    }
}
);

var mem_x=-1;
var mem_y=-1;

function glisse_carte(X,Y)
{
    //var ratio = largeur_carte / realWidth;
    appui_modif = 1;
    if (mem_x>=0 && mem_y>=0)
    {
    	offsetX = offsetX - (X - mem_x)
    	offsetY = offsetY - (Y - mem_y)
    }
    moveMap(0);
    mem_x = X;
    mem_y = Y;
}



/*
????????????????????????????????????????????????????????????????????????????????????????????????
?//////////////////////////////////////////////////////////////////////////////////////////////?
?////////////////////////////////////////                 /////////////////////////////////////?
?////////////////////////////////////////      ZOOM       /////////////////////////////////////?
?////////////////////////////////////////                 /////////////////////////////////////?
?//////////////////////////////////////////////////////////////////////////////////////////////?
????????????????????????????????????????????????????????????????????????????????????????????????
*/

// Zoom avec le clavier

function zoom_buttons(delta)
{
    var PlaceImage = new DonnePlaceObjet(document.getElementById("map"));
    zoom_total(delta, (Math.min(screen.availWidth,document.getElementById("map").offsetWidth * zoom) - PlaceImage.Left) / 2, (Math.min(screen.availHeight,hauteur_carte/largeur_carte*document.getElementById("map").offsetWidth * zoom) - PlaceImage.Top) / 2);    
}


// Zoom avec la molette de la souris

$(document).on('wheel', function(e)
{
	if (zoom_on==1 && (+offsetX + e.pageX - document.getElementById("map").offsetLeft)<=realWidth && (+offsetY + e.pageY) * largeur_carte <=hauteur_carte*realWidth)
    {
        var delta=0;
        if (e.originalEvent.deltaY>0)
        {
            delta =-0.5;
        }
        else
        {
            delta =+0.5;
        }
        var PlaceImage = new DonnePlaceObjet(document.getElementById("map"));
        var x = e.originalEvent.pageX - PlaceImage.Left;
        var y = e.originalEvent.pageY - PlaceImage.Top;
        zoom_total(delta, x, y);
    }
    else {}
});

function zoom_total(delta, x, y)
{
    var zoomSlide = zoom;
    if (x > 0 && y < (document.getElementById("map").offsetWidth / largeur_carte * hauteur_carte * zoomSlide - offsetY))
    {
        var rap=document.getElementById("map").offsetWidth*zoom;
        var exRatioX = (x + offsetX) / rap;
        var exRatioY = (y + offsetY) / rap;
        setSize(zoomSlide + delta);
        rap=document.getElementById("map").offsetWidth*zoom
        offsetX = (exRatioX * rap) - x;
        offsetY = (exRatioY * rap) - y;
        moveMap(0);
    }
    else {}
}

function DonnePlaceObjet(obj) {
    this.Left = 0;
    this.Top = 0;
    if (obj) {
        var objTemp = obj.offsetParent;
        this.Left = obj.offsetLeft;
        this.Top = obj.offsetTop;
        while (objTemp) {
            this.Left = this.Left + objTemp.offsetLeft;
            this.Top = this.Top + objTemp.offsetTop;
            objTemp = objTemp.offsetParent;
        }
    }
    return this;
}


// Fixe la taille de l'image suivant le zoom

function setSize(z) {
    if (zoom!=z)
    {
        if (z>zoom_max)
        {
            zoom=zoom_max
        }
        else if (z<Math.min(1,(largeur_carte/document.getElementById("map").offsetWidth)/(hauteur_carte/window.innerHeight)))
        {
            zoom=Math.min(1,(largeur_carte/document.getElementById("map").offsetWidth)/(hauteur_carte/window.innerHeight));
        }
        else
        {
            zoom = z;
        }
        realWidth = Math.min(document.getElementById("map").offsetWidth, window.innerWidth) * zoom;
        realHeight = Math.min(document.getElementById("map").offsetHeight, window.innerHeight) * zoom;
        map.setSize(document.getElementById("map").offsetWidth * zoom,Math.max(document.getElementById("map").offsetHeight * zoom, window.innerHeight));
        moveMap(0);
        $("#map").css('background-size', (document.getElementById("map").offsetWidth * zoom) + 'px '+(hauteur_carte/largeur_carte*document.getElementById("map").offsetWidth * zoom )+'px' );
    }
    else {}
}


Le fichier data.js avec les marqueurs et zones :

var zones = [
	
	{  	name: "Oeil gauche",
		txt: "",
		bord: "blue",
        path:"M 97, 252, 140, 252, 140, 297, 99, 297 Z"
	}
	
];

var items = [

	{ type:0,name:"Oeil droit",	x:201, y:276,  txt:""},
	
];



Et enfin le fichier html (démo qui bug):
<!DOCTYPE html>
<html lang="fr">

<head>
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
	<title>Chat</title>
	<link href="files/css/style.css" rel="stylesheet" type="text/css">
	<link href="files/fonts/fontawesome-free-6.1.1-web/css/all.css" rel="stylesheet" type="text/css">
	<link href="files/css/jquery-ui.min.css" rel="stylesheet" type="text/css"> 	
	<script type="text/javascript" src="files/javascript/jquery.min.js"></script>
	<script type="text/javascript" src="files/javascript/jquery-ui.min.js"></script>
	<script type="text/javascript" src="files/javascript/raphael.min.js"></script>
	<script type="text/javascript" src="files/data/demo/demo.js"></script>
	
	<script type="text/javascript">
	var largeur_carte  = 378 ; // Largeur de la carte en px
	var hauteur_carte = 567 ; // Hauteur de la carte en px	
	</script>
	
</head>

<body>

<!-- Image -->
<div id="map" style="background-image: url('files/images/demo_1.jpg')"></div>

<!-- Boutons Zoom -->
<div id="zoom">
	<button class="zoomplus" onclick="zoom_buttons(0.5)"></button><br />
	<button class="zoommoins" onclick="zoom_buttons(-.5)"></button>
</div>

<!-- Panneau de commandes -->
<div id="board">		

		<!-- Recherche -->
		<div id="searchpanel">
			<div id="viewListe">
			<select id="search">
				<option>Sélectionnez une pastille</option>
				<option value="Oeil droit">Oeil droit</option>
			</select>
			</div>
		</div>

	<div class="clr"></div>
	
	<!-- Panneau textuel -->
	<div id="textepanel">
		<div id="txt">
		</div>
	</div>	
	
</div>

<script type="text/javascript" src="files/javascript/fonctions.js"></script>

</body>
</html>

Modifié par bendeb (12 May 2022 - 14:50)