2

I'm working on a project where I need to place a video clip on a specific part of the background image (video should always be on top of the phone's screen). The background is responsive and keeps changing. How can I make sure the video containing the video always stays in the same relative location?

CSS:

.main{
background-image: url("../images/moshe.jpg"); 
background-attachment:fixed;
background-position: center center;
background-size: cover;
height: auto;
left: 0;
min-height: 100%;
min-width: 100%;
position: absolute;
top: 0;
width: auto;
}
.overlay{
position:fixed;
position: absolute;
top: 30%;
left: 30%;

}

HTML:

 <!-- video -->
    <div class="embed-responsive embed-responsive-16by9 overlay">
    <video muted autoplay loop class="embed-responsive-item" width="100">
     <source src="images/time.mp4" type=video/mp4>
    </video>
    </div>
Pebbl
  • 34,937
  • 6
  • 62
  • 64
Roman Smirnov
  • 329
  • 1
  • 4
  • 12
  • 3
    First, note there's no `iframe` involved here, but a `video` tag. – Boaz Apr 04 '15 at 20:06
  • You're right. I was planning to put an iframe there with a youtube video later on. Skipped that :) – Roman Smirnov Apr 04 '15 at 20:09
  • 1
    Second, consider using JS to calculate the correct position of the video, based on the window size. – Boaz Apr 04 '15 at 20:09
  • Thanks I'll look into that. Although I was hoping for a pure css/html solution. – Roman Smirnov Apr 04 '15 at 20:11
  • 1
    You might be able to achieve that by breaking up the image into different elements, but that would not be as responsive as the current markup. Though I understand the preference for a CSS-only solution, sometimes JS is the way to go. – Boaz Apr 04 '15 at 20:15

2 Answers2

5

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-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>
Pebbl
  • 34,937
  • 6
  • 62
  • 64
  • Can I have your babies? Too bad I'm male. Awesome solution regardless, I'll figure out how to make it work based on what you did. Thanks a lot!!!! :) – Roman Smirnov Apr 04 '15 at 23:23
  • 1
    @RomanSmirnov ~ Heh :) No problem... nice question by the way. Not attempted to place an element inline with a background-sized background before... it really should be easier to achieve than the above! Shame after all that it isn't a complete CSS solution though. – Pebbl Apr 05 '15 at 00:01
  • Just a quick comment and clarification for people who might use this solution. `no-repeat center center` and `background-size: cover` are not the same thing. – Roman Smirnov Apr 25 '15 at 15:07
1

First of all your css is a bit redundant.

.overlay{
    position:fixed;       /*  <--- This line  */
    position: absolute;   /*  <--- And This line  */
    top: 30%;
    left: 30%;
}

Here position:absolute will be taken into consideration as it is placed later.

Considering the positioning of the video element is correctly done by top: 30% and left: 30%, try giving the lowest z-index value (like z-index: -100) to it to take it back.

Anupam Basak
  • 1,503
  • 11
  • 13
  • Thanks for the tip! The problem is top:30% and left 30% are NOT correct. It's this positioning I need to figure out. – Roman Smirnov Apr 04 '15 at 21:28
  • 1
    If it's with the positioning, then post the complete code with a link to JSFiddle. That would make it a bit easier to debug. – Anupam Basak Apr 05 '15 at 04:53
  • Thanks Anupam :) I managed to solve the problem over night's work. Will post my own solution in addition to Pebbl's later today.... – Roman Smirnov Apr 05 '15 at 06:19