如何延迟加载图像并使它们可用于打印

How to lazy load images and make them available for print

一些网站有很多图片,因此 lazyloading 似乎适合减少加载时间和数据消耗。但是,如果您还需要为该网站支持 打印 怎么办?

我的意思是,你可以尝试检测打印事件然后加载图像,像这样:

HTML

<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7">

注意:这是一个逐一像素的 gif 虚拟图像。

JavaScript

window.addEventListener('DOMContentLoaded', () => {
  img = document.querySelector('img');
  var isPrinting = window.matchMedia('print');
  isPrinting.addListener((media) => {
    if (media.matches) {
      img.src = 'http://unsplash.it/500/300/?image=705';
    }
  })
});

注意: 如果您在代码操场上尝试此操作,请删除 DOMContentLoaded 事件(或简单地分叉这些:JSFiddle | Codepen)。

注意: 我没有为 obvious reasonsonbeforeprintonafterprint 操心。

如果图像被缓存,这会很好地工作,但话又说回来,不是重点。 image/s 应该全部加载,然后出现在打印屏幕中。

你有什么想法吗?有人成功实现了 打印就绪 延迟加载插件吗?

更新

我已经尝试在检测到打印对话框后重定向用户到标记版本的网站a.k.awebsite.com?print=true,其中延迟加载已停用并且所有图像都正常加载。

通过在该标记的打印就绪版本页面中应用 window.print() 方法改进了此方法,在所有图像加载完成后打开一个新的打印对话框,并显示 "等待它 同时在页面顶部的消息。

重要说明:此方法已在 Chrome 中测试,它在 Firefox 和 Edge 中不起作用(因此这不是答案,而是证明) .

它在 Chrome 中有效,因为当您重定向到另一个网站时打印对话框关闭(在本例中相同 url 但已标记)。在 Edge 和 Firefox 中,打印对话框是一个实际的 window 并且它不会关闭它,因此非常不可用。

根据你想要的功能,我不太确定你想要做什么是可行的。作为开发人员,我们实际上无法控制用户的浏览器。以下是我对为什么这不完全可行的看法。

  1. 挂钩事件并加载丢失的图像并不能保证图像会从服务器进入您的页面。更具体地说,为打印预览生成的 PDF 将在图像加载完成之前生成,img.src = "..." 是异步的。不幸的是,您 运行 也会遇到与 onbeforeprint 类似的问题。有时有效,有时无效(例如,您的 fiddle 在 safari 中测试时有效,但在 Chrome 中无效)

  2. 您不能 stall or stop 打印调用 -- 您不能强制浏览器在延迟加载上下文中等待您的图像完成加载。 (我曾经读过一些关于使用警报来实现这一点的内容,但对我来说这似乎真的很老套,它比拖延更能阻止打印)

  3. 您不能强制 img.src 在延迟加载上下文中同步获取该数据。有一些方法可以做到这一点,但它们是 clever hacks -- referenced as pure evil and may not work in always. I found another link with a similar approach

所以我们有一个问题,如果在打印事件被触发时图像没有加载,我们不能强制浏览器等到它们完成。当然,我们可以直接打印这些图像,但正如上面所指出的,我们不能在打印预览弹出之前等待这些资源加载。

可能的解决方案(灵感来自第三点中的 link 以及 this link

您几乎可以通过同步 XMLHttpRequest 逃脱。同步 XMLHTTPRequests 不会让您更改响应类型,它们始终是字符串。但是,您可以将字符串值转换为 arrayBuffer,将其编码为 base-64 编码的字符串,并将 src 设置为 dataURL(请参阅引用巧妙技巧的 link)——但是,当我尝试这样做时,我在 the jsfiddle 中出现错误——因此,如果配置正确,理论上是有可能的。我犹豫是否可以,因为我无法让 fiddle 使用以下内容(但这是您可以探索的路线!)。

var xhr = new XMLHttpRequest();
xhr.open("GET","http://unsplash.it/500/300/?image=705",false);
xhr.send(null);
if (request.status === 200) {
    //we cannot change the resposne type in synchronous XMLHTTPRequests
    //we can convert the string into a dataURL though
    var arr = new Uint8Array(this.response);
    // Convert the int array to a binary string
    // We have to use apply() as we are converting an *array*
    // and String.fromCharCode() takes one or more single values, not
    // an array.
    var raw = String.fromCharCode.apply(null,arr);

    // This is supported in modern browsers
    var b64=btoa(raw);
    var dataURL="data:image/jpeg;base64,"+b64;
    img.src = dataURL;
}

改善用户体验

你可以做的是让一些文本只显示在页面的打印版本中(通过 @print css 媒体),上面写着 "images are still loading, cancel your print request and try again" 并且当图像完成时加载,从 DOM 中删除 "still waiting on resources try again message"。此外,您可以将主要内容包装在一个元素中,该元素在未加载内容时将显示反转为 none,因此您所看到的只是打印预览对话框中的消息。

脱离您发布的代码可能如下所示(请参阅更新后的 jsfiddle):

CSS

.printing-not-ready-message{
  display:none;
}
@media print{
  .printing-not-ready-message{
    display:block;
  }
  .do-not-print-content{
    display:none;
  }
}

HTML

<div class="printing-not-ready-message">
Images are still loading please cancel your preview and try again shortly.
</div>
<div class="do-not-print-content">
    <h1>Welcome to my Lazy Page</h1>
    <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7">
    <p>Insert some comment about picture</p>
</div>

JavaScript

window.addEventListener('DOMContentLoaded', () => {
  img = document.querySelector('img');
  var isPrinting = window.matchMedia('print');
  isPrinting.addListener((media) => {
    if (media.matches) {
      img.src = 'http://unsplash.it/500/300/?image=705';
      //depending on how the lazy loading is done, the following might
      //exist in some other call, should happen after all images are loaded.
      //There is only 1 image in this example so this code can be called here.
      img.onload = ()=>{
          document.querySelector(".printing-not-ready-message").remove();
          document.querySelector(".do-not-print-content").className=""
      }
    }
  })
});

我写了一个延迟加载 jquery 插件,支持使用 window.onbeforeprint 事件和 mediaQueryListeners 在打印时显示图像。 https://github.com/msigley/Unveil-EX/

我是 vanilla-lazyload 脚本的作者,我最近开发了一项功能,可以打印所有图像!

使用 this repo code which is live here.

测试跨浏览器

看一看,让我知道您的想法! 当然,我愿意在 GitHub 上提出请求。

对于和我处在同一条船上的人:当使用浏览器本机 loading="lazy" 时,您可以在打印即将发生时简单地删除该属性。下面是我的 jQuery 实现。

window.onbeforeprint = function () {
  $('img').each(function () {
    $(this).removeAttr('loading')
  });
}

Chrome 然后将只加载所有图像,它们将在打印时显示。

//declare custom onbeforeprint method
const customOnBeforePrint = () => {
    const smoothScroll = (h) => {
        let i = h || 0;
        if (i < 200) {
            setTimeout(() => {
                window.scrollTo(window.scrollY, window.scrollY + i);
                smoothScroll(i + 10);
            }, 10);
        }
    };

    let height = document.body.scrollHeight;
    let newHeight;
    while (true) {
        smoothScroll(100);

        if (newHeight === height) break;
        height = newHeight;
    }
};

//override the onbeforeprint method
window.onbeforeprint = customOnBeforePrint;

将该块复制粘贴到 devtools 控制台,然后尝试单击打印按钮。该解决方法对我有用。