单击按钮时预加载然后在页面加载到图片库后添加其他图像

Preloading then adding additional images after page load to an image gallery on button click

我正在构建一个从 JSON 文件填充的图片库。目前一切都按预期工作,但截至目前,初始页面加载后没有预加载内容。我想要发生的是在单击“查看更多”按钮后我将显示一些“正在加载”文本,将预加载一批图像,“正在加载”文本将消失,然后将图像添加到页面加载所有项目后。

这是涉及 JSON 提取请求和页面元素构建的代码部分:


var HTML = '';
var itemsStart = 6; // Starting number of items on page.
var itemsAdd = 9; // Number of items to add to page at a time via button click.
var pItems = document.getElementById('pItems');
var pWrapper = document.getElementById('pItemWrapper');

function addProjects() {
    pItems.insertAdjacentHTML('beforeend', HTML);
    console.log('BUILD PROJECTS');
}

//Load json
fetch('data/projects.json').then(function (response) {
    return response.json();
}).then(function (data){

    //Loop through first set of items to load on page.
    for (var i = 0; i < itemsStart; i++) {
        HTML += '<img src="' + data.projects[i].Image + '" alt=""></img>';
         
        if (i == (itemsStart - 1)) {
            addProjects();
        }
    }

    //Load additional items when clicking 'view more'.
    document.getElementById('view-more').addEventListener('click', function() {
        HTML = '';
        for (var i = itemsStart; i < itemsStart + itemsAdd; i++) {
            if ((i < data.projects.length)) {
                HTML += '<img src="' + data.projects[i].Image + '" alt=""></img>';
            }
            
            if (i == ((itemsStart + itemsAdd) - 1) )   {
                addProjects();
            }
        }
        itemsStart = itemsStart + itemsAdd;
    });

}).catch(function(error) {
    console.error('Something went wrong');
});

我没有使用 jQuery 所以我想坚持使用 vanilla js。我不知道我需要向我的按钮事件侦听器添加什么,我从来没有尝试过在不使用插件的情况下预加载图像,但我觉得我不需要为此加载整个插件一件事,我想了解它是如何工作的。

编辑


我觉得我快到了,但我还是有问题。我做了一些修改,将每个项目都放在自己的容器中,但我没有为每次循环创建一个空容器,而是将每个图像添加到最后一个容器中。我的代码如下所示:

var itemsAdd = 3;

//Load additional items when clicking 'view more'.
document.getElementById('view-more').addEventListener('click', function() {

//The loop will add the next 3 items in the json file per click.
    for (var i = itemsStart; i < itemsStart + itemsAdd; i++) {

        var placeholder = document.createElement('div');
        var src = 'img/portfolio/' + data.projects[i].url;
        placeholder.innerHTML= '<div class="img-container"><a href="projects/'+ data.projects[i].ImageName +'" class="fig-container">' + data.projects[i].Title + '</a></div>';
        var galleryItem = placeholder.firstChild;

        preloadImage(src).then(function (image) {
            galleryItem.append(image);
        });
        
        pItems.append(galleryItem);  
    }
    itemsStart = itemsStart + itemsAdd;
});

我得到的结果是这样的:

这是因为 promise 对 preloadImage 函数的作用吗?


我想你可以使用 css 在加载图像上放置一个带有黑色背景的 <div> ,当图像准备好时用 js 删除它。您可以使用 img.onload = () => {} 函数检测何时加载图像。

或者您可以在其中放置一个带有加载屏幕的 img,并在加载图像后将其替换为实际图像。

通常,您可以通过 document.createElement('img') or the Image() constructor. Both result an in instance of an HTMLImageElement.

创建一个带有 JavaScript 的图像

有了这个,您将拥有一个未连接到 DOM 的图像,因此您或用户都看不到它。您可以使用此元素通过设置图像' src 属性.

在幕后加载图像

然后通过侦听 onload 事件,您可以确定图像何时完成加载。从这里开始,您可以通过将图像添加到 DOM 来继续您的流程,例如,使用动画淡入。

下面的示例以 returns a Promise 的函数形式显示了此过程。只要加载事件被触发,这个承诺就会解决。

const preloadImage = src => 
  new Promise(resolve => {
    const image = new Image();
    const onLoad = () => {
      resolve(image);
    };
    image.addEventListener('load', onLoad, {once: true});
    image.src = src;
  });

使用应该是这样的:

const src = 'http://example.com/my-image.jpg';
preloadImage(src).then(image => {
  // Here the image has been loaded and is available to be appended, or otherwise.
  document.body.append(image);
});

在您的情况下,您将遍历每个图像,在传递图像的 URL 时调用函数,并在完成加载后将图像附加到 DOM。

您可以处理任何动画,例如 CSS 的淡入效果。

现实世界的实施

那么你应该如何在你的项目中实现它呢?您需要从创建图像的地方开始。目前您的图像是作为字符串创建的。但是字符串只是字符串,它们还不是 HTML 元素。

我建议您为每张图片创建一个占位符。此占位符可以直观地指示图像正在加载并充当图像的包装器。立即将此占位符添加到 pItems 元素。

然后通过调用 preloadImagedata.projects 数组中的每个 Image 加载图像。每当加载图像时,将其附加到我们刚刚创建的占位符。您现在应该具有首先添加占位符并且图像开始一张一张显示的效果。

加载更多循环应该应用相同的逻辑。

...
}).then(function (data){
  for (let i = 0; i < itemsStart; i++) {

    // Create a <div class="placeholder"> which should act as a placeholder / wrapper.
    const placeholder = document.createElement('div');
    placeholder.classList.add('placeholder');

    // Create the image based on the Image value.
    // Whenever the image is loaded, add it to the placeholder.
    const src = data.projects[i].Image;
    preloadImage(src).then(image => { 
      placeholder.append(image);
    });

    // Immediately add the placeholder.
    // This line doesn't wait for preloadImage to finish because preloadImage is asynchronous. Look into Promises if that is new to you.
    pItems.append(placeholder); 
  }

  ...
});

从这里您可以控制占位符的外观以及该占位符内的图像应具有的任何动画。