最近有一些关于Fabrice Bellard的BPG图像格式(http://bellard.org/bpg/)的喧哗,基于他网站上提供的演示,它提供了比jpeg,webp和其他一些更好的压缩。图像解码在浏览器中使用JS完成,这意味着它可以立即使用,无需等待浏览器采用。总的来说,这似乎是一个好主意,交换一些CPU时间来加快下载速度通常是可行的权衡。
交换图像的used here技术是在window.load上迭代document.images,找到src属性包含以" .bpg"结尾的URL的任何位置。并用画布替换它。
然而,这绝对不是解决问题的唯一方法,我看到这种技术的一些缺点,包括:a)画布与图像没有完全相同的布局规则 - 例如在它上面设置宽度属性意味着在img和画布上有不同的东西,b)至少在Chrome中,对于缩小与画布相比的图像,缩放的方式也是不同的。
理想情况下,更好的解决方案可以满足这些要求:
我们想到的一些相关工具包括数据:和blob:urls。
任何人都有工作代码的例子,这些代码使用这样的"更好的"来加载BPG。技术? (Fabrice在他的例子中使用它的方式并不糟糕,当然方法有权衡,但我认为可能有更好的广泛使用。)
答案 0 :(得分:2)
BPG确实看起来很有希望。如果您想在任何时间从任何来源检测到<img>
元素的添加,您可以使用MutationObservers。
如果您不了解它们,它们是异步的,记录文档中所有DOM突变的子集,并允许回调一次处理这些突变,而不是像DOM事件那样同步。因此,如果您在脚本中创建或更改大量图像的来源,观察者将等待您的脚本完成,然后一次性处理所有新图像(因此回调中的循环)。
以下假设你有一个名为doSomethingToDecode(img)
的函数(对不起,我现在不会帮忙)用img替换src
生成的PNG(最有可能) 。它可以异步执行,这不是问题。此外,只要生成的重新加入不以“.bpg”结尾,您就不需要在交换src
时停止观察图像。
当你在任何地方添加<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级别0 document.images
集合。非常老派,但它正在非常有效和简洁地完成我们想要的,所以为什么不呢?
首先,您使用bpg源解码现有的<img>
,然后观察它们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})
答案 1 :(得分:0)
为图像添加错误事件处理程序。在Chrome 40上,当图像丢失或浏览器不知道如何显示时会触发它:
<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 />
优点:客户端部分没有内存/ CPU使用情况。 缺点:需要在服务器上提供备用图像。
我相信你可以在所有img
标签上声明一个全局错误监听器,这样也可以用于未来的图像。