如何延迟加载图像并使其可用于打印 [英] How to lazy load images and make them available for print

查看:43
本文介绍了如何延迟加载图像并使其可用于打印的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有些网站有很多图片,所以延迟加载似乎适合减少加载时间和数据消耗.但是,如果您还需要支持该网站的打印怎么办?

我的意思是,您可以尝试检测打印事件,然后加载图像,如下所示:

HTML

注意:这是一张一像素的 gif 虚拟图像.

JavaScript

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

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

注意:我没有为 明显原因.

如果图像被缓存,这将正常工作,但同样不是的重点.图像应全部加载,然后出现在打印屏幕中.

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

更新

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

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

重要说明:此方法已在 Chrome 中进行了测试,在 Firefox 和 Edge 中均无效(因此这不是答案,而是证词).

它在 Chrome 中有效,因为当您重定向到另一个网站时,打印对话框会关闭(在这种情况下,网址相同但已标记).在 Edge 和 Firefox 中,打印对话框是一个实际的窗口,它不会关闭它,因此非常无法使用.

解决方案

根据您想要的功能,我不太确定您想要做的是否可行.作为开发人员,我们无法真正控制用户浏览器.以下是我对为什么这不完全可能的想法.

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

  2. 您不能暂停或停止打印调用——您不能强制浏览器等待您的图像在延迟加载上下文中完成加载.(我曾经读过一些关于使用警报来实现这一目标的内容,但对我来说这似乎很hacky,与其说是拖延,不如说是对打印的一种威慑)

  3. 您不能强制 img.src 在延迟加载上下文中同步获取该数据.有一些方法可以做到这一点,但它们是聪明的黑客 - 被称为纯粹的邪恶,可能并不总是有效.我找到了另一个采用类似方法的链接

所以我们有一个问题,如果在触发打印事件时图像没有加载,我们不能强制浏览器等待它们完成.当然,我们可以钩住并去打印这些图像,但如上所述,我们不能在打印预览弹出之前等待这些资源加载.

潜在解决方案(灵感来自第三点中的链接以及此链接)

您几乎可以通过同步 XMLHttpRequest 逃脱.同步 XMLHTTPRequests 不会让您更改 responseType,它们始终是字符串.但是,您可以将字符串值转换为 arrayBuffer 将其编码为 base-64 编码的字符串,并将 src 设置为 dataURL(请参阅引用了聪明黑客的链接)——但是,当我尝试这样做时,我得到了一个错误jsfiddle - 所以理论上,如果配置正确,这是可能的.我很犹豫说是的,你可以,因为我无法让小提琴使用以下方法(但这是一条你可以探索的路线!).

var xhr = new XMLHttpRequest();xhr.open("GET","http://unsplash.it/500/300/?image=705",false);xhr.send(null);if (request.status === 200) {//我们不能改变同步 XMLHTTPRequests 中的 resposne 类型//我们可以将字符串转换为 dataURLvar arr = new Uint8Array(this.response);//将 int 数组转换为二进制字符串//我们必须使用 apply() 来转换 *array*//并且 String.fromCharCode() 接受一个或多个单个值,而不是//数组.var raw = String.fromCharCode.apply(null,arr);//这在现代浏览器中受支持var b64=btoa(raw);var dataURL="data:image/jpeg;base64,"+b64;img.src = dataURL;}

努力提升用户体验

你可以做的是让一些文本只显示在你页面的打印版本中(通过 @print css 媒体),上面写着图像仍在加载,取消你的打印请求,然后再试一次",当图像加载完成后,从 DOM 中删除仍在等待资源再试一次消息".此外,您可以将主要内容包装在一个元素中,该元素在未加载内容时将显示反转为无,因此您在打印预览对话框中看到的只是该消息.

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

CSS

.printing-not-ready-message{显示:无;}@媒体打印{.printing-not-ready-message{显示:块;}.不打印内容{显示:无;}}

HTML

图片仍在加载中,请取消您的预览并稍后重试.

<div class="do-not-print-content"><h1>欢迎来到我的懒人页面</h1><img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP//yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"><p>插入一些关于图片的评论</p>

JavaScript

window.addEventListener('DOMContentLoaded', () => {img = document.querySelector('img');var isPrinting = window.matchMedia('print');isPrinting.addListener((media) => {如果(媒体.匹配){img.src = 'http://unsplash.it/500/300/?image=705';//根据延迟加载的完成方式,以下可能//存在于其他调用中,应该在加载所有图像后发生.//本例中只有1张图片,可以在这里调用这段代码.img.onload = ()=>{document.querySelector(".printing-not-ready-message").remove();document.querySelector(".do-not-print-content").className=""}}})});

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

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

Note: this is a one by one pixels gif dummy image.

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).

Note: I didn't event bother with the onbeforeprint and onafterprint for obvious reasons.

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 a.k.a website.com?print=true where lazyloading is deactivated and all images load normally.

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.

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).

It works in Chrome beacuse the print dialog closes when you redirect to another website (in this case same url but flagged). In Edge and Firefox the print dialog is an actual window and it does not close it, making it pretty unusable.

解决方案

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. You'd run into similar issues with onbeforeprint as well, unfortunately. Sometimes it works, sometimes it does not (example, your fiddle worked when testing in safari, but did not in 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. 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. Syncrhonous XMLHTTPRequests will not let you change the responseType, they are always strings. 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. 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. 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):

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

这篇关于如何延迟加载图像并使其可用于打印的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆