web-dev-qa-db-fra.com

Chargez d'abord une image d'arrière-plan basse résolution, puis une image haute résolution

J'essaie d'optimiser la taille de mon site lorsqu'il est envoyé au client. Je suis en baisse à 1,9 Mo et 29 Ko lors de la mise en cache. Le problème est que le premier chargement contient une image qui est très non optimisée pour les appareils mobiles; il a une résolution de 1080p.

Je cherche donc une méthode qui me permette de charger une version basse résolution (min.bg.jpg) et une fois le site chargé, utilisez une version haute résolution - ou même une résolution proche de celle de l'appareil. utilisé (NNNxNNN.bg.jpg ou simplement bg.jpg).

L'arrière-plan est défini à l'aide de CSS, comme tout le monde s'y attendait. Son appliqué au corps et la déclaration entière ressemble à ceci:

body {
    background: url("/cdn/theme/images/bg.jpg");
    color: white;
    height: 100%;
    width: 100%;
    background-repeat: no-repeat;
    background-position: 50% 50%;
    background-attachment: fixed;
}

Maintenant, je veux changer cela pour utiliser min.bg.jpg à la place pour le premier chargement, et ensuite quelque chose comme ceci:

jQuery(function(){
    jQuery("body").[...]
});

De quelle manière puis-je télécharger de manière asynchrone le nouvel arrière-plan, puis l'insérer en tant que nouvelle image d'arrière-plan CSS?

Pour montrer quelques différences, voici un exemple de la version principale et de la version mini que j'utilise pour les tests:

[email protected] ~/Work/BIRD3/cdn/theme/images $ file *.jpg
bg.jpg:     JPEG image data, EXIF standard
min.bg.jpg: JPEG image data, JFIF standard 1.01
[email protected] ~/Work/BIRD3/cdn/theme/images $ du -h *.jpg
1,0M    bg.jpg
620K    min.bg.jpg
19
Ingwie Phoenix

Voici la méthode que j'utilise ...

CSS:

#div_whatever {
   position: whatever;
   background-repeat: no-repeat;
   background-position: whatever whatever; 
   background-image: url(dir/image.jpg);
   /* image.jpg is a low-resolution at 30% quality. */
}

#img_highQuality {
    display: none;
}

HTML:

<img id="img_highQuality" src="dir/image.png">
<!-- img.png is a full-resolution image. -->

<div id="div_whatever"></div>

JQUERY:

$("#img_highQuality").off().on("load", function() {
    $("#div_whatever").css({
        "background-image" : "url(dir/image.png)"
    });
});
// Side note: I usually define CSS arrays because
// I inevitably want to go back and add another 
// property at some point.

Ce qui se produit:

  1. Une version basse résolution de l'arrière-plan se charge rapidement.
  2. Pendant ce temps, la version haute résolution se charge en tant qu’image cachée.
  3. Lorsque l'image haute résolution est chargée, jQuery remplace l'image basse résolution du div par la version haute résolution.

PURE JS VERSION

Cet exemple serait efficace pour changer un à plusieurs éléments.

CSS:

.hidden {
   display: none;
}

#div_whatever {
   position: whatever;
   background-repeat: no-repeat;
   background-position: whatever whatever; 
   background-image: url(dir/image.jpg);
   /* image.jpg is a low-resolution at 30% quality. */
}

HTML:

<div id="div_whatever"></div>
<img id="img_whatever" class="hidden" src="dir/image.png" onload="upgradeImage(this);">

JAVASCRIPT:

function upgradeImage(object) {
    var id = object.id;
    var target = "div_" + id.substring(4);

    document.getElementById(target).style.backgroundImage = "url(" + object.src + ")";
}

MISE À JOUR/AMÉLIORATION (31/01/2017)

Cette amélioration est inspirée par l'excellent point de gdbj selon lequel ma solution a pour résultat que le chemin de l'image est spécifié à trois emplacements. Bien que je n'aie pas utilisé la technique addClass () de gdbj , le code jQuery suivant est modifié pour extraire le chemin de l'image (plutôt qu'il est câblé dans le code jQuery). Plus important encore, cette version permet plusieurs substitutions d’images de basse à haute résolution. 

CSS

.img_highres {
  display: none;
}

#div_whatever1 {
  width: 100px;
  height: 100px;
  background-repeat: no-repeat;
  background-position: center center;
  background-image: url(PATH_TO_LOW_RES_PHOTO_1);
}

#div_whatever2 {
  width: 200px;
  height: 200px;
  background-repeat: no-repeat;
  background-position: center center;
  background-image: url(PATH_TO_LOW_RES_PHOTO_2);
}

HTML

<div id="div_whatever1"></div>
<img id="img_whatever1" class="img_highres" src="PATH_TO_HIGH_RES_PHOTO_1">

<div id="div_whatever2"></div>
<img id="img_whatever2" class="img_highres" src="PATH_TO_HIGH_RES_PHOTO_2">

JQUERY

  $(function() {
      $(".img_highres").off().on("load", function() {
         var id = $(this).attr("id");
         var highres = $(this).attr("src").toString();
         var target = "#div_" + id.substring(4);
         $(target).css("background-image", "url(" + highres + ")");
      });
   });

Qu'est-ce qui se passe:

  1. Les images basse résolution sont chargées pour chaque div en fonction de leurs paramètres CSS Background-image. (Notez que le CSS définit également la div avec les dimensions prévues ) 
  2. Pendant ce temps, les photos de résolution supérieure sont en train d'être chargées en tant qu'images cachées (toutes partagent le même nom de classe, img_highres). 
  3. Une fonction jQuery est déclenchée chaque fois qu'une photo img_highres Termine son chargement. 
  4. La fonction jQuery lit le chemin src de l'image et Change l'image de fond du div correspondant. Dans l'exemple ci-dessus, la convention de dénomination est "div_ [nom]" pour les divs visibles Et "img_ [même nom]" pour les images haute résolution chargées dans l'arrière-plan
14
Alan M.

Essayons une base:

<img border="0" 
     style="background:url(http://i.stack.imgur.com/zWfJ5.jpg) no-repeat; 
            width:1920px;
            height:1200px"
     src="http://i.stack.imgur.com/XOYra.jpg" width="1920" height="1200" />

zWfJ5.jpg est la version basse résolution, et XOYra.jpg est la version haute résolution.

S'il existe un moyen d'organiser le chargement de sorte que l'image d'arrière-plan s'affiche en premier, cela pourrait être le plus simple que je puisse imaginer.

où basse résolution 44k:  low resolution 44k:

et la haute résolution est de 1,16M  high resolution is 1.16M

résultat :

jsFiddled here (Cela nécessite une image plus grande pour la comparaison de chargement.)

23
Milche Patern

Un peu tard, mais vous pouvez utiliser cette solution extrêmement simple: Vous pouvez mettre les deux images dans le fond css:

  background-image: url("high-res.jpg"),url("low-res.jpg");

Le navigateur affichera le poing de l'image basse résolution, puis affichera la haute résolution sur la basse résolution une fois celle-ci chargée.

8
user7956100

Normalement, j'optimiserais l'image à l'aide de Grunt ou d'un outil en ligne tel que Tiny PNG pour réduire la taille du fichier. 

Ensuite, vous pouvez choisir de différer le chargement des images. J'ai trouvé l'article suivant utile pour reporter des images - https://www.feedthebot.com/pagespeed/defer-images.html

L'article décrit l'utilisation d'une image base64 pour le chargement initial, puis diffère le chargement de l'image de haute qualité. L'image balisée mentionnée dans l'article est la suivante ...

<img src="data:image/png;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=" data-src="your-image-here">

Le JavaScript mentionné dans l'article est le suivant ...

<script type="text/javascript" async>
function init() {
    var imgDefer = document.getElementsByTagName('img');
    for (var i=0; i<imgDefer.length; i++) {
        if(imgDefer[i].getAttribute('data-src')) {
            imgDefer[i].setAttribute('src',imgDefer[i].getAttribute('data-src'));
        }
    }
}
window.onload = init;
</script>

J'espère que ça aide.

1
steffcarrington

Sur Ubuntu/Chrome 71, la réponse de Milche ne fonctionne pas systématiquement pour moi et l'image de résolution supérieure (via img src) se charge et se résout avant que l'image de résolution inférieure (via css background) ne commence même à télécharger.

Ma solution consiste à commencer par l'image de la résolution inférieure en tant que src et à utiliser la classe Image class pour créer une instance <img> non attachée avec l'image en résolution élevée. Une fois qu'il est chargé, mettez à jour la source <img> existante avec la source d'image haute résolution.

HTML:

<img id='my-image' src='low-res.png' alt='Title' width='1920px' height='1200px'>

JavaScript:

window.addEventListener('load', function() {
    loadHighResImage(document.getElementById('my-image'), 'high-res.png')
})

function loadHighResImage(elem, highResUrl) {
    let image = new Image()
    image.addEventListener('load', () => elem.src = highResUrl)
    image.src = highResUrl
}

Fiddle: https://jsfiddle.net/25aqmd67/

Cette approche fonctionne pour les images de résolution inférieure qui sont simplement réduites.

0
Vulcan

Toutes les réponses ci-dessus fonctionnent généralement avec un petit ajustement, mais voici la façon dont je pense être simple et rapide. 

Remarque: - Décommentez le code chargez l'image haute résolution avant de l'utiliser, une fonction de veille sert uniquement à simuler un réseau lent . - En fait, cette méthode ne charge pas 2 ressources simultanément (haut et bas), mais elle est acceptable car le chargement de ressources faibles ne prend pas beaucoup de temps . - Copiez tout le code et lancez-vous pour une vérification rapide.

<!DOCTYPE html>
<html>
  <head>
    <script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>

    <style type="text/css">
      
    </style>
  </head>

  <body>
    <!-- Load low res image first -->
    <img style="width: 400px; height: auto;" alt="" src="https://s3-ap-southeast-1.amazonaws.com/wheredat/banner-low-quality/banner_20180725_123048.jpg" onload="upgrade(this)">
  </body>
  
  <script type="text/javascript">

	function upgrade(image){

                // After load low res image, remove onload listener.

		// Remove onload listener.
		$(image).prop("onload", null);

                // Load high resolution image.
                // $(image).attr('src', 'https://s3-ap-southeast-1.amazonaws.com/wheredat/banner/banner_20180725_123048.jpeg');

		// Simulate slow network, after 1.5s, the high res image loads.
		sleep(1500).then(() => {
		    // Do something after the sleep!

			// Load a high resolution image.
			$(image).attr('src', 'https://s3-ap-southeast-1.amazonaws.com/wheredat/banner/banner_20180725_123048.jpeg');
		});	
	}

	// Sleep time expects milliseconds
	function sleep (time) {
	  return new Promise((resolve) => setTimeout(resolve, time));
	}

  </script>
  
</html>

0
Nguyen Tan Dat

Avez-vous essayé d'utiliser des sprites CSS pour vous aider à obtenir ce que vous voulez? http://css-tricks.com/css-sprites/

0
Caleb Hutchinson