2

I want to use a responsive div with a scrolling background image.

In the example I showed it small and large. I want the entire scroll to be constant - so the small div should take x seconds and the large div should also take x seconds (rather than the small one taking less time to complete a whole image pan than the larger one).

I've tried using percentage values in background-position-x but it stops the animation.

.offset {
  background-image: url('https://upload.wikimedia.org/wikipedia/commons/c/c4/PM5544_with_non-PAL_signals.png');
  background-size: 100%;
  animation: slideLeft768px 5s linear infinite;
}

.div1 {
  width: 76.8px;
  height: 57.6px;
}

.div2 {
  width: 768px;
  height: 576px;
}

@keyframes slideLeft768px {
  0% {
    background-position-x: 768px;
  }
  100% {
    background-position-x: 0px;
  }
}
<div class="offset div1"></div>
<div class="offset div2"></div>

====================

This is based on Temani Afif's answer:

.offset {
  background-image: url('https://upload.wikimedia.org/wikipedia/commons/c/c4/PM5544_with_non-PAL_signals.png');
  background-size: 100%;
  animation: slideLeft 5s linear infinite;
  width: var(--p);
  --p: 40vw;
  height: 30vw;
}
.div1 {
    --p: 12vw;
    height: 9vw;
}

@keyframes slideLeft {
  0% {
    background-position-x: var(--p);
  }
  100% {
    background-position-x: 0px;
  }
}  
<div class="offset div1"></div>
<div class="offset div2"></div>

I made it only use responsive units so it adjusts when you resize the window.

Luke Wenke
  • 1,149
  • 2
  • 23
  • 43

2 Answers2

4

You can consider CSS variable to make the animation dynamic:

.offset {
  background-image: url('https://upload.wikimedia.org/wikipedia/commons/c/c4/PM5544_with_non-PAL_signals.png');
  background-size: 100%;
  animation: slideLeft768px 5s linear infinite;
  width: var(--p);
}

.div1 {
  --p: 76.8px;
  height: 57.6px;
}

.div2 {
  --p:768px;
  height: 576px;
}

@keyframes slideLeft768px {
  0% {
    background-position: var(--p,0px) 0px;
  }
  100% {
    background-position: 0px 0px;
  }
}
<div class="offset div1"></div>
<div class="offset div2"></div>

And since your are using the same image with known dimension you can optimize your code like below:

.offset {
  background-image: url('https://upload.wikimedia.org/wikipedia/commons/c/c4/PM5544_with_non-PAL_signals.png');
  background-size: 100%;
  animation: slideLeft768px 5s linear infinite;
  width: calc(768px * var(--p,1));
  height: calc(576px * var(--p,1));
}

.div1 {
  --p: 0.1;
}

.div2 {
  --p:0.2;
}

@keyframes slideLeft768px {
  0% {
    background-position: calc(768px * var(--p,1)) 0px;
  }
  100% {
    background-position: 0px 0px;
  }
}
<div class="offset div1"></div>
<div class="offset div2"></div>
<div class="offset" style="--p:0.8"></div>

You can also check this answer (https://stackoverflow.com/a/51734530/8620333) to understand why percentage value won't work and how to make it working.

Temani Afif
  • 245,468
  • 26
  • 309
  • 415
  • Thanks I didn't know about css variables like that. It seems it has pretty good browser support (I'm going to use Cordova). Thanks for the link to that answer and your advanced code. – Luke Wenke Jan 05 '19 at 10:43
1

The trick is to use a percentage for the background-position. Since setting the background-size to 100% makes this impossible, we need to set it to another value.

A trick is to use the padding for this. Create a padding right the same dimension than the width. Making the background origin the paddin box, and clip tghe content box, now we can set the size to 50%. Visually, nothing will change. (In case that the extra padding is a problem, you could set a negative margin or a clip-path). And now, the background-position can be moved in percentages:

.offset {
  background-image: url('https://upload.wikimedia.org/wikipedia/commons/c/c4/PM5544_with_non-PAL_signals.png');
  background-size: 50%;
  background-origin: border-box;
  background-clip: content-box;
  animation: slideLeft768px 5s linear infinite;
}

.div1 {
  width: 76.8px;
  padding-right: 76.8px;
  height: 57.6px;
}

.div2 {
  width: 768px;
  padding-right: 768px;
  height: 576px;
}

@keyframes slideLeft768px {
  0% {
    background-position-x: 100%;
  }
  100% {
    background-position-x: 0%;
  }
}
<div class="offset div1"></div>
<div class="offset div2"></div>
vals
  • 61,425
  • 11
  • 89
  • 138
  • I like this trick. We can also consider pseudo element and we can easily get rid of the extra space by hidding the overflow : https://jsfiddle.net/u9yf0gwL/ – Temani Afif Jan 05 '19 at 09:40
  • Hi I want to use a border in the div so I can't use this method. – Luke Wenke Jan 05 '19 at 10:44