Approche technique
La réaliastion se décompose en plusieurs étapes : charger une image sur un élément canvas, découper une zone spécifique pour former le curseur, puis gérer les interactions de l'utilisateur pour valider la position.
1. Chargement et affichage de l'image
On utilise un canvas pour dessiner l'image de fond. Le chargement est asynchrone pour garantir que l'image est prête avant le rendu.
<div id="captcha-container">
<canvas id="zone-dessin"></canvas>
</div>
<script>
const zoneDessin = document.getElementById('zone-dessin');
const contexte = zoneDessin.getContext('2d');
const chargerImage = (urlSource) => {
return new Promise((resolution, rejet) => {
const imageObj = new Image();
imageObj.src = urlSource;
imageObj.crossOrigin = 'anonymous';
imageObj.onload = () => resolution(imageObj);
imageObj.onerror = () => rejet(new Error('Échec du chargement'));
});
};
const dessinerImage = (imageObj) => {
const largeur = imageObj.width;
const hauteur = imageObj.height;
zoneDessin.width = largeur;
zoneDessin.height = hauteur;
contexte.drawImage(imageObj, 0, 0, largeur, hauteur);
return { largeur, hauteur };
};
const initialiser = async () => {
try {
const imageChargee = await chargerImage('url_image.jpg');
const dimensions = dessinerImage(imageChargee);
} catch (erreur) {
console.error('Problème lors du chargement de l\'image');
}
};
initialiser();
</script>
2. Découpe du curseur glissant
Une portion rectangulaire est extraite de l'image principale pour devenir le curseur déplaçable. Cette zone est ensuite effacée du canvas initial.
const creerCurseur = (largeurCanvas, hauteurCanvas) => {
const largeurSlider = 45;
const hauteurSlider = 45;
const posX = Math.floor(Math.random() * (largeurCanvas - largeurSlider));
const posY = (hauteurCanvas - hauteurSlider) / 2;
const donneesImage = contexte.getImageData(posX, posY, largeurSlider, hauteurSlider);
contexte.clearRect(posX, posY, largeurSlider, hauteurSlider);
const canvasTemporaire = document.createElement("canvas");
canvasTemporaire.width = largeurSlider;
canvasTemporaire.height = hauteurSlider;
const ctxTemp = canvasTemporaire.getContext("2d");
ctxTemp.putImageData(donneesImage, 0, 0);
const elementImg = document.createElement('img');
elementImg.src = canvasTemporaire.toDataURL();
elementImg.style.position = 'absolute';
elementImg.style.top = `${posY}px`;
elementImg.style.left = '0px';
elementImg.classList.add('curseur-actif');
document.getElementById('captcha-container').appendChild(elementImg);
return { element: elementImg, positionCible: posX };
};
3. Gestion du déplacement et validation
Pour éviter les problèmes liés au glisser-déposer natif, on utilise les événements souris pour contrôler le mouvement du curseur. La validation se fait en comparant la position finale avec la position cible.
const configurerEvenements = (elementCurseur, positionCible) => {
let origineX = 0;
let deplacementActif = false;
const demarrerDeplacement = (evenement) => {
deplacementActif = true;
origineX = evenement.clientX;
evenement.preventDefault();
};
const enCoursDeplacement = (evenement) => {
if (!deplacementActif) return;
const deltaX = evenement.clientX - origineX;
elementCurseur.style.left = `${deltaX}px`;
};
const terminerDeplacement = () => {
deplacementActif = false;
const positionActuelle = parseInt(elementCurseur.style.left, 10);
const tolerance = 8;
if (Math.abs(positionActuelle - positionCible) < tolerance) {
alert('Validation réussie !');
} else {
alert('Position incorrecte');
}
elementCurseur.classList.remove('curseur-actif');
};
elementCurseur.addEventListener('mousedown', demarrerDeplacement);
document.addEventListener('mousemove', enCoursDeplacement);
document.addEventListener('mouseup', terminerDeplacement);
};
4. Assemblage final
Les fonctions sont enchaînées pour former le processus complet : chargement, découpe, puis gestion des interactions.
const demarrerCaptcha = async () => {
try {
const img = await chargerImage('url_image.jpg');
const { largeur, hauteur } = dessinerImage(img);
const { element, positionCible } = creerCurseur(largeur, hauteur);
configurerEvenements(element, positionCible);
} catch (erreur) {
console.error('Initialisation échouée');
}
};
demarrerCaptcha();
Une classe CSS comme .curseur-actif { cursor: grab; } peut être ajoutée pour améliorer l'expérience utilisateur.