Cascading style solution
There is a way to achieve this in pure CSS, but — as usual — it does come with some caveats:
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!
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.
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-11744120
var 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>