Open technopagan opened 8 years ago
@technopagan
I have no experience here. But it should work. Could you put a simplified testcase together. In this case, I can also check how to get the job done if there are steps needed in a JS decoder.
@aFarkas i already started digging: it's the BGP polyfill, not LazySizes.
I changed BPG's decoder polyfill "bpgdec8.js" (https://github.com/mirrorer/libbpg/blob/master/html/bpgdec8.js) to work on 'data-src' instead of img src:
My altered version of lines 90+91 of 'bpgdec8.js' in readable form:
window.onload = function() {
var a, d, c, e, f, j, g;
e = document.images;
d = e.length;
f = [];
for (a = 0; a < d; a++){
c = e[a];
if( c.hasAttribute('data-src') ){
j = c.getAttribute('data-src');
".bpg" == j.substr(-4, 4).toLowerCase() && (f[f.length] = c);
}
}
d = f.length;
for (a = 0; a < d; a++) {
c = f[a];
if( c.hasAttribute('data-src') ){
j = c.getAttribute('data-src');
e = document.createElement("canvas");
c.id && (e.id = c.id);
c.className && (e.className = c.className);
if (g = c.getAttribute("width") | 0) e.style.width = g + "px";
if (g = c.getAttribute("height") | 0) e.style.height = g + "px";
c.parentNode.replaceChild(e, c);
g = e.getContext("2d");
c = new BPGDecoder(g);
c.onload = function(a, c) {
function d() {
var a =
e.n;
++a >= f.length && (0 == e.loop_count || e.q < e.loop_count ? (a = 0, e.q++) : a = -1);
0 <= a && (e.n = a, c.putImageData(f[a].img, 0, 0), setTimeout(d, f[a].duration))
}
var e = this,
f = this.frames,
g = f[0].img;
a.width = g.width;
a.height = g.height;
c.putImageData(g, 0, 0);
1 < f.length && (e.n = 0, e.q = 0, setTimeout(d, f[0].duration))
}.bind(c, e, g);
c.load(j)
}
}
};
The thing is that the c.onload function of the polyfill seems to be loading the image all by itself, regardless of lazysizes.
I realize that this is hardly your problem, but any help to get the BGP polyfill working with lazysizes would be greatly appreciated!
@aFarkas The simplified test case can be found here: http://tobias.is/nifty/imgload/lazysizes-bpg.html
You shouldn't mess around with the data-src
in the bpg lib. Instead you should either use a MutationObserver for a src
mutate or use the lazybeforeunveil
event to create your own transform.
The later could look something like this (Code is not tested):
(function(){
'use strict';
var regBgp = /\.bpg$/i;
var swapImage = function(img, src){
var ctx, BPGimg;
var canvas = document.createElement('canvas');
canvas.className = img.className;
img.parentNode.replaceChild(canvas, img);
ctx = canvas.getContext("2d");
BPGimg = new BPGDecoder(ctx);
//
BPGimg.onload = function() {
/* draw the image to the canvas */
canvas.width = this.imageData.width;
canvas.height = this.imageData.height;
ctx.putImageData(this.imageData, 0, 0);
};
BPGimg.load(src);
};
document.addEventListener('lazybeforeunveil', function(e){
var src = e.target.getAttribute(lazySizesConfig.srcAttr) || '';
if(regBgp.test(src)){
e.preventDefault();
swapImage(e.target, src);
}
});
})();
Note: Maybe the bgp lib author can/should provide a convenient method that can be used as swapImage replacement.
@technopagan
Does it actually work for you now? Should I try to re-create an example?
@aFarkas Thanks for checking back with me! That's very kind of you!
Flu is keeping me from fully commiting to this at the moment, but I've grabbed my laptop after your question nontheless to see if I can get it working.
I've updated the demo page at http://tobias.is/nifty/imgload/lazysizes-bpg.html in a way that I thought would work: Unveilhook to load the BPG decoder async with lazysizes, then load & execute the code you posted above. The BPG image itself is also being lazy-loaded.
The browser is now fetching all resources correctly & at the right async'ed time. The BPG image does not render, however. The JS console is empty of errors.
I have to admit that I'm out of my depth atm. JS is not my forté. Can you have another look at http://tobias.is/nifty/imgload/lazysizes-bpg.html to spot what I've missed?
Just checked you demo. It works sometimes, but has async problems in 99% of all cases. The reason at the time of the image transformation the lazybeforeunveil
hook has to be already fully loaded as also the bgp decoder.
If you would add them directly it would work. You could also do something like:
[data-script="customload.js"] {
padding-top: 999px;
}
Here is how you can do it more lazy/async, but more robust:
(function(){
'use strict';
var SCRIPTURL = 'bpgdec8_orginal.js';
var regBgp = /\.bpg$/i;
var getBPGDecoder = (function(){
var loading = false;
var cbs = [];
var call = function(){
while(cbs.length){
cbs.shift()(window.BPGDecoder);
}
};
return function(cb){
if(window.BPGDecoder){
cb(window.BPGDecoder);
return;
}
cbs.push(cb);
if(!loading){
loading = true;
loadJS(SCRIPTURL, call);
}
};
})();
var swapImage = function(img, src){
getBPGDecoder(function(){
var ctx, BPGimg;
if(img && img.parentNode){return;}
var canvas = document.createElement('canvas');
canvas.className = img.className;
img.parentNode.replaceChild(canvas, img);
ctx = canvas.getContext("2d");
BPGimg = new BPGDecoder(ctx);
//
BPGimg.onload = function() {
/* draw the image to the canvas */
canvas.width = this.imageData.width;
canvas.height = this.imageData.height;
ctx.putImageData(this.imageData, 0, 0);
};
BPGimg.load(src);
});
};
document.addEventListener('lazybeforeunveil', function(e){
var src = e.target.getAttribute(lazySizesConfig.srcAttr) || '';
if(regBgp.test(src)){
e.preventDefault();
swapImage(e.target, src);
}
});
})();
Hope this helps and get well soon!
@aFarkas I've worked in your first suggestion (sufficient for my use case) and made good progress. Thank you!
May I ask for your assistance one final time? It's the last bit in my row of tests.
I have now combined lazysizes + picturefill + BPG Decoder, using the picture element in my source: http://tobias.is/nifty/imgload/04-bpg.html
The BPG images are once more not showing. It's not a race condition like the last time. I asssume it's down to the customload JS function, but my JS skill is too weak to confirm it or fix it.
Can you point me into the right direction once more?
(And can I buy you a drink next time I'm in Berlin? ;-) )
@technopagan I know, what 's the problem and I will try to put something together in the next 4-10h.
Sorry, will need to do this tomorrow. No time today.
@aFarkas No worries! You're a great help & I appreciate your expertise very much! Thank you!
@technopagan
Unfortunately your last example isn't online anymore, so I couldn't test it.
(function(){
'use strict';
var SCRIPTURL = 'bpgdec8_orginal.js';
var regBgp = /\.bpg$/i;
var getBPGDecoder = (function(){
var loading = false;
var cbs = [];
var call = function(){
while(cbs.length){
cbs.shift()(window.BPGDecoder);
}
};
return function(cb){
if(window.BPGDecoder){
cb(window.BPGDecoder);
return;
}
cbs.push(cb);
if(!loading){
loading = true;
loadJS(SCRIPTURL, call);
}
};
})();
var swapImage = function(img, src){
getBPGDecoder(function(){
var ctx, BPGimg;
if(img && img.parentNode){return;}
var canvas = document.createElement('canvas');
canvas.className = img.className;
img.parentNode.replaceChild(canvas, img);
ctx = canvas.getContext("2d");
BPGimg = new BPGDecoder(ctx);
//
BPGimg.onload = function() {
/* draw the image to the canvas */
canvas.width = this.imageData.width;
canvas.height = this.imageData.height;
ctx.putImageData(this.imageData, 0, 0);
};
BPGimg.load(src);
});
};
//start: new
var slice = [].slice;
var getCurrentSrc = function(img){
var sources = [img];
var parent = img.parentNode;
if(parent.nodeName.toLowerCase() == 'picture'){
sources = slice.call(parent.querySelectorAll('source, img')).filter(function(source){
var media = source.getAttribute('media');
return (!media || window.matchMedia(media));
});
}
return sources[0] ?
sources[0].getAttribute(lazySizesConfig.srcAttr) || sources[0].getAttribute(lazySizesConfig.srcsetAttr) || '' :
''
;
};
//end: new
document.addEventListener('lazybeforeunveil', function(e){
var src = getCurrentSrc(e.target);
if(regBgp.test(src)){
e.preventDefault();
swapImage(e.target, src);
}
});
})();
The basic addition is getCurrentSrc
, which tries to get the matching src from src
or srcset
. One (big) caveat: It doesn't work with resize and it doesn't work with multiple image candidates inside of one srcset
.
But I assume, you are only testing things.
@aFarkas Thank you! Sorry for the missing test case - I renamed them.
The newest test case is: http://tobias.is/nifty/imgload/0x-bpg.html and incorporates your new code. Unfortunately, it doesn't work: lazy loading is now broken completely & of course without BPG images bing loaded, there's no way to see if the BPG decoder would not work.
Is this because I have several resolutions definded inside the
@aFarkas I've made some progress in debugging this & now BPGs are loaded & rendered. However, as you mentioned, the code currently picks the first candidate inside a
What can I do to make this test honor the functionality of the picture element?
(the current state is at http://tobias.is/nifty/imgload/0x-bpg.html)
@aFarkas I suspect it comes down to detecting the viewport (var viewport = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;), reading the media attribute within each source array and then comparing if the current viewport is bigger or smaller than the media attribute for each of the three resolutions inside the picture element. Then use the correct resolution BPG to be returned from the getCurrentSrc function.
Is this a good approach? I'd greatly appreciate your insights. :)
@technopagan Sorry, I actually wrote something for you 2 days ago, but somehow I missed to press the comment button.
I saw you moved on with your code. I will look into this tomorrow. Currently plenty of work.
Is this a good approach? I'd greatly appreciate your insights. :)
We already have some code for this in place. But I made a mistake. You need to change the following line:
return (!media || window.matchMedia(media));
to:
return (!media || window.matchMedia(media).matches);
As far as my tests show, non-standard image formats such as BGP or FLIF do not get lazy-loaded fully.
The behaviour I see is that the image does in fact get loaded async, but it is ALWAYS loaded, regardless of whether I scroll towards its position inside the test page or not (tested in Firefox 42).
Is this due to LazySizes or is it a compound effect of BGP's / FLIF's JS-based decoder that's necessary to display these images?
Of course, in an ideal world, I'd love for such non-standard image formats to be handled just as smoothly by LazySizes as JPEGs etc are.