Fermer

juillet 2, 2018

Redimensionnement d'image à la demande –


Cet article fait partie d'une série de sur la construction d'un exemple d'application – un blog de galerie multi-images – pour l'analyse comparative des performances et les optimisations. (Voir le repo ici.)


Nous avons construit un exemple d'application – un blog de galerie multi-images – pour l'analyse comparative des performances et les optimisations. À ce stade, notre application diffuse la même image quelle que soit la résolution et la taille de l'écran dans laquelle elle est exposée. Dans ce tutoriel de redimensionnement d'image, nous allons le modifier pour servir une version redimensionnée en fonction de la taille d'affichage. 19659003] Objectif

Cette amélioration comporte deux étapes:

  1. Nous devons rendre toutes les images réceptives là où cela peut être utile. Les vignettes sur la page d'accueil et dans les pages de la galerie sont un autre endroit, et l'autre est l'image en taille réelle lorsqu'une image individuelle est cliquée dans la galerie.
  2. Nous devons ajouter une logique de redimensionnement à notre application. Le but est de générer une image redimensionnée à la volée comme il est demandé. Ceci empêchera les images non-populaires de polluer notre disque dur, et assurera que les images les plus populaires seront servies dans des tailles optimales lors de demandes ultérieures.

Responsive Images?

Comme cet article ] explique, les images dans le web moderne sont incroyablement complexes. Au lieu de juste des temps anciens, nous avons maintenant quelque chose de fou comme ceci:






 Humain 

Une combinaison des tailles srcset et est nécessaire dans un scénario où vous doutez que si vous utilisez la même image pour une taille d'écran plus petite, le sujet principal de l'image peut devenir trop petit. Vous souhaitez afficher une image différente (plus centrée sur le sujet principal) dans une taille d'écran différente, mais vous voulez toujours afficher des éléments distincts de la même image en fonction du ratio pixel / périphérique, et souhaitez personnaliser la hauteur et la largeur de l'image

Comme nos images sont des photos et que nous voulons toujours qu'elles soient dans leur position DOM spécifiée par défaut en remplissant le maximum de leur conteneur parent, nous n'avons pas besoin de image (ce qui nous permet définir une source alternative pour une résolution différente ou un support de navigateur – comme essayer de rendre SVG, puis PNG si SVG n'est pas supporté) ou tailles (ce qui nous permet de définir la portion de fenêtre qu'une image doit occuper). Nous pouvons sortir avec srcset qui charge une version de taille différente de la même image en fonction de la taille de l'écran.

Ajout de srcset

Le premier emplacement où l'on rencontre des images est ] home-galleries-lazy-load.html.twig le modèle partiel qui affiche la liste des galeries de l'écran d'accueil.


   {{gallery.name}} "
    Nous pouvons voir ici que le lien de l'image est extrait d'un filtre Twig, qui peut être trouvé dans le fichier <code> src / Twig / ImageRendererExtension.php </code>. Il prend l'ID de l'image et le nom de la route (défini dans l'annotation dans route <code> ImageController </code> <code> serveImageAction </code>) et génère une URL basée sur cette formule: <code> / image / {id } / raw </code> -> remplaçant <code> {id} </code> avec l'ID donné: </p>
<pre><code class= public function getImageUrl (Image $ image)
{
  return $ this-> router-> generate ('image.serve', [
      'id' => $image->getId(),
  ]RouterInterface :: ABSOLUTE_URL);
}

Passons à ceci:

 public function getImageUrl (Image $ image, $ size = null)
{
  return $ this-> router-> generate ('image.serve', [
      'id' => $image->getId() . (($size) ? '--' . $size : "http://www.sitepoint.com/"),
  ]RouterInterface :: ABSOLUTE_URL);
}

Maintenant, toutes nos URL d'images auront - x comme suffixe, où x est leur taille. C'est la modification que nous appliquerons à notre tag img sous la forme de srcset . Changeons le à:


   {{gallery.name}} "
       srcset = "
           {{gallery.images.first | getImageUrl ('1120')}} 1120w,
           {{gallery.images.first | getImageUrl ('720')}} 720w,
           {{gallery.images.first | getImageUrl ('400')}} 400w "
       Si vous actualisez la page d'accueil maintenant, nous remarquerons les nouvelles tailles de srcset listées: </p>
<p> <img decoding= 

Ça ne va pas Si notre fenêtre d'affichage est large, cela demandera des images en taille réelle, malgré le fait qu'elles soient des vignettes, donc au lieu de srcset il est préférable d'utiliser une petite taille de vignette fixe ici: [19659025] {{gallery.name}} "        class = "gallery__leading-image card-img-top ">

Nous avons maintenant des miniatures à la demande, mais qui sont mises en cache et récupérées lorsqu'elles sont déjà générées.

Cherchons les autres srcset

Dans templates / gallery / single-gallery.html.twig nous appliquons le même correctif que précédemment.Nous avons affaire à des vignettes, alors rétrécissons le fichier en ajoutant la taille paramètre dans notre getImageUrl filtre:

 {{image.originalFilename}} "
    class = "unique-gallery__item-image carte-img-top 

Et maintenant pour l'implémentation srcset enfin!

Les différentes vues d'images sont rendues avec une fenêtre modale JavaScript en bas de la même vue de single-gallery:

 {% block javascripts %}
    {{ parent() }}

     {% endblock%}

Il y a un appel append qui ajoute l'élément img dans le corps du modal, donc c'est là que notre attribut srcset doit aller. Mais puisque nos URL d'image sont générées dynamiquement, nous ne pouvons pas vraiment appeler le filtre Twig depuis le script . Une alternative consiste à ajouter le srcset dans les vignettes, puis l'utiliser dans le JS en le copiant à partir des éléments du pouce, mais cela ne ferait pas que charger les images en arrière-plan des vignettes ( parce que notre viewport est large), mais il appelle aussi le filtre 4 fois pour chaque vignette, ralentissant les choses. Au lieu de cela, créons un nouveau filtre Twig dans src / Twig / ImageRendererExtension.php qui générera l'attribut complet srcset pour chaque image.

 public function getImageSrcset (Image $ image)
{
    $ id = $ image-> getId ();
    $ sizes = [1120, 720, 400];
    $ string = "http://www.sitepoint.com/";
    foreach ($ tailles comme taille $) {
        $ string. = $ this-> router-> generate ('image.serve', [
            'id' => $image->getId() . '--' . $size,
        ]RouterInterface :: ABSOLUTE_URL). "http://www.sitepoint.com/". $ size.'w, ';
    }
    $ string = trim ($ chaîne, ',');
    retourne html_entity_decode ($ string);
}

Il ne faut pas oublier d'enregistrer ce filtre:

 public function getFilters ()
{
    retour [
        new Twig_SimpleFilter('getImageUrl', [$this, 'getImageUrl']),
        nouveau Twig_SimpleFilter ('getImageSrcset', [$this, 'getImageSrcset']),
    ]
}

Nous devons ajouter ces valeurs dans un attribut personnalisé, que nous appellerons data-srcset sur chaque vignette individuelle:

  {{image.originalFilename}} "
      data-srcset = "http://www.sitepoint.com/ {{image" getImageSrcset}} "
      class = "unique-gallery__item-image carte-img-top 

Chaque vignette individuelle possède maintenant un attribut data-srcset avec les valeurs srcset requises, mais cela ne se déclenche pas car il s'agit d'un attribut personnalisé, données à utiliser plus tard.

 data-srcset généré

La dernière étape consiste à mettre à jour le JS pour en tirer parti:

 {% block javascripts%}
    {{ parent() }}

     {% endblock%}

Ajout de Glide

Glide est une librairie qui fait ce que l'on veut: un redimensionnement d'image à la demande. Installez-le.

 composer require league / glide

Ensuite, enregistrons-le dans l'application. Nous faisons cela en ajoutant un nouveau service dans src / Services avec le contenu suivant:

  server = $ server = Glide  ServerFactory :: create ([
            'source' => $fm->getUploadsDirectory(),
            'cache' => $fm->getUploadsDirectory().'/cache',
        ]);
    }

    fonction publique getGlide ()
    {
        return $ this-> serveur;
    }
}

Le service consomme le service FileManager déjà déclaré qui est auto-injecté en raison de la nouvelle approche de câblage automatique de Symfony. Nous déclarons les chemins d'entrée et de sortie comme le uploads dir, donnons au répertoire de sortie un suffixe cache et ajoutons une méthode pour renvoyer le serveur. Le serveur est fondamentalement l'instance de Glide qui fait le redimensionnement et sert une image redimensionnée.

Nous devons rendre publique la méthode getUploadsDirectory dans FileManager public, comme elle est actuellement ] private :

 public function getUploadsDirectory ()
{
    return $ this-> path;
}

Enfin, modifions la méthode serveImageAction de ImageController pour qu'elle ressemble à ceci:

 / **
 * @Route ("/ image / {id} / raw", name = "image.serve")
 * /
function public serveImageAction (Demande $ request, $ id, GlideServer $ glide)
{
    $ idFragments = explode ('-', $ id);
    $ id = $ idFragments [0];
    $ size = $ idFragments [1]? nul;

    $ image = $ this-> em-> getRepository (Image :: classe) -> find ($ id);

    if (vide ($ image)) {
        throw new NotFoundHttpException ('Image non trouvée');
    }

    $ fullPath = $ this-> fileManager-> getFilePath ($ image-> getFilename ());

    if (taille $) {

        $ info = pathinfo ($ fullPath);
        $ file = $ info ['filename']. '.' . $ info ['extension'];
        $ newfile = $ info ['filename']. '-'. $ taille. '.' . $ info ['extension'];
        $ fullPathNew = str_replace ($ fichier, $ nouveau fichier, $ fullPath);

        if (file_exists ($ fullPath) &&! fichier_exists ($ fullPathNew)) {

            $ fullPath = $ fullPathNew;
            $ img = $ glide-> getGlide () -> getImageAsBase64 ($ fichier,
                ['w' => $size]);

            $ ifp = fopen ($ fullPath, 'wb');

            $ data = explode (',', $ img);
            fwrite ($ ifp, base64_decode ($ data [1]));
            fclose ($ ifp);
        }
    }

    $ response = new BinaryFileResponse ($ fullPath);
    $ response-> headers-> set ('Content-type',
        mime_content_type ($ fullPath));
    $ response-> headers-> set ('Content-Disposition',
        'attachement; filename = "http://www.sitepoint.com/". $ image-> getOriginalFilename (). "http://www.sitepoint.com/"; ');

    return $ response;
}

Cette méthode explose maintenant l'ID de l'image en double tiret, en séparant la taille de l'ID de l'image. Une fois que Doctrine récupère le chemin de fichier de l'image à partir de la base de données, la taille est attachée au nom de fichier si l'un d'eux a été transmis, sinon l'image d'origine est utilisée. Si cette image n'existe pas, elle est générée à partir du chemin d'origine et sauvegardée pour une utilisation ultérieure.

À des fins de démonstration, nous allons ici plus loin et générons des fichiers manuellement en leur ajoutant la taille et en les sauvegardant dans le télécharge le dossier . Il convient de noter que vous pouvez également utiliser la méthode outputImage de Glide pour sortir directement l'image, et qu'elle sera directement tirée du sous-dossier cache sans l'enregistrer avec un suffixe dans le dossier principal télécharger . Vous pouvez également utiliser la méthode makeImage pour créer l'image et laisser l'ancienne logique de récupération de l'image prendre le dessus. C'est l'approche que nous avons choisie ci-dessous:

 / **
 * @Route ("/ image / {id} / raw", name = "image.serve")
 * /
function public serveImageAction (Demande $ request, $ id, GlideServer $ glide)
{
    $ idFragments = explode ('-', $ id);
    $ id = $ idFragments [0];
    $ size = $ idFragments [1]? nul;

    $ image = $ this-> em-> getRepository (Image :: classe) -> find ($ id);

    if (vide ($ image)) {
        throw new NotFoundHttpException ('Image non trouvée');
    }

    $ fullPath = $ this-> fileManager-> getFilePath ($ image-> getFilename ());

    if (taille $) {

        $ info = pathinfo ($ fullPath);
        $ file = $ info ['filename']. '.' . $ info ['extension'];

        $ cachePath = $ glide-> getGlide () -> makeImage ($ fichier, ['w' => $size]);
        $ fullPath = str_replace ($ fichier, '/ cache /'. $ cachePath, $ fullPath);
    }

    $ response = new BinaryFileResponse ($ fullPath);
    $ response-> headers-> set ('Content-type',
        mime_content_type ($ fullPath));
    $ response-> headers-> set ('Content-Disposition',
        'attachement; filename = "http://www.sitepoint.com/". $ image-> getOriginalFilename (). "http://www.sitepoint.com/"; ');

    return $ response;
}

Notre activité de redimensionnement d'image à la demande est opérationnelle. Tout ce que nous avons à faire est de tester les choses.

Test

Dès que nous actualisons la page d'accueil, qui sera un peu plus lente, les images commenceront à être générées dans var / uploads ] dossier. Jetons un coup d'œil, sans faire défiler jusqu'à la deuxième page

 Images générées

Effectivement, nous avons maintenant une minuscule version miniature de chaque image de la page d'accueil, et c'est l'image qui devient servi. Notez les petites tailles de fichier. Maintenant, accédons à une galerie et cliquons sur une image pour en obtenir une grande version

 Une grande image générée avec srcset

Oui, notre image a été générée à partir de l'original.

Qu'en est-il du mobile? Dans les navigateurs modernes, il est assez facile d'activer le mode mobile. Essayons d'ouvrir une image de la galerie en vue mobile et de vérifier le dossier de l'image par la suite.

 Image générée pour mobile

Et si nous changeons d'orientation et vérifions le dossier?

 mobile

Succès, la taille mobile de notre image a été générée avec succès, et l'image en plein écran d'avant a été réutilisée car c'est la taille de notre écran «mobile» en mode paysage. À la demande srcset a été implémenté avec succès!

L'application avec ces mises à niveau en place a été étiqueté comme cette version .

Conclusion

Dans ce post, nous sommes allés à travers le processus d'optimisation des images pour la livraison dans un site photo-orienté. Nous avons gardé les vignettes à une taille fixe pour de meilleurs résultats, et avec des images en plein écran nous nous sommes concentrés sur l'implémentation de srcset - un ajout simple à tout site web moderne - en collaboration avec Glide, un package de redimensionnement d'image à la demande.

Mais pendant que nous redimensionnons les images, ne serait-il pas intelligent de les optimiser automatiquement pour la qualité et la taille en supprimant les métadonnées? Et est-ce vraiment la meilleure option pour les redimensionner à la demande pendant que l'utilisateur attend ou y a-t-il une autre approche plus pratique? Découvrez dans la prochaine partie.






Source link