Créer un outil de découpe d'image avec HTML5

JavaScriptBeginner
Pratiquer maintenant

Introduction

Ce projet vous guidera tout au long du processus de création d'un outil de découpe d'image simple. À la fin, vous aurez une application interactive qui permet aux utilisateurs de télécharger, d'afficher et de découper des images.

👀 Aperçu

Image cropping tool demo

🎯 Tâches

Dans ce projet, vous allez apprendre :

  • Comment créer la structure HTML pour l'outil de découpe d'image
  • Comment styliser la page web à l'aide de CSS pour la rendre visuellement attrayante
  • Comment initialiser des variables et des écouteurs d'événements à l'aide de JavaScript pour gérer les interactions de l'utilisateur
  • Comment gérer le téléchargement et l'affichage d'images à l'aide de l'API FileReader en JavaScript
  • Comment implémenter le mécanisme de découpe à l'aide de l'API Canvas en JavaScript
  • Comment enregistrer l'image découpée et afficher le résultat

🏆 Réalisations

Après avoir terminé ce projet, vous serez capable de :

  • Comprendre les balises et la structure HTML
  • Appliquer efficacement les propriétés et les sélecteurs CSS
  • Utiliser la syntaxe, les variables et les écouteurs d'événements JavaScript
  • Mettre à profit l'API FileReader en JavaScript pour gérer les téléchargements de fichiers
  • Implémenter la manipulation d'images à l'aide de l'API Canvas en JavaScript

Mettre en place la structure HTML

Exigences :

  • Connaissance des balises et de la structure HTML.

Fonctionnalité :

  • Concevoir une interface permettant aux utilisateurs de télécharger une image et de déclencher le processus de découpe.

Insérez le code HTML dans votre index.html.

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>HTML5 Crop Image</title>
    <link rel="stylesheet" href="style.css" />
  </head>
  <body>
    <input type="file" name="file" id="post_file" />

    <button id="save_button">ENREGISTRER</button>
    <div id="label">
      <canvas id="get_image"></canvas>
      <p>
        <canvas id="cover_box"></canvas>
        <canvas id="edit_pic"></canvas>
      </p>
    </div>
    <p>
      <span id="show_edit"></span>
      <span id="show_pic"><img src="" /></span>
    </p>
    <script type="text/javascript" src="main.js"></script>
  </body>
</html>

Les trois balises <canvas> mentionnées ci-dessus sont utilisées pour traiter le contenu lié aux images. Le traitement détaillé sera fourni dans le code js (JavaScript) ultérieur. Les éléments avec l'identifiant show_edit et l'identifiant show_pic sont pour prévisualiser l'image et visualiser le résultat de la génération de l'image finale.

✨ Vérifier la solution et pratiquer

Styler la page web

Exigences :

  • Familiarité avec les propriétés et les sélecteurs CSS.

Fonctionnalité :

  • Styliser les éléments HTML pour rendre l'interface conviviale et visuellement attrayante.

Insérez le CSS dans votre style.css.

body {
  background-color: #f6f6f6;
  margin: 0;
  padding: 20px;
  text-align: center;
}

#label {
  border: 1px solid #ccc;
  background-color: #fff;
  text-align: center;
  height: 300px;
  width: 300px;
  margin: 20px auto;
  position: relative;
}

#get_image {
  position: absolute;
}

#edit_pic {
  position: absolute;
  display: none;
  background: #000;
}

#cover_box {
  position: absolute;
  z-index: 9999;
  display: none;
  top: 0px;
  left: 0px;
}

#show_edit {
  margin: 0 auto;
  display: inline-block;
}

#show_pic {
  height: 100px;
  width: 100px;
  border: 2px solid #000;
  overflow: hidden;
  margin: 0 auto;
  display: inline-block;
}

canvas {
  position: absolute;
  top: 0;
  left: 0;
}

#save_button {
  padding: 8px 16px;
  background-color: #3498db;
  color: #fff;
  border: none;
  border-radius: 5px;
  cursor: pointer;
  transition: background-color 0.2s;
}

#save_button:hover {
  background-color: #2980b9;
}

input[type="file"] {
  margin-bottom: 20px;
}
✨ Vérifier la solution et pratiquer

Initialiser les variables et les écouteurs d'événements

Exigences :

  • Compréhension fondamentale de la syntaxe, des variables et des écouteurs d'événements JavaScript.

Fonctionnalité :

  • Initialiser les propriétés et les configurations pour l'outil de découpe. Ajouter un écouteur d'événements pour traiter l'image téléchargée.

Dans main.js, écrivez le code pour initialiser les propriétés de l'outil de découpe et configurer les écouteurs d'événements.

var postFile = {
  init: function () {
    var t = this;
    t.regional = document.getElementById("label");
    t.getImage = document.getElementById("get_image");
    t.editPic = document.getElementById("edit_pic");
    t.editBox = document.getElementById("cover_box");
    t.px = 0; //background image x
    t.py = 0; //background image y
    t.sx = 15; //crop area x
    t.sy = 15; //crop area y
    t.sHeight = 150; //crop area height
    t.sWidth = 150; //crop area width
    document
      .getElementById("post_file")
      .addEventListener("change", t.handleFiles, false);
  }
};

Toutes nos fonctions et variables sont encapsulées dans l'objet postFile. La fonction init mentionnée ci-dessus fixe principalement certaines valeurs initiales.

t.px = 0;
t.py = 0;
t.sx = 15;
t.sy = 15;
t.sHeight = 100;
t.sWidth = 100;

Les variables t.px et t.py représentent les coordonnées de l'image d'arrière-plan dans la zone de prévisualisation en temps réel ; t.sx, t.sy, t.sHeight et t.sWidth représentent respectivement les coordonnées x, y et la largeur, la hauteur de l'image.

Nous obtenons également plusieurs éléments sur lesquels nous allons opérer plus tard grâce à document.getElementById.

document
  .getElementById("post_file")
  .addEventListener("change", t.handleFiles, false);

Nous écoutons l'événement change du formulaire input avec l'id post_file pour traiter les fichiers téléchargés par l'utilisateur. Ici, nous le déléguons à la fonction handleFiles. Donc, ensuite, nous allons implémenter la fonction handleFiles.

✨ Vérifier la solution et pratiquer

Gérer le téléchargement et l'affichage d'images

Exigences :

  • Connaissance de base de l'API FileReader en JavaScript.

Fonctionnalité :

  • Vérifier que lorsque les utilisateurs téléchargent une image, elle est correctement traitée, lue et affichée à l'écran.

Agrandissez votre main.js avec les fonctions pour traiter et afficher l'image téléchargée.

  1. Pour implémenter la fonction handleFiles

    Ici, nous utilisons l'API HTML5 File. Tout d'abord, en invoquant new FileReader(), nous instancions un objet FileReader nommé oFReader. Ensuite, nous appelons sa méthode readAsDataURL() pour lire le contenu du fichier et le convertir en un format encodé en base64.

    Enfin, lorsque le fichier est entièrement lu et chargé, nous traitons l'image que nous avons lue à l'aide de postFile.paintImage(oFREvent.target.result). En termes simples, nous redessinons les données d'image que nous avons lues sur le navigateur.

handleFiles: function () {
        var fileList = this.files[0];
        var oFReader = new FileReader();
        oFReader.readAsDataURL(fileList);
        oFReader.onload = function (oFREvent) {
            postFile.paintImage(oFREvent.target.result);
        };
},
  1. Pour implémenter la fonction paintImage

    L'étape la plus cruciale ici est de dessiner l'image selon la taille du conteneur à l'aide de canvas. Dans l'étape précédente, en utilisant le FileReader de l'API File, nous avons déjà obtenu l'URL de l'image que nous voulons télécharger (la valeur de oFREvent.target.result). L'étape suivante est d'utiliser canvas pour dessiner cette image. Tout d'abord, nous utilisons getImage.getContext pour obtenir le contenu 2D de <canvas id="get_image"></canvas>, ce qui peut être simplement compris comme le contenu de l'image. Après cela, nous utilisons new Image() pour créer un élément <img> et définir la valeur de son attribut src.

paintImage: function (url) {
        var t = this;
        var createCanvas = t.getImage.getContext("2d");
        var img = new Image();
        img.src = url;
        img.onload = function () {
            if (
                img.width < t.regional.offsetWidth &&
                img.height < t.regional.offsetHeight
            ) {
                t.imgWidth = img.width;
                t.imgHeight = img.height;
            } else {
                var pWidth = img.width / (img.height / t.regional.offsetHeight);
                var pHeight = img.height / (img.width / t.regional.offsetWidth);
                t.imgWidth = img.width > img.height? t.regional.offsetWidth : pWidth;
                t.imgHeight =
                    img.height > img.width? t.regional.offsetHeight : pHeight;
            }
            t.px = (t.regional.offsetWidth - t.imgWidth) / 2 + "px";
            t.py = (t.regional.offsetHeight - t.imgHeight) / 2 + "px";

            t.getImage.height = t.imgHeight;
            t.getImage.width = t.imgWidth;
            t.getImage.style.left = t.px;
            t.getImage.style.top = t.py;

            createCanvas.drawImage(img, 0, 0, t.imgWidth, t.imgHeight);
            t.imgUrl = t.getImage.toDataURL();
            t.cutImage();
            t.drag();
        };
},

Dans la fonction img.onload, notre objectif principal est de redessiner l'image dans sa taille d'origine tout en maintenant ses proportions, c'est pourquoi nous avons une condition if. En fin de compte, nous utilisons la ligne de code createCanvas.drawImage(img,0,0,t.imgWidth,t.imgHeight); pour réellement afficher l'image.

✨ Vérifier la solution et pratiquer

Mettre en œuvre le mécanisme de découpe

Exigences :

  • Familiarité avec l'API Canvas en JavaScript pour le dessin et la manipulation d'images.

Fonctionnalité :

  • Ajouter une région de découpe sur l'image affichée et rendre cette région déplaçable. Cela offre aux utilisateurs la flexibilité de sélectionner la zone souhaitée à découper.

Ajoutez les méthodes pertinentes dans main.js.

  1. Créer une méthode cutImage

    La méthode cutImage est principalement responsable de deux tâches : l'une est de créer une couche de masque, et l'autre est d'utiliser la propriété CSS background pour fournir une prévisualisation en temps réel de la zone de découpe sélectionnée.

cutImage: function () {
        var t = this;

        t.editBox.height = t.imgHeight;
        t.editBox.width = t.imgWidth;
        t.editBox.style.display = "block";
        t.editBox.style.left = t.px;
        t.editBox.style.top = t.py;

        var cover = t.editBox.getContext("2d");
        cover.fillStyle = "rgba(0, 0, 0, 0.5)";
        cover.fillRect(0, 0, t.imgWidth, t.imgHeight);
        cover.clearRect(t.sx, t.sy, t.sHeight, t.sWidth);

        document.getElementById("show_edit").style.background =
            "url(" + t.imgUrl + ")" + -t.sx + "px " + -t.sy + "px no-repeat";
        document.getElementById("show_edit").style.height = t.sHeight + "px";
        document.getElementById("show_edit").style.width = t.sWidth + "px";
},
  1. Créer une méthode drag
drag: function () {
        var t = this;
        var draging = false;
        var startX = 0;
        var startY = 0;

        document.getElementById("cover_box").onmousemove = function (e) {
            var pageX = e.pageX - (t.regional.offsetLeft + this.offsetLeft);
            var pageY = e.pageY - (t.regional.offsetTop + this.offsetTop);

            if (
                pageX > t.sx &&
                pageX < t.sx + t.sWidth &&
                pageY > t.sy &&
                pageY < t.sy + t.sHeight
            ) {
                this.style.cursor = "move";

                this.onmousedown = function () {
                    draging = true;

                    t.ex = t.sx;
                    t.ey = t.sy;

                    startX = e.pageX - (t.regional.offsetLeft + this.offsetLeft);
                    startY = e.pageY - (t.regional.offsetTop + this.offsetTop);
                };
                window.onmouseup = function () {
                    draging = false;
                };

                if (draging) {
                    if (t.ex + (pageX - startX) < 0) {
                        t.sx = 0;
                    } else if (t.ex + (pageX - startX) + t.sWidth > t.imgWidth) {
                        t.sx = t.imgWidth - t.sWidth;
                    } else {
                        t.sx = t.ex + (pageX - startX);
                    }

                    if (t.ey + (pageY - startY) < 0) {
                        t.sy = 0;
                    } else if (t.ey + (pageY - startY) + t.sHeight > t.imgHeight) {
                        t.sy = t.imgHeight - t.sHeight;
                    } else {
                        t.sy = t.ey + (pageY - startY);
                    }

                    t.cutImage();
                }
            } else {
                this.style.cursor = "auto";
            }
        };
},

Pour comprendre cette méthode, vous devez saisir les points clés suivants :

var pageX = e.pageX - (t.regional.offsetLeft + this.offsetLeft);
var pageY = e.pageY - (t.regional.offsetTop + this.offsetTop);

Avec les deux lignes de code ci-dessus, nous obtenons la distance entre la souris et l'image d'arrière-plan. e.pageX représente la distance de la souris au bord gauche du navigateur, et t.regional.offsetLeft + this.offsetLeft calcule la distance de l'image au bord gauche du navigateur. De même, la distance supérieure peut être déduite.

 if ( pageX > t.sx && pageX < t.sx + t.sWidth && pageY > t.sy && pageY < t.sy + t.sHeight )

Ayant compris la distance entre la souris et l'image d'arrière-plan, cela devrait être facile à saisir : cela détermine si la souris est dans la région de l'image.

t.ex = t.sx;
t.ey = t.sy;

startX = e.pageX - (t.regional.offsetLeft + this.offsetLeft);
startY = e.pageY - (t.regional.offsetTop + this.offsetTop);

Ces deux extraits de code méritent d'être soulignés. Les deux premières lignes enregistrent les coordonnées de la dernière capture d'écran (ou si il n'y en a pas eu de précédente, alors les coordonnées initiales) ; les deux lignes suivantes enregistrent les coordonnées lorsque la souris est pressée. Vous pouvez examiner ces valeurs séparément à l'aide de console.log().

if (draging) {
  if (t.ex + (pageX - startX) < 0) {
    t.sx = 0;
  } else if (t.ex + (pageX - startX) + t.sWidth > t.imgWidth) {
    t.sx = t.imgWidth - t.sWidth;
  } else {
    t.sx = t.ex + (pageX - startX);
  }

  if (t.ey + (pageY - startY) < 0) {
    t.sy = 0;
  } else if (t.ey + (pageY - startY) + t.sHeight > t.imgHeight) {
    t.sy = t.imgHeight - t.sHeight;
  } else {
    t.sy = t.ey + (pageY - startY);
  }

  t.cutImage();
}

Le code ci-dessus signifie essentiellement : si nous sommes en train de faire glisser, nous devons mettre à jour en temps réel les valeurs de t.sx et t.sy en fonction des changements de coordonnées et appeler la méthode cutImage pour fournir une prévisualisation en direct.

les coordonnées de la zone de découpe pendant le déplacement = dernière position enregistrée + (position actuelle de la souris - position lorsque la souris a été pressée)

✨ Vérifier la solution et pratiquer

Enregistrer l'image découpée

Exigences :

  • Connaissance de l'utilisation de canvas pour extraire et afficher les données d'image.

Fonctionnalité :

  • Après avoir découpé la zone d'image souhaitée, permettre aux utilisateurs de sauvegarder cette région découpée et d'afficher le résultat à l'écran.

Améliorez la fonction d'initialisation dans main.js pour gérer la sauvegarde de l'image découpée.

var postFile = {
  init: function () {
    //...
    document.getElementById("save_button").onclick = function () {
      t.editPic.height = t.sHeight;
      t.editPic.width = t.sWidth;
      var ctx = t.editPic.getContext("2d");
      var images = new Image();
      images.src = t.imgUrl;

      images.onload = function () {
        ctx.drawImage(
          images,
          t.sx,
          t.sy,
          t.sHeight,
          t.sWidth,
          0,
          0,
          t.sHeight,
          t.sWidth
        );
        document.getElementById("show_pic").getElementsByTagName("img")[0].src =
          t.editPic.toDataURL();
      };
    };
  }
};
postFile.init();

De manière similaire à l'implémentation de la méthode paintImage, nous écoutons tout d'abord l'événement de clic sur le bouton de sauvegarde. Nous utilisons ensuite la méthode drawImage pour afficher la région d'image sélectionnée. Enfin, nous employons la méthode toDataURL pour convertir l'image en un format encodé en base64. Cette valeur est ensuite assignée à l'attribut src de l'img sous show_pic. De cette manière, le découragement et la sauvegarde de l'image sont terminés.

✨ Vérifier la solution et pratiquer

Tester l'outil

  • Ouvrez index.html dans un navigateur web.
    open web
  • Téléchargez une image et testez la fonctionnalité de découpe.
  • L'effet de la page est le suivant :
    image cropping tool demo
✨ Vérifier la solution et pratiquer

Résumé

Félicitations ! Vous avez construit un outil de découpe d'image de base à l'aide d'HTML5 et de JavaScript. Cette base peut être approfondie pour des fonctionnalités plus avancées ou d'autres tâches de manipulation d'images. Entraînez-vous en améliorant l'outil ou en explorant d'autres projets de développement web !