Speed up image loading time

I did not have access to your images because they are hidden behind JavaScript so I used Linux desktop backgrounds which are over one meg each. The image sizes are shown for every image.
Your images can be substituted in the $imgs array.

Here is the AmpProject.org version


> [**Tools.Pingdom.com**](https://tools.pingdom.com/#!/dTOyif/https://www.amppagemaker.com/assets/sp-coding-noobie/test-003.php) - 405.ms to load about 50 images
> and not forgetting the [**FREE GOOGLE CACHED Version**] (https://cdn.ampproject.org/c/s/www.amppagemaker.com/assets/sp-coding-noobie/test-003.php)
### Source Code: ``` <?php declare(strict_types=1); error_reporting(-1); ini_set('display_errors', '1');

$imgs =
[
‘assets/imgs/happier-than-a-corgi-on-stilts.jpg’,
‘assets/imgs/jokes/blogging-20-says-i-can-get-mre-post-than-you.png’,
‘assets/imgs/jokes/browsing-for-funny-stuff.png’,
‘assets/imgs/jokes/captain-kirk-error-message.jpg’,
‘assets/imgs/jokes/coffee-helps-you-do-stupid-things-faster.jpg’,
‘assets/imgs/jokes/congratulations-dash.png’,
‘assets/linux-17/A_star_has_fallen_by_Juan_Pablo_Lauriente.jpg’,
‘assets/linux-17/Alien_wing_by_Ralph_Crutzen.jpg’,
‘assets/linux-17/Candy_by_Bernhard_Hanakam.jpg’,
‘assets/linux-17/Classic_Guitar_Detail_by_Sten_Jorgen_Pettersen.jpg’,
‘assets/linux-17/DSC3907_by_Todor_Velichkov.jpg’,
‘assets/linux-17/Espaciolandia_by_Nando_uy.jpg’,
‘assets/linux-17/Forest_by_Moritz_Reisinger.jpg’,
‘assets/linux-17/Haukland_Beach_view_by_Michele_Agostini.jpg’,
‘assets/linux-17/IMG_0081_by_Jobin_Babu.jpg’,
‘assets/linux-17/IMG_7632_by_Jobin_Babu.jpg’,
‘assets/linux-17/Mushrooms_3_by_moritzmhmk.jpg’,
‘assets/linux-17/P1310728_by_Ferran_Reyes.jpg’,
‘assets/linux-17/Passion_by_Vilia_Majere.jpg’,
‘assets/linux-17/Pink_and_Blue_by_Ashwin_Deshpande.jpg’,
‘assets/linux-17/Purple_Daisy_by_Maria_Scotto.jpg’,
‘assets/linux-17/Road_to_Nowhere_by_Matt_Bailey.jpg’,
‘assets/linux-17/Seebrucke_Graal-Muritz_by_Oliver_hb.jpg’,
‘assets/linux-17/Some_Light_Reading_by_Brandilyn_Carpenter.jpg’,
‘assets/linux-17/Wanaka_Tree_by_Stephane_Pakula.jpg’,
‘assets/linux-17/Yak_Wallpaper_Grey_4096x2304.png’,
‘assets/linux-17/Yala_mountain_by_Geza_Radics.jpg’,
‘assets/linux-17/Zapus_Wallpaper_Grey_4096x2304.png’,
‘assets/linux-17/larung_gar_by_night_by_Geza_Radics.jpg’,
‘assets/linux-17/there_is_something_human_in_that_stuff_by_Pierre_Cante.jpg’,
‘assets/linux-17/ubuntu-default-greyscale-wallpaper.png’,
‘assets/linux-17/ubuntu16_10_by_Khoir_Rudin.png’,
‘assets/linux-17/ubuntu_wallpaper_16_10_02_by_screen_name_007.jpg’,
‘assets/linux-17/warty-final-ubuntu.png’,
‘assets/linux-bg/160218-deux-two_by_Pierre_Cante.jpg’,
‘assets/linux-bg/Black_hole_by_Marek_Koteluk.jpg’,
‘assets/linux-bg/Cielo_estrellado_by_Eduardo_Diez_Vihuela.jpg’,
‘assets/linux-bg/Dans_ma_bulle_by_Christophe_Weibel.jpg’,
‘assets/linux-bg/Flora_by_Marek_Koteluk.jpg’,
‘assets/linux-bg/Icy_Grass_by_Raymond_Lavoie.jpg’,
‘assets/linux-bg/Night_lights_by_Alberto_Salvia_Novella.jpg’,
‘assets/linux-bg/Spring_by_Peter_Apas.jpg’,
‘assets/linux-bg/TCP118v1_by_Tiziano_Consonni.jpg’,
‘assets/linux-bg/The_Land_of_Edonias-by-somebody-greek.jpg’,
‘assets/linux-bg/clock_by_Bernhard_Hanakam.jpg’,
‘assets/linux-bg/eric-skwarczynski-233332.jpg’,
‘assets/linux-bg/kai-gradert-233001.jpg’,
‘assets/linux-bg/passion_flower_by_Irene_Gr.jpg’,
‘assets/linux-bg/warty-final-ubuntu.png’,
];

//================================================================
function getImage($img, $layout=‘responsive’, $dims=NULL) // fixed
{

$img ==> ‘assets/linux-bg/160218-deux-two_by_Pierre_Cante.jpg’

$IMG = ‘/var/www/amppagemaker.com/public_html/’ .$img;
$tmp = getimagesize($IMG);
if($dims):
// image specific
else:
$dims = $tmp[‘3’];
endif;
$fSize = number_format( (float) filesize( $IMG ) );
$tmp = <<< ____TMP


<amp-img

src=“https://www.amppagemaker.com/$img

$dims

File size: $fSize bytes

layout=“$layout”

alt=“an image”></amp-img>

<amp-img
src=“https://www.amppagemaker.com/$img
$dims
layout=“$layout”
alt=“an image”>





____TMP;

return $tmp;
}///

require ‘test-amp-head.php’;
?>

Menu

<a  class="btn tdn fwb" 
  href="https://cdn.ampproject.org/c/www.amppagemaker.com/assets/sp-coding-noobie/test-003.php" 
  title="https://cdn.ampproject.org/c/www.amppagemaker.com/assets/sp-coding-noobie/test-003.php"> 
GOOGLE &nbsp; <b class="fsl clc">⚡</b> &nbsp; Version 


 start: AMP-img

AMP-IMG


Small images above the fold


<?php 
  foreach( $imgs as $id => $img):
    echo getImage($img, $layout='responsive', $dims=NULL);
  endforeach;  
?>    
end: AMP-img


¯\_(ツ)_/¯

Wonderful place for a footer GMF  Design: John_Betong 3.1.5
```

The include file: :test-amp-head.php

<?php 
  //  <!-- v-components.php -->

?><!DOCTYPE HTML>
<html ⚡ lang="en">
<head>
  <meta http-equiv='content-type' content='text/html; charset=utf-8'>
  <meta charset='utf-8'>
  <meta name='viewport' content='width=device-width,minimum-scale=1,initial-scale=1'>

  <style amp-boilerplate>
    body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}
  </style>
  <noscript>
    <style amp-boilerplate>
      body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}
    </style>
  </noscript>

<!-- CAN MODIFY SCHEMA CONTENTS BELOW -->
  <?php 
    // 
  ?>
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "NewsArticle",
  "mainEntityOfPage":{
    "@type":"WebPage",
    "@id":"/<?= $_SERVER['SERVER_NAME'];?>"
  },
  "headline": "Amp Page Maker",
  "image": {
    "@type": "ImageObject",
    "url":   "https://supiet2.tk/assets/imgs/jb-photo-696x1239.jpg",
    "height": 696,
    "width": 1239
  },
  "datePublished": "2017-03-07T20:56:27+07:00",
  "dateModified":  "<?php echo date('Y-m-d') .'T' .date('H:i:s') .'+07:00'; ?>",
  "author": {
    "@type": "Person",
    "name": "John_Betong"
  },
   "publisher": {
    "@type": "Organization",
    "name":  "Amp Page Maker",
    "logo": {
      "@type": "ImageObject",
      "url": "https://www.johns-jokes.com/afiles/images/logo-180x60.png",
      "width": 180,
      "height": 60
    }
  },
  "description": "The definitive AMP Page Maker"
}
</script>

<!-- CAN MODFY FOLLOWING LINKS and TITLE -->
  <link rel="icon" href="https://www.amppagemaker.com/amp_favicon.png" type="image/x-icon"/>
  <link rel="shortcut icon" href="https://www.amppagemaker.com/amp_favicon.png">
  <link rel="shortcut icon" href="https://www.amppagemaker.com/amp_favicon.png" type="image/x-icon"/>
  <link rel='canonical' href='https://www.amppagemaker.com/comps-create/10'>
  <link rel='amphtml'   href='https://www.amppagemaker.com/comps-create/10'>
  <title> Comps-create </title>

<!-- SCRIPTS GENERATED DEPENDING ON BODY CONTENTS -->
  <script async src="https://cdn.ampproject.org/v0.js"></script>
  <link rel="yandex-tableau-widget" href="https://ampbyexample.com/favicons/yandex-browser-manifest.json"> 
  
<!-- EMBEDDED CSS STYLE SHEET -->
  <style amp-custom> 
    <?php require 'test-style-000.css'; ?>
 </style>

I just checked your link again (is it still up to date?) and it seems to be working, although just for every 2nd image… :-$ seems we do have a bug here.

Now the problem is that calling .splice() shortens the original array, while the .forEach() index keeps incrementing as usual; so when there are multiple images entering the viewport simultaneously, this results in skipping the checks for every other image. To solve this, we can create a new .filter()ed imgArray instead of modifying the existing one – which is actually much clearer to read anyway:

const images = document.querySelectorAll('[data-lazy-srcset]')
let imgArray = Array.from(images)

const checkImage = image => {
  // Check if the image entered the vieport
  if (image.getBoundingClientRect().top < window.innerHeight) {
    // Set the actual source of the image
    image.srcset = image.dataset.lazySrcset
    // Image got updated, no need to check it any more
    return false
  }

  // Otherwise keep it in the list
  return true
}

const scrollHandler = () => {
  // On each scroll event, iterate over the images and check 
  // their position; thereby filter the image array to those
  // images which haven't been updated yet
  imgArray = imgArray.filter(checkImage)

  if (!imgArray.length) {
    // When there are no images left to lazy-load,
    // listening to the scroll event is no longer
    // required and we can free the resource
    window.removeEventListener('scroll', scrollHandler)
  }
}

// Bind the scroll handler
window.addEventListener('scroll', scrollHandler)
// Also check the images initially
imgArray = imgArray.filter(checkImage)

BTW this is a prime example why one should keep side effects to a minimum. ;-)

4 Likes

You can use GTmatrics to evaluate your website. This site will privide you all the information about Page speed, YSlow speed. By evaluating all the information given about your site you can try to improve the site by doing the suggestions made by GTmetrix. By using CDN you can improve the Page speed and can compress the images. even if the image is not compressed you have to optimize it manually.

Thank you that is brilliant. Can I ask one question though? Is it possible to change it so that if an image is partly showing it’s loaded? On my screen the 5th row is partially showing but the images aren’t until I start to scroll?

There are A LOT of methods concerned with image performance.

I wanted to write more in depth about it, but there is a lot more to it than lazy loading.

These are the few methods I have used and can think of right now. More info will follow.

Image performance methods

Actually load faster
Can be achieved with fewer network requests and smaller file size. There are image optimization tools online to reduce filesize. Like https://tinypng.com/. But there is a lot more to performance than making the files smaller.
Perceived load time
Google uses gradients of the dominant color. There is a blogpost about that here:
https://manu.ninja/dominant-colors-for-lazy-loading-images
https://csswizardry.com/2016/10/improving-perceived-performance-with-multiple-background-images/
Lazy load
Either use the new Intersection observer: https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API or Waypoints http://imakewebthings.com/waypoints/ to find out if the image has to be loaded.
You can use data-src and switch to real src as soon as the How are requests handled? Find it out here:
jakearchibald.github.io/request-quest/
Thumbnails
Telegram uses blurred thumbnails with a loading indicator This works beautifully for message clients, for websites I would load a blurred thumbnail and then replace with the loaded image.


To automate this, this depends a lot on your back-end stack.
If you are looking for a back-end solution, ProcessWire offers tools to generate images with any size you demand. This is part of the content management framework, not sure if you can get this as standalone tool.
https://processwire.com/api/fieldtypes/images/
Caching
Depending on your webserver, you can define caching timelife for your assets.
http://www.askapache.com/htaccess/apache-speed-cache-control/

Can you supply image links because I would like to compare the lazy-loading technique against the AmpProject option.

Actually, that’s already the case… they probably just are not in the viewport by the time of the initial check due to the slide-animation. To fix this, you might delay the initial check for the time of the animation duration using setTimeout(), or load the images when they are coming near the viewport by certain amount of pixels (for example by image.clientHeight).

I think I’d prefer to load the images when they’re 10px into the viewpoint but I’m not sure how to do that - sorry my knowledge pf Javascript is very limited. I tried to do it but then none of the images loaded until started to scroll :worried:

Well I can’t help you debug if you don’t post your code. ;-)

Sorry I didn’t think about that. This is what I changed it to:

<script>
const images = document.querySelectorAll('[data-lazy-srcset]')
let imgArray = Array.from(images)

const checkImage = image => {
  // Check if the image entered the vieport
  if (image.getBoundingClientRect().top < image.clientHeight) {
    // Set the actual source of the image
    image.srcset = image.dataset.lazySrcset
    // Image got updated, no need to check it any more
    return false
  }

  // Otherwise keep it in the list
  return true
}

const scrollHandler = () => {
  // On each scroll event, iterate over the images and check 
  // their position; thereby filter the image array to those
  // images which haven't been updated yet
  imgArray = imgArray.filter(checkImage)

  if (!imgArray.length) {
    // When there are no images left to lazy-load,
    // listening to the scroll event is no longer
    // required and we can free the resource
    window.removeEventListener('scroll', scrollHandler)
  }
}

// Bind the scroll handler
window.addEventListener('scroll', scrollHandler)
// Also check the images initially
imgArray = imgArray.filter(checkImage)
</script>

I didn’t get any different errors, the only error I got (which I had before) was:
TypeError: undefined is not an object (evaluating ‘e[n].call’)
but I’m not sure if that is to do with the lazy load.

Here you’re checking of the top of the image (relative to the viewport) is less than the height of the image; so you’d have to scroll quite bit to make it load. If you want to load it before it enters the viewport, you’ll have to add to window.innerHeight (which is the height of the window, as you might guess). :-)

Another, maybe more convenient option would the use of an intersection observer as @MartinMuzatko suggests. It’s not supported by IE, but then again, neither is the ES6 syntax I’ve been using. ^^

BTW, as you say your JS is not so fluent we can also stick to the “traditional” syntax, if you prefer… most resources you’ll find on the web will use regular functions and var declarations, and you’ll be on the safe side regarding x-browser compatibility. Maybe I should have thought of mentioning that earlier.

No, that would be something different.

Thanks, I changed it to

if (image.getBoundingClientRect().top < window.innerHeight < image.clientHeight)

and now it loads even if they’re only partly showing.

One thing that did concern me though was you said that it doesn’t work in IE - do you mean every version of IE of just older versions like IE8/9?

I’m afraid so. :-/ This should be IE-compliant:

var images = document.querySelectorAll('[data-lazy-src]')
var imgArray = Array.prototype.slice.call(images)

var checkImage = function (image) {
  if (image.getBoundingClientRect().top < window.innerHeight + image.clientHeight) {
    image.src = image.dataset.lazySrc
    return false
  }

  return true
}

var scrollHandler = function () {
  imgArray = imgArray.filter(checkImage)

  if (!imgArray.length) {
    window.removeEventListener('scroll', scrollHandler)
  }
}

window.addEventListener('scroll', scrollHandler)
imgArray = imgArray.filter(checkImage)

For a bit of background, back in 2015 a couple of new features were introduced to the ES standard. These included some new syntax such as arrow functions and const and let to basically replace var, as well as some new API methods such as Array.from(). As usual, MS didn’t bother to keep all their browsers up to date and now focuses on Edge only.

Thank you that’s perfect! :slight_smile:

Typical about Microsoft though!

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.