Issue
I have this responsive image :
<img
class="post-card-image"
srcset="/content/images/size/w300/2021/04/photo-1459411552884-841db9b3cc2a.jpeg 300w,
/content/images/size/w600/2021/04/photo-1459411552884-841db9b3cc2a.jpeg 600w,
/content/images/size/w1000/2021/04/photo-1459411552884-841db9b3cc2a.jpeg 1000w,
/content/images/size/w2000/2021/04/photo-1459411552884-841db9b3cc2a.jpeg 2000w"
sizes="(min-width: 1200px) 600px, (min-width: 768px) 50vw, 100vw"
src="/content/images/size/w600/2021/04/photo-1459411552884-841db9b3cc2a.jpeg"
alt="Publishing options"
loading="lazy" />
When simulating an iPhone X (375x812 DPR:3) on firefox, I can see that the 2000w image was loaded by checking img.currentSrc
.
My problem is when I check img.naturalWith
, this gives me the value 375
which is exactly 100vw
. I was expecting this to return the image's original width 2000
or maybe 2000/3 = 666
.
My question is why does img.naturalWith
returns the value 375
in this particular case ?
Edit
Here is a demo of the problem.
const img1 = document.getElementById("img-with-srcset");
const img2 = document.getElementById("img-no-srcset");
window.addEventListener('load', (event) => {
console.log('page is fully loaded');
console.log("img1.naturalWidth : ", img1.naturalWidth);
console.log("img1.currentSrc : ", img1.currentSrc);
document.getElementById('meta-1').innerHTML = `Natural Width: ${img1.naturalWidth}<br/>Current src : ${img1.currentSrc}`;
console.log("img2.naturalWidth : ", img2.naturalWidth);
console.log("img2.currentSrc : ",img2.currentSrc);
document.getElementById('meta-2').innerHTML = `Natural Width: ${img2.naturalWidth}<br/>Current src : ${img2.currentSrc}`;
});
.grid {
display: grid;
grid-template-columns: 1fr;
max-width: 1200px;
margin: 0 auto;
}
@media (min-width: 768px) {
.grid {
grid-template-columns: 1fr 1fr;
}
}
.card-image {
position: relative;
width: 100%;
height: 300px;
}
.card-image img {
width: 100%;
height: 100%;
object-fit: cover;
}
<div class="grid">
<div class="card">
<div class="card-image">
<img id="img-with-srcset"
srcset="https://images.unsplash.com/photo-1509587584298-0f3b3a3a1797?w=400 400w,
https://images.unsplash.com/photo-1509587584298-0f3b3a3a1797?w=600 600w,
https://images.unsplash.com/photo-1509587584298-0f3b3a3a1797?w=800 800w,
https://images.unsplash.com/photo-1509587584298-0f3b3a3a1797?w=2000 2000w"
sizes="(min-width: 1200px) 600px, (min-width: 768px) 50vw, 100vw"
src="https://images.unsplash.com/photo-1509587584298-0f3b3a3a1797?w=2000" alt="Writing posts" />
</div>
<div class="card-meta" id="meta-1"></div>
</div>
<div class="card">
<div class="card-image">
<img
id="img-no-srcset"
src="https://images.unsplash.com/photo-1509587584298-0f3b3a3a1797?w=2000"
alt="Writing posts"
/>
</div>
<div class="card-meta" id="meta-2"></div>
</div>
</div>
And a screen shot when simulating an iPhone X (375x812 DPR:3) on firefox :
The first image has srcset
and sizes
attr, the second does not. You can see that the same image (w=2000) was loaded in both cases by checking currentSrc
, but the value of naturalWidth is different. The CSS is the same for both images.
Thank you
Solution
naturalWidth/Height returns the intrinsic density-corrected width/height of the image.
I guess what's the intrinsic dimension is, is pretty clear for a bitmap image like a JPEG.
The hard bit is what does "density corrected" mean.
Internally when an image source is selected it has a current-pixel-density value. This value is determined as follow (specs):
- If the image source is selected from the
src
of the image, it's1
. - If the image source is selected from the
srcset
attribute and it has a pixel-density-descriptor (nx
), this descriptor's value is used. - If the image source is selected from the
srcset
attribute and it has a width-descriptor, then the pixel density represents the width descriptor value divided by the used source size. This source size is one that is defined in thesizes
attribute of the<source>
.
Here we are in the third case.
To make it a bit less dynamic, let's set an unique 50vw
value in the sizes
attribute.
const img1 = document.getElementById("img-with-srcset");
window.addEventListener('load', (event) => {
console.log("img1.naturalWidth : ", img1.naturalWidth);
console.log("img1.currentSrc : ", img1.currentSrc);
const src = img1.currentSrc;
const width_descriptor = src.slice(src.lastIndexOf("w=") + 2);
// in our case the intrinsic width is the same as width_descriptor
const intrinsic_width = width_descriptor;
const source_size = innerWidth * 0.5; // 50vw
const density = width_descriptor / source_size;
const expected_size = intrinsic_width / density;
console.log( "expected : ", expected_size );
});
<img id="img-with-srcset"
srcset="https://images.unsplash.com/photo-1509587584298-0f3b3a3a1797?w=400 400w,
https://images.unsplash.com/photo-1509587584298-0f3b3a3a1797?w=600 600w,
https://images.unsplash.com/photo-1509587584298-0f3b3a3a1797?w=800 800w,
https://images.unsplash.com/photo-1509587584298-0f3b3a3a1797?w=2000 2000w"
sizes="50vw"
src="https://images.unsplash.com/photo-1509587584298-0f3b3a3a1797?w=2000" alt="Writing posts" />
So in this case of not having a pixel-density-descriptor, the naturalWidth/Height values do depend on the selected size
value, which can be itself relative to the viewport's size.
Not knowing what you are willing to do it's hard to go further, but note that there is a proposal to expose the current size of a <source>
going on.
Answered By - Kaiido
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.