【问题标题】:Preloading then adding additional images after page load to an image gallery on button click页面加载后预加载,然后在按钮单击时将其他图像添加到图像库
【发布时间】:2021-05-11 21:03:10
【问题描述】:

我正在构建一个从 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 函数的作用吗?

【问题讨论】:

    标签: javascript image-gallery


    【解决方案1】:

    通常,您可以通过 document.createElement('img')Image() 构造函数使用 JavaScript 创建图像。两者都会产生一个HTMLImageElement 的实例。

    这样,您将拥有一个未连接到 DOM 的图像,因此您或用户看不到它。您可以通过设置图像的src 属性,使用此元素在幕后加载图像。

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

    下面的示例以返回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 元素。

    然后通过调用preloadImage 为您的data.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); 
      }
    
      ...
    });
    

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

    【讨论】:

    • 嗨,Emiel,感谢您的回复。我正在用我所拥有的方法尝试你的方法,但我遇到了一个问题。使用我的按钮循环,我基本上是在生成 html,然后一旦创建了所有 html,我就会使用 addProjects(); 将它添加到 DOM;功能。我不应该这样做吗?如果元素尚未创建,我不认为我只想将图像添加到任何内容中。
    • 虽然您可以将 HTML 生成为字符串,但在将其添加到 DOM 之前,您将无法与它进行交互。因为此时 HTML 不仅仅是字符串。使用document.createElement() 创建元素将允许您监听事件、添加属性值并使用某些元素具有的方法。我建议您遍历数据,在网格中附加一个
      作为占位符,然后开始预加载图像。每当图像完成加载时,将其附加到之前创建的
      中。我将在答案中添加更多说明。
    • 我添加了另一个示例,其中我修改了您的第一个循环并实现了此技术。
    • 再次感谢埃米尔。我尝试了您推荐的解决方案,并且离我越来越近了!一切看起来都像我希望的那样在构建,除了图像都进入最后一个占位符而不是进入它们各自的占位符。我修改了我原来的问题,以展示我如何尝试应用你的建议。我得到这个结果是因为诺言是如何运作的吗?我对他们很陌生,但希望这能帮助我理解。
    • 啊!你说得对!有人告诉我不要混合使用 ES5 和 ES6,所以我试图保持旧语法。看起来我做不到,所以我想我会尝试开始将所有旧代码转换为新的语法。感谢您的帮助,成功了!
    【解决方案2】:


    我认为您可以使用 css 在加载图像上放置一个黑色背景的&lt;div&gt;,并在图像准备好时使用 js 将其删除。您可以使用img.onload = () =&gt; {} 函数检测图像何时加载。

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

    【讨论】:

      猜你喜欢
      相关资源
      最近更新 更多
      热门标签