Responsive images without layout reflow when they load - html

To have responsive images that scale down proportionally on a small screen, I currently use max-width: 100%; (or a fixed max width). As opposed to specifying a fixed size, this has a terrible downsize in that when the page is loading, the browser allocates no space for the image and can only do that after it starts downloading it. This causes a lot of layout reflows and a bad experience before all images are loaded.
This seems like it could be easily fixed - after all, I just have to tell the browser what the real size is so that it can figure out the aspect ratio and the final size before downloading it. First, I was wondering if that's what the width and height html attributes are for, but I know that that's not their purpose as they can be used to rescale the image and change the final size.
What I really want is something like "srcwidth" and "srcheight" that would tell the browser the actual size of the image file so that it doesn't have to load before knowing the aspect ratio to make use of the max-width styling. But from what I could find, there is no such thing like these attributes. Is there really no way to achieve this?
I tried using the actual width and height html attributes in this way and then overriding it with CSS, but browsers simply don't care about that.

The most common way I've seen this addressed is to put all your images in containers and use the padding-bottom property to "pre-allocate" the height.
<div class="responsive-container">
<img src="image.jpg"/>
</div>
To do this, you need to know the aspect ratio of the image to calculate the padding.
Using the Aspect Ratio to work out the height
For example, for an aspect ratio of 16:9 (e.g. 800 x 450px) the height is 56.25% of the width so that will be the value for the padding.
.responsive-container {
width: 100%;
padding-bottom: 56.25%; /* 9/16 = aspect ratio of image */
position: relative;
height: 0;
overflow: hidden;
}
.responsive-container img {
position: absolute;
top: 0;
left: 0;
width: 100%;
}
Different Aspect Ratios
If your images will have different aspect ratios, but you will still know then ahead of time, you set up different classes for each, e.g.
.responsive-container {
width: 100%;
position: relative;
height: 0;
overflow: hidden;
}
.ratio-16-9{
padding-bottom:56.25%; /* 9/16*100 */
}
.ratio-4-3{
padding-bottom:75%; /* 3/4*100 */
}
.ratio-1-1{
padding-bottom:100%;
}
And to use it in the html:
<div class="responsive-container ratio-16-9">
<img src="image.jpg"/>
</div>
Calculate height dynamically
Finally, if you want the CSS to calculate the height dynamically for each image, you can use CSS calc() to calculate the height like this:
calc((image_height/image_width)*100%)
So your CSS would be:
.responsive-container {
width: 100%;
position: relative;
height: 0;
overflow: hidden;
}
And you use it like this:
<div class="responsive-container" style="padding-bottom: calc((426/640)*100%);" >
<img src="image.jpg"/>
</div>
References
Responsive images – how to prevent reflow
Responsive Images Without Browser Reflow
MDN Web Docs for CSS calc()

Related

Preventing page reflow due to image loading, while also imposing a max-width on the said images (HTML/CSS only)

I want to prevent page-reflow, caused by image loading on a web page.
Page reflow occurs when images load after the page's text content has already rendered. There's a 'jerk' caused by the said page-reflow. It makes for awful user experience.
My requirements are:
(i) All images be fully responsive
(ii) Have a max-width of 450px (while maintaining aspect-ratio)
(iii) Be center-aligned within their containers
There can be several images on the page. All have different aspect ratios (but scaled to the same width - i.e. 450px). I know their dimensions beforehand.
Currently my code is simply:
.container {
text-align:center;
overflow:hidden;
background:whitesmoke;
border-top:1px solid #F0F0F0;
border-bottom:1px solid #F0F0F0;
}
.container img {
width:100%;
max-width:450px;
vertical-align: top;
}
<div class="container">
<img src="https://s3.ap-southeast-1.amazonaws.com/damadam-2019/public/31a1b420-59c9-405a-a197-e04dd1e2eaf9.jpg" alt="image">
</div>
This fulfils all my requirements - except it can't prevent page reflow. How do I tweak this to get my desired result?
Traditional solutions to prevent such page-reflow go something like this:
HTML
<div class="container">
<img src="https://s3.ap-southeast-1.amazonaws.com/damadam-2019/public/31a1b420-59c9-405a-a197-e04dd1e2eaf9.jpg" alt="image">
</div>
CSS
.container {
display: block;
position: relative;
padding-bottom: calc(100%/(450/562));/* example width=450px height=562px*/
height: 0;
}
.container img {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
This works fine. But it doesn't impose a max-width like I need it to. The image fills the entire container - as large as that container is (e.g. the full width of the screen on a laptop).
To tweak it, I tried adding max-width:450px;max-height:562px in .container img. That corrected the image's dimensions. But it gave the container extra padding at the bottom:
That's a shame. What I really wanted was for it to look like below:
Note that the gray colouration above is the background container, which simply disappears on smaller resolutions:
What's the best way for me to achieve my requirements? An illustrative example would be great.
Note: adding max-width: 450px;max-height: 561px; in .container doesn't solve the problem either.

Relative positioning on a full-width image

I have a background image which is naturally 1500x1062 with a 100vw width and an auto height.
Now, I need to place an image on top that needs to be placed exactly 306px from the top and 852px from the left of the original image. But, the image needs to scale with the changing aspect-ratio since the other image is a 100% of the window width.
My head hurts and I'm not sure how to accomplish this.
Have you tried converting the px to vw / vh of the second image? With one element in px and the other in view port will not work out well with changing aspect ratios.
I think the key is to set the position top and the width of the element based on the width of the browser, because it's the width of the browser that decides the width of the image, and since the aspect ratio doesn't change, it will also decide the height of the image:
.background {
width: 100vw;
background-image: url('http://digital-photography-school.com/wp-content/uploads/flickr/5661878892_15fba42846_o.jpg');
background-size: 100vw;
height: 800px;
position: relative;
}
.inner {
position: absolute;
left: 25vw;
top: 40vw;
width: 1vw;
height: 1vw;
background: red;
}
<div class="background">
<div class="inner">
</div>
</div>
fiddle for easy resizing: https://jsfiddle.net/pdn2trju/1/
306 out of 1500 is 20.4%. Try assigning left:20.4% to the smaller image. Also u did not say what is the original width of the smaller image, just assign it the relative percentage for its width

Fill a div with an image respecting aspect ratio

Is it possible to fill a div with an image such that at least one image dimension is 100% and the other dimension is either wider or equal size as the div, whilst respecting the image's aspect ratio.
An example could use the classes wide and tall like this:
<div class="tall">
<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/a/ae/Klaproos.jpg/266px-Klaproos.jpg"/>
</div>
<div class="wide">
<img src="https://groenevrijdag.files.wordpress.com/2013/11/klaproos2.jpg"/>
</div>
div {
width: 400px; height: 400px;
display: block;
overflow: hidden;
border-radius: 50%;
display: inline-block;
}
div.tall img { width: 100%; margin-top: -50%; }
div.wide img { height: 100%; margin-left: -50%; }
https://jsfiddle.net/7tuod6vu/
I'm looking for a pure HTML+CSS solution which works for responsive rectangular (not necessarily square) divs. For this particular reason, Javascript would be a pain as one would need to determine whether the width or height should be 100% on every resize. Server side wouldn't even be an option.
Does a pure HTML+CSS solution exist for this?
EDIT Should have been clear about this from the beginning, sorry about that :( I'm not looking for the background-image solution, since it does not allow base64-inhtml representation of images. Moreover, I think background-image's are semantically different from <img>s.
Consider using the CSS object-fit property.
5.5. Sizing Objects: the object-fit
property
The object-fit property specifies how the contents of a replaced
element should be fitted to the box established by its used height and
width.
Here are two of the values:
cover
The replaced content is sized to maintain its aspect ratio while
filling the element's entire content box.
contain
The replaced content is sized to maintain its aspect ratio while
fitting within the element's content box.
So, with cover the image retains its aspect ratio and covers all available space. Of course, this means that much of an image may be cropped off-screen.
With contain the aspect ratio is also maintained, but the image scales to fit within the box. This means that an image may have whitespace on the left and right, or top and bottom.
Browser Compatibility
As of this writing, object-fit is not supported by Internet Explorer. For a workaround see:
Neat trick for CSS object-fit fallback on Edge (and other browsers)
fitie - An object-fit polyfill for Internet Explorer
object-fit-images - Adds support for object-fit on IE9, IE10, IE11, Edge and other old browsers
Polyfill (mostly IE) for CSS object-fit property to fill-in/fit-in images into containers.
More information
MDN object-fit property
CSS-Tricks object-fit property
object-fit browser support # caniuse.com
Here is the solution without using background images and with HTML and CSS only: http://codepen.io/anon/pen/JGGObQ
(change overflow to visible in the .container1 rule to see the full pictures. The numbers in them are their original size in pixels.)
It uses position: absolute on the images, and depending on the format (two classes, as suggested by yourself) a top or left of 50% that moves the position reference into the (horizontal or vertical) center, and a transform : translate setting that moves the position reference point of the image back from that center by 50% of their own size, which results in centering:
.container1 {
position: relative;
float: left;
margin-right: 50px;
width: 400px;
height: 400px;
border-radius: 50%;
overflow: hidden;
border: 1px solid red;
}
img.landscape {
position: absolute;
width: auto;
height: 100%;
transform: translate(-50%, 0);
left: 50%;
}
img.portrait {
position: absolute;
width: 100%;
height: auto;
transform: translate(0, -50%);
top: 50%;
}
<div class="container1">
<img src="http://placehold.it/750x500/09d/fff" class="landscape">
</div>
<div class="container1">
<img src="http://placehold.it/600x900/0d9/fff" class="portrait">
</div>
This is not the exact solution, but it could be an implementation that you could try to make your code work. Here is an example:
As you can't predict the aspect ratio of the image here is what I would do:
HTML:
Set the div tag to 'fill':
<div class="fill"></div>
CSS:
This will place your image as the background, and stretch it to fit the div size without distortion.
.fill {
overflow: hidden;
background-size: cover;
background-position: center;
background-image:"path/to/image.jpg";
}
You could set the images as the div's backgrounds instead and use backkground-size:cover
https://jsfiddle.net/3x5x0v24/

Using CSS only to remove the black bars from YouTube HQdefault image?

(Did some search but couldn't find the exact same question/answer)
I am displaying the YouTube's hqdefault thumbnails on my page. However, I noticed they are 480 by 360, which means they have black top and bottom bars for all 16:9 ratio videos (which are the majority)
Example is: http://img.youtube.com/vi/dA6Jsr7MWw4/hqdefault.jpg
My question is:
I want the image to auto scale to fit its container's width, which will be a percentage of the total window's width (this means I don't know the exact pixel value in advance). And hide the black bars, and of course don't distort the image's ratio.
Can this be done using CSS only (hopefully with good browser support)? -- I am ok to assume all images should be 16:9 (for those that are of other ratio, I am ok to cut off some part of it, and only display part of it in 16:9).
Thanks
(PS: I have a JS solution, but I want to see if it doable in CSS. The JS solution is to use JS to get the container's width, then set the container's size according to 16:9 ratio. Then stretch the image and position it in the center, hide the extra areas of it -- which basically hides its top and bottom black bars)
I found this solution. Here's an example :
You set the div to width:100%, it will now stretch to the container size, in this case, the body. Then you set the padding-bottom: 56.25%; to get the 16:9 ratio.
Now set overflow: hidden; to hide what's coming out of the div and set top: -16.75%; to hide the upper black strip.
HTML
<div class="stretchy-wrapper">
<div>
<img src="http://img.youtube.com/vi/dA6Jsr7MWw4/hqdefault.jpg" style="overflow: hidden; width:100%;"/>
</div>
</div>
CSS
body {
width: 70%;
margin: 8px auto;
}
div.stretchy-wrapper{
width: 100%;
padding-bottom: 56.25%; /* 16:9 */
position: relative;
background: blue;
overflow: hidden;
}
div.stretchy-wrapper > div {
position: absolute;
top: -16.75%; bottom: 0; left: 0; right: 0;
}
Maybe this - set the image as the background to a 16 x 9 div, then just set image width to 100% and position 50% 50%
div {
background:url('http://img.youtube.com/vi/dA6Jsr7MWw4/hqdefault.jpg');
background-size:100%;
background-position: 50% 50%;
height:180px;
width:320px;
}
<div></div>
I hope this jsfiddle will help you. Cheers!
working urljsfiddle
It will be fit even your web application/web site is responsive.

CSS: Set Max Width of an Image to Its Actual Pixel Width

I am playing around with a responsive layout and I am trying to get my image handling to behave a specific way.
I want the max-width of my image to never exceed its actual resolution, however if that is too wide for the screen I'd like the width to be 90% of the screen width. The only solution I can come up with for this is set width: 90%; and then to hard-code the max-width for every image I want to display like this, which is problematic if I want to change the image on the fly or update it frequently.
Is there any CSS I can use to describe this scenario or do I have to rely on javascript tricks to set the max-width from the image's actual width once the image has finished loading?
I think the following may work. Set the max-width: 90% and the let the image take its natural width (width: auto, default value).
See the samples below.
There is an end-point (corner case) when the image size is identical to the width of the containing block (screen size). In this case, the image will take 90% of the width of the parent block. If you need this to be 100%, you would need jQuery/JavaScript to take care of the exception.
div {
border: 1px dotted blue;
margin: 10px 0;
}
div img {
max-width: 90%;
vertical-align: top; /* Removes white space below baseline */
}
.ex1 {
width: 500px;
}
.ex2 {
width: 400px;
}
.ex3 {
width: 300px;
}
<div class="ex1">
<img src="http://placehold.it/400x100">
</div>
<div class="ex2">
<img src="http://placehold.it/400x110">
</div>
<div class="ex3">
<img src="http://placehold.it/400x120">
</div>