18

最近有一些关于 Fabrice Bellard 的 BPG 图像格式 ( http://bellard.org/bpg/ ) 的喧嚣,它(基于他网站上提供的演示)提供比 jpeg、webp 和其他一些更好的压缩。图像解码是在浏览器中用JS完成的,这意味着它可以立即使用,无需等待浏览器采用。总的来说,这似乎是一个好主意,用一些 CPU 时间换取更快的下载速度通常是一个可行的折衷方案。

此处用于交换图像的技术是,在 window.load 上,遍历 document.images,找到 src 属性包含以“.bpg”结尾的 URL 的任何位置,并将其替换为画布。

然而,这绝对不是解决这个问题的唯一方法,我看到了这种技术的一些缺点,其中包括:a) 画布没有与图像完全相同的布局规则 - 例如,在其上设置宽度属性意味着不同的东西在 img 与画布上,b) 似乎至少在 Chrome 中,如何对缩小的图像与画布进行缩放是不同的。

一个更好的解决方案将理想地满足这些要求:

  • 尽量不要在内存中复制不必要的图像数据(也不要不必要地使用比必要更多的 CPU - 与原生图像处理相比,JS 中的解码已经需要很多)
  • 尽可能多地兼容浏览器
  • 使用 <img> 标签而不是 <canvas> (不是必需的,但似乎更好)
  • 提供一种简单的方法,不仅可以处理文档加载时的图像,还可以处理稍后添加到文档中的图像(例如,响应用户活动)
  • 仍然易于使用(bellard.org 上的现有技术当然很容易集成)
  • 编辑:使用网络工作者在不阻塞页面的情况下解码图像也可能是一个好方法。

想到的一些相关工具包括 data: 和 blob: url。

任何人都有使用这种“更好”技术加载 BPG 的工作代码示例?(Fabrice 在他的示例中使用它的方式还不错,当然方法也有取舍,但我认为可能有更好的通用方法。)

4

2 回答 2

2

BPG 看起来确实很有希望。如果您想<img>随时从任何来源检测元素的添加,您可以使用MutationObservers。如果您不了解它们,它们是异步的,在文档中记录所有 DOM 突变的子集,并允许回调立即处理这些突变,而不是像 DOM 事件那样同步处理。因此,如果您在脚本中创建或更改大量图像的来源,观察者将等到您的脚本完成,然后一次性处理所有新图像(因此回调中的循环)。

以下假设您有一个名为doSomethingToDecode(img)(对不起,我现在不打算提供帮助)的函数,它src用(很可能)生成的 PNG 替换 img 的 。它可以异步执行此操作,这不是问题。src此外,只要生成的替换不以“.bpg”结尾,您就不需要在交换图像时停止观察图像。

当您在<img>任何地方添加后代(以及其他元素,但它会忽略这些)时,第一个观察者会做出反应;不幸的是,在一般情况下不可能对其进行太多优化。它必须遍历突变列表,然后是每个突变的新节点列表,因此是那些嵌套for循环。但

imgObs=new MutationObserver(
    function(mutations){
        for(var i=0, m; m=mutations[i]; i++) if(m.addedNodes.length)
            for(var j=0, img; img=m.addedNodes[j]; j++) if(img.localName=="img"){
                if(img.src && /\.bpg$/.test(img.src))
                    doSomethingToDecode(img)
                srcObs.observe(img, srcOptions)
            }
    }
)

当您更改src元素的属性时,其他观察者会做出反应,并且应该只观察<img>元素(为了获得最佳性能,它没有故障保护,以防您让它观察其他元素,所以不要)。

srcObs=new MutationObserver(
    function(mutations){
        for(var i=0, m; m=mutations[i]; i++){
            var img=m.target
            if(img.src && /\.bpg$/.test(img.src))
                doSomethingToDecode(img)
        }
    }
)

还要把它放在手边,每次我们开始观察新图像时都需要它:

var srcOptions={childList:false, attributes:true, attributeFilter:["src"]}

您还可以让观察者对<img>元素的删除做出反应以停止观察它们,并释放任何与解码相关的资源,但希望浏览器至少足够聪明,可以停止观察应该被垃圾收集的元素(未经测试!) .

在加载 HTML 后运行它(不要等待带有图像和 CSS 等的整个页面)。注意:这是使用 DOM 级别 0document.images集合。非常老派,但它正在做我们想要的非常有效和简洁的事情,那为什么不呢?

所以首先你用 bpg 源解码现有<img>的 s,然后观察它们的src变化:

for(var i=0, img; img=document.images[i]; i++){
    if(img.src && /\.bpg$/.test(img.src))
        doSomethingToDecode(img)
    srcObs.observe(img, srcOptions)
}

然后这告诉第一个观察者对整个文档中的内容做出反应<body>;不幸的是,没有tagNameFilter参数可以本地过滤掉非<img>childList 突变。

imgObs.observe(document.body, {subtree:true, childList:true, attributes:false})
于 2015-02-20T00:13:12.150 回答
0

Add an error event handler to the images. On Chrome 40, it is triggered when the image is missing or when the browser does not know how to display it:

<script>
var errors = [];
function fallback(elt) {
    if(errors[elt.src]) return;
    // you should also extract the extension in order to test lena.bpg, lena.png, lena.jpg and not lena.bpg.png, lena.bpg.png.jpg
    errors[elt.src] = true;
    console.error('could not load image, falling back');
    elt.src = elt.src + '.png';
    // stop trying
    elt.onerror= function(){};
}
</script>
<img src="test.html" onerror="fallback(this);" width=300 height=300 />

Pros: no memory/cpu usage on the client part. Cons: need to provide alternate images on the server.

I believe you could declare a global error listener on all img tags so this would work for future images too.

于 2015-02-20T21:45:08.953 回答