Css Background-Size: Cover + Background-Attachment: Fixed Clipping Background Images

CSS background-size: cover + background-attachment: fixed clipping background images

Unfortunately this is simply an artifact of how fixed positioning works in CSS and there is no way around it in pure CSS - you have to use Javascript.

The reason this happens is due to the combination of background-attachment: fixed and background-size: cover. When you specify background-attachment: fixed it essentially causes the background-image to behave as if it were a position: fixed image, meaning that it's taken out of the page flow and positioning context and becomes relative to the viewport rather than the element it's the background image of.

So whenever you use these properties together, the cover value is being calculated relative to the size of the viewport irrespective of the size of the element itself, which is why it works as expected when the element is the same size as the viewport but is cropped in unexpected ways when the element is smaller than the viewport.

To get around this you basically need to use background-attachment: scroll and bind an event listener to the scroll event in JS that manually updates the background-position relative to how far the window has been scrolled in order to simulate fixed positioning but still calculate background-size: cover relative to the container element rather than the viewport.

Background attachment fixed but full size image

Using an image tag it's doable considering object-fict and position:fixed

* {
box-sizing: border-box;
}
body {
margin: 0;
}

.home-panel {
display: grid;
grid-template-columns: 50% 50%;
align-items: center;
height: 100vh;
}

.home-panel-media {
height: 100vh;
clip-path: inset(0); /* to clip the image to this div */
}

.home-panel-media img {
height: 100vh;
width: 50vw;
position: fixed;
top: 0;
right: 0;
object-fit: cover;
object-position: top center;
}
<div id="wrapper">
<div class="home-panels-wrapper">
<div class="home-panel home-panel-1">
<div class="home-panel-content">
<h1>Hello World</h1>
<p>
Lorem ipsum dolar gamet
</p>
<p>
</div>
<div class="home-panel-media">
<img src="https://i.pinimg.com/originals/a1/1e/2a/a11e2a9d5803e4dc2c034819ce12a16e.jpg">
</div>
</div>
<div class="home-panel home-panel-2">
<div class="home-panel-content">
<h1>Hello World</h1>
<p>
Lorem ipsum dolar gamet
</p>
<p>
</div>
<div class="home-panel-media">
<img src="https://images.squarespace-cdn.com/content/v1/56e05d74746fb93dcd805e8b/1553109528895-RYRDJLVNN61UDN65S59U/ke17ZwdGBToddI8pDm48kDmiacAi515_OfcChA6MRIQUqsxRUqqbr1mOJYKfIPR7LoDQ9mXPOjoJoqy81S2I8PaoYXhp6HxIwZIk7-Mi3Tsic-L2IOPH3Dwrhl-Ne3Z23Oc3-AlX22j3PUzoYuTVI2MKzQWw7jmw4KYJnTQU-9E_twk1mUNduAk0T15_nZ7z/Tulsa-Headshot-Photographer_9639a.jpg?format=1500w">
</div>
</div>
</div>
</div>

Background-size cover jumping when background-position switches to fixed

I had the same issue, when setting background-size to cover or contain

Setting a fixed height, in example for smaller screens via @media prevents the background-image from jumping. After my tests I came to the conclusion, that the jumping is due to the orientation of the element after setting background-attachment to fixed

Setting it to fixed, the size is calculated by the size of the viewport, not the element containing the background-image. This is where the jumping comes from and why setting a fixed height or width for the background-size solves this issue.

Repaint bug with background-attachment fixed and background-size cover in Chrome

I have noticed the best way to make sure the page backgound stays fixed no matter what is: place it as the background image of an empty first child of body, with these CSS rules:

.background-holder {
position: fixed;
width: 100%;
height: 100%;
display: block;
z-index: -10;
background-image: url(//link-to-image);
background-size: cover;
}

And here's the page structure:

<html>
<head>
</head>
<body>
<div class="background-holder"></div>
<div class="main-container">
<!-- content goes here -->
</div>
</body>
</html>

Fix video to background position, when using background-size cover

Cascading style solution

There is a way to achieve this in pure CSS, but — as usual — it does come with some caveats:

  1. It relies on vh and vw units. These are relatively new, so support for them is not perfect, but it isn't that bad either. Yay for CSS3!

  2. In order for this to work, your target image has to be sized to the viewport, or at least something that relates to the viewport.

  3. It relies on media queries, but then again, so does much of the mobile-enabled interweb.

It may need a little tweaking to get the video exactly where you want it. But it has worked in everything modern I've tested so far.


Explanification

The key point to realise is that when "covering" an image, the browser switches between two ways of scaling the image. This switch happens when the viewport width goes above a certain proportion of the height. That proportion depends on the aspect ratio of the image you are using.

I'd like to say I'm awake enough at the moment to have actually calculated the value used in @media (min-width: 178vh) but if I'm honest, I trialed and errored it. Just know that taking the original dimensions of your image 3264 x 1836, and calculating a ratio from that, 3264 / 1836 = 1.7777777778 leaves you with a certain number. Because vh and vw a 1/100ths of the viewport dimensions, you should multiply this number by a 100 and you get 178 when rounded. This is the switch point. Obviously if you change the dimensions of your original image, you will need to recalculate this and update the media query's selector.

Other than that it's just the simple case of working out what proportion of the viewport your video occupies in terms of vh and vw. Again, this was trial and errored, but in my defense beer seems to increase the likelihood that I'll use that methodology... no idea why.


Ramifications

Ah well, after a bit further testing it seems that webkit doesn't like the vh in the media query — or at least it doesn't seem to be applying. I shall have to investigate as to why. It's possible that it just doesn't support this in max-width which is a shame. There may be a solution in using vmin and vmax however.

Some time later.

Yep. Webkit lets the side down, again. Sorry, hats off to what is a very good browser... it just seems I can always rely on Firefox to do the right thing, Webkit, not so much. Unfortunately if a browser doesn't understand vh and vw in a particular context, there isn't much that can be done, save for falling back to scripting. Annoyingly Webkit does understand the viewport lengths in other contexts, so they have implemented code to handle these values, just not for media queries — as far as I can tell.

There is a slight fix though, it involves using orientation in the media query instead. This isn't perfect, as it fails when the viewport is anything that approximates a square. It definitely decreases the number of different ratios it will fail for however. I've updated the code below.


Concollusions

In the end if I were implementing this (as the browsers currently stand). I'd use the CSS version, and then enhance with JavaScript for browsers that don't support the viewport metrics in media queries. You could do this from scratch using window.matchMedia or a library such as Enquire.js.

I've updated the following with a JavaScript fallback, it should be noted this should be improved upon... most likely by using existing libraries to use cross-browser methods for applying event listeners and adding and removing class names. The current code will override any class names set on the HTML tag, so beware!

/// with thanks to http://stackoverflow.com/questions/3437786/#answer-11744120var viewportDimensions = function(){  var w = window,      d = document,      e = d.documentElement,      g = d.getElementsByTagName('body')[0];  return {    w: w.innerWidth || e.clientWidth || g.clientWidth,     h: w.innerHeight|| e.clientHeight|| g.clientHeight  };};
window.matchMedia && (function(){ var test = window.matchMedia('screen and (min-width: 0vh)'), f; /// this will only fail on browsers that do not support vh in media queries if ( !test.matches ) { /// listen out for resize window.addEventListener('resize', (f=function(e){ /// get the viewport dimensions var vpd = viewportDimensions(); /// run our js based test, same as the media query if ( vpd.w > (1.78 * vpd.h) ) { document.documentElement.className = 'min-width-178vh'; } else { document.documentElement.className = 'max-width-178vh'; } })); /// first run! f(); }})();
/* Shifts the coordinates to the center, because   that is the only fixed coordinate when using   "cover" on an image */.layer {    position: absolute;    left: 50%;    top: 50%;    width: 1px;    height: 1px;}
/* By default base our values on viewport height This is what "cover" is doing, at least when your image is streched to the viewport */.video { position: absolute; width: 25.5vh; left: -50.5vh; top: -22.8vh; background: #F00;}
/* Half fix for webkit, it seems webkit does not support vh and vw in its media queries... why??? This will work as long as the viewport dimension are not squareish */@media screen and (orientation: landscape) { .video { top: -12.75vw; left: -28.5vw; width: 14.2vw; }}
/* Detect when we change scaling/cropping reaction and base our values on viewport width instead */@media screen and (min-width: 178vh) { .video { top: -12.75vw; left: -28.5vw; width: 14.2vw; }}
/* Repeating myself to repair the damage that the webkit fix does to Firefox's handling */@media screen and (max-width: 178vh) { .video { width: 25.5vh; left: -50.5vh; top: -22.8vh; }}
/* These styles override the media queries when called in by the JavaScript fallback */.min-width-178vh .video { top: -12.75vw !important; left: -28.5vw !important; width: 14.2vw !important;}.max-width-178vh .video { width: 25.5vh !important; left: -50.5vh !important; top: -22.8vh !important;}
/* These styles are just for set-up. You don't need to split the background image out into .inner, I just did so to open up options whilst trialing a solution. */.main { top: 0; left: 0; position: absolute; width: 100%; height: 100%;}
.inner { position: absolute; background: url("http://www.codelamp.co.uk/so/cover-video-pos-bg.jpg") no-repeat center center / cover; background-color: #000; width: 100%; height: 100%;}
<div class="main">    <div class="inner"></div>    <div class="layer">        <div class="video">            <video width="100%"></video>        </div>    </div></div>

background attachment:fixed how to make the image not full screen ?

Changing:

.pic1,
.pic2,
.pic3 {
background-size: cover;
background-position: center center;
...

to:

.pic1,
.pic2,
.pic3 {
background-size: 50% auto;
background-position: center right;
...

.pic2 {
background-image: url("../img/restaurant.jpg");
background-position: center left;
}

Could be one solution to fix your issue.

CSS Background Attachment Scroll/Fixed and Background Size cover

The difference isn't really in background-size: cover. The difference between background-attachment: scroll and background-attachment: fixed is that

"...scroll means that the background is fixed with regard to the element itself and does not scroll with its contents. (It is effectively attached to the element's border.)

"...fixed means that the background is fixed with regard to the viewport. Even if an element has a scrolling mechanism, a ‘fixed’ background doesn't move with the element."

as MDN says. So you'll see in your fiddle that the background-attachment: fixed background doesn't remain in its containing element <div id="two"> border. It is, instead taking on the fixed point of absolute positioning 0, 0 in the entire body's background.

In essence, background-attachment: fixed is overwriting background-size: cover and not allowing the latter style to take effect.



Related Topics



Leave a reply



Submit