简体   繁体   English

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

[英]How to lazy load images and make them available for print

Some websites have lots of images, so lazyloading seems appropiate to reduce load times and data consumption.有些网站有很多图片,所以延迟加载似乎适合减少加载时间和数据消耗。 But what if you also need to support printing for that website?但是,如果您还需要支持该网站的打印呢?

I mean, you can try to detect the print event and then load the images, with something like this:我的意思是,您可以尝试检测打印事件,然后加载图像,如下所示:

HTML HTML

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

Note: this is a one by one pixels gif dummy image.注意:这是一张一比一像素的 gif 虚拟图像。

JavaScript 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';
    }
  })
});

Note: if you try this in a code playground, remove the DOMContentLoaded event (or simply fork these: JSFiddle | Codepen ).注意:如果您在代码游乐场尝试此操作,请删除DOMContentLoaded事件(或简单地分叉这些: JSFiddle | Codepen )。

Note: I didn't event bother with the onbeforeprint and onafterprint for obvious reasons .注意:出于明显的原因,我没有打扰onbeforeprintonafterprint

This will work fine if the image is cached, but then again that's precisely not the point.如果图像被缓存,这将正常工作,但那又不是重点。 The image/s should all load and then appear in the printing screen.图像应全部加载,然后出现在打印屏幕中。

Do you have any ideas?你有什么想法? Has anyone successfully implemented a print-ready lazyloading plugin?有没有人成功实现了可打印的延迟加载插件?

Update更新

I've tried redirecting the user after the print dialog is detected, to a flagged version of the website aka website.com?print=true where lazyloading is deactivated and all images load normally.在检测到打印对话框后,我尝试将用户重定向到网站的标记版本,即website.com?print=true ,其中延迟加载已停用,所有图像都正常加载。

This method is improved by applying the window.print() method in this flagged print-ready version of the page, opening a new print dialog once all images are finished loading, and showing a "wait for it" message in the meantime at the top of the page.此方法通过在此已标记的可打印页面版本中应用window.print()方法得到改进,在所有图像加载完成后打开一个新的打印对话框,同时显示“等待它”消息页面顶部。

Important note: this method was tested in Chrome, it does not work in Firefox nor Edge (hence why this is not an answer, but a testimony).重要提示:此方法在 Chrome 中进行了测试,它在 Firefox 和 Edge 中都不起作用(因此这不是答案,而是见证)。

It works in Chrome beacuse the print dialog closes when you redirect to another website (in this case same url but flagged).它在 Chrome 中有效,因为当您重定向到另一个网站时打印对话框会关闭(在这种情况下,相同的 url 但已标记)。 In Edge and Firefox the print dialog is an actual window and it does not close it, making it pretty unusable.在 Edge 和 Firefox 中,打印对话框是一个实际的窗口,它不会关闭它,因此非常不可用。

Based on your desired functionality, I'm not quite sure what you want to do is feasible.根据您想要的功能,我不太确定您想要做的事情是否可行。 As a developer we don't really have control over a users browser.作为开发人员,我们并不能真正控制用户的浏览器。 Here are my thoughts as to why this isn't fully possible.以下是我对为什么这不完全可能的想法。

  1. Hooking the event to go and load your missing images won't let you guarantee images will make it from the server into your page.将事件挂钩并加载丢失的图像不会让您保证图像会从服务器进入您的页面。 More specifically, the PDF generated for your print preview is going to get generated before your image(s) is done loading, the img.src = "..." is asynchronous.更具体地说,为您的打印预览生成的 PDF 将在您的图像完成加载之前生成, img.src = "..."是异步的。 You'd run into similar issues with onbeforeprint as well, unfortunately.不幸的是,您也会在onbeforeprint上遇到类似的问题。 Sometimes it works, sometimes it does not (example, your fiddle worked when testing in safari, but did not in Chrome)有时有效,有时无效(例如,您的小提琴在 safari 中测试时有效,但在 Chrome 中无效)

  2. You cannot stall or stop the print call -- you can't force the browser to wait for your image to finish loading in the lazy loading context.你不能停止或停止打印调用——你不能强迫浏览器等待你的图像在延迟加载上下文中完成加载。 (I read something about using alerts to achieve this once, but it seemed really hacky to me, was more of a deterrent to printing than stalling) (我曾经读过一些关于使用警报来实现这一点的东西,但它对我来说似乎真的很老套,更多的是对打印的威慑而不是拖延)

  3. You cannot force img.src to get that data synchronously in a lazy-load context.您不能强制img.src在延迟加载上下文中同步获取该数据。 There are some methods of doing this, but they are clever hacks -- referenced as pure evil and may not work in always.有一些方法可以做到这一点,但它们是聪明的黑客——被称为纯粹的邪恶,可能并不总是有效。 I found another link with a similar approach我找到了另一个类似方法的链接

So we have a problem, if the images are not loaded by the time print event is fired, we cannot force the browser to wait until they are done.所以我们有一个问题,如果图像在打印事件触发时没有加载,我们不能强制浏览器等待它们完成。 Sure, we can hook and go get those images on print, but as above points out, we cannot wait for those resources to load before the print preview pops up.当然,我们可以挂钩并在打印时获取这些图像,但如上所述,我们不能等待这些资源在打印预览弹出之前加载。

Potential solution (inspired by links in point three as well as this link )潜在的解决方案(灵感来自第三点中的链接以及此链接

You could almost get away with doing a synchronous XMLHttpRequest.您几乎可以通过同步 XMLHttpRequest 侥幸逃脱。 Syncrhonous XMLHTTPRequests will not let you change the responseType, they are always strings.同步 XMLHTTPRequests 不会让您更改 responseType,它们始终是字符串。 However, you could convert the string value to an arrayBuffer encode it to a base-64 encoded string, and set the src to a dataURL (see the link that referenced clever hacks) -- however, when I tried this I got an error in the jsfiddle -- so it would be possible, if things were configured correctly, in theory.但是,您可以将字符串值转换为 arrayBuffer 将其编码为 base-64 编码字符串,并将 src 设置为 dataURL(请参阅引用聪明黑客的链接)——但是,当我尝试这个时,我得到了一个错误jsfiddle - 如果事情配置正确,理论上是可能的。 I'm hesitant to say yes you can, since I wasn't able to get the fiddle working with the following (but it's a route you could explore!).我不敢说是的,你可以,因为我无法让小提琴与以下内容一起工作(但这是你可以探索的路线!)。

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;
}

Work around to enhance the user experience解决问题以增强用户体验

Something you could do is have some text that only displays in the print version of your page (via @print css media) that says "images are still loading, cancel your print request and try again" and when the images are finished loading, remove that "still waiting on resources try again message" from the DOM.您可以做的事情是在页面的打印版本中显示一些文本(通过@print css 媒体),上面写着“图像仍在加载,取消您的打印请求并重试”,当图像完成加载时,删除来自 DOM 的“仍在等待资源重试消息”。 Farther, you could wrap your main content inside an element that inverses the display to none when content is not loaded, so all you see is that message in the print preview dialog.此外,您可以将主要内容包装在一个元素中,该元素在未加载内容时将显示反转为无,因此您所看到的只是打印预览对话框中的该消息。

Going off of the code you posted this could look something like the following (see updated jsfiddle ):关闭您发布的代码可能如下所示(请参阅更新的jsfiddle ):

CSS CSS

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

HTML 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 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=""
      }
    }
  })
});

I'm the author of the vanilla-lazyload script and I've recently developed a feature that makes print of all images possible!我是vanilla-lazyload脚本的作者,我最近开发了一个可以打印所有图像的功能!

Tested cross browser using this repo code which is live here .使用这里repo 代码测试跨浏览器。

Take a look and let me know what you think!看看,让我知道你的想法! I'm open to pull requests on GitHub of course.我当然愿意在 GitHub 上拉取请求。

I wrote a lazy loading jquery plugin that supports showing images on print using the window.onbeforeprint events and mediaQueryListeners.我编写了一个延迟加载 jquery 插件,它支持使用 window.onbeforeprint 事件和 mediaQueryListeners 在打印时显示图像。 https://github.com/msigley/Unveil-EX/ https://github.com/msigley/Unveil-EX/

//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;

Copy&Paste that block into devtool's console and then try to click print button.将该块复制并粘贴到 devtool 的控制台中,然后尝试单击打印按钮。 That workaround is working for me.该解决方法对我有用。

For whoever is in the same boat as I was: when using the browser native loading="lazy" , you can simply remove that attribute when printing is going to happen.对于与我在同一条船上的人:当使用浏览器本机loading="lazy"时,您可以在打印时简单地删除该属性。 Below is my jQuery implementation.下面是我的 jQuery 实现。

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

Chrome will then just load all images and they will show up when printing.然后,Chrome 将加载所有图像,并在打印时显示。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM