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: Comme cet article ] explique, les images dans le web moderne sont incroyablement complexes. Au lieu de juste Une combinaison des tailles 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 Le premier emplacement où l'on rencontre des images est Passons à ceci: Maintenant, toutes nos URL d'images auront Responsive Images?
des temps anciens, nous avons maintenant quelque chose de fou comme ceci:
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 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
] home-galleries-lazy-load.html.twig
le modèle partiel qui affiche la liste des galeries de l'écran d'accueil.
public function getImageUrl (Image $ image)
{
return $ this-> router-> generate ('image.serve', [
'id' => $image->getId(),
]RouterInterface :: ABSOLUTE_URL);
}
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);
}
- 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 à:
Ç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:
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:
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.
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
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
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.
Et si nous changeons d'orientation et vérifions le dossier?
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