CSS way of looping a background image with cover or contain sizing

19.7k views Asked by At

Say you are trying to animate a tilable background like this:

.container {
  width: 160px;
  height: 91px;
}
.bg {
  width: 100%;
  height: 100%;
  background: url(http://i60.tinypic.com/2j2fhjm.jpg) repeat-x left center;
  background-size: contain;
  -webkit-animation: displace 2s linear infinite;
  animation: displace 2s linear infinite;
}
@-webkit-keyframes displace {
  from {
    background-position: 0 center;
  }
  to {
    background-position: 160px center;
  }
}
@keyframes displace {
  from {
    background-position: 0 center;
  }
  to {
    background-position: 160px center;
  }
}
<div class="container">
  <textarea class="bg"></textarea>
</div>

As soon as you change the dimensions of the container, the looping animation breaks!

Is there any way to make this responsive without JS?

6

There are 6 answers

6
vals On BEST ANSWER

The problem is that, to make it responsive, you need to set the animated background-position using percentages.

But, when you set background-size as cover or contain, in some cases the width is adjusted to 100%. In this case, background-position using percentages is useless (won't move it).

The only way that I have found to manage this is moving the image to a pseudo element, and moving it. To keep the continuity, though, we will need two pseudo elements.

But that won't work on a textarea.

You didn't said anything about textarea being a requirement, so I am posting this. To show that it works on resize, hover it.

.container {
  width: 160px;
  height: 100px;
  position: relative;
  border: solid 1px black;
  display: inline-block;
}
.container:nth-child(2) {
   width: 220px;  
}
.bg {
    position: absolute;
    width: 100%;
    height: 100%;
    overflow: hidden;
}

.bg:before, .bg:after {
    content: "";
    position: absolute;
    width: 100%;
    height: 100%;
    background-image: url(https://i.stack.imgur.com/wBHey.png);
    background-size: 100%;
    animation: move 2s infinite linear;
}

.bg:before {
    right: 100%;
}

@keyframes move {
    from {transform: translateX(  0%);}
      to {transform: translateX(100%);}
}
<div class="container">
  <div class="bg"></div>
</div>
<div class="container">
  <div class="bg"></div>
</div>

test image

3
Okku On

I was able to make it work by making the background twice as big.

I know this isn't the perfect solution, but maybe you can do a trick with the image size or something to make it look the way you wanted it to.

    .container {width: 160px;height: 91px;}
    .bg {
      width: 100%;
      height: 100%;
      background: url(http://i60.tinypic.com/2j2fhjm.jpg) repeat-x left center;
      background-size: 200%;
      -webkit-animation: displace 2s linear infinite;
      animation: displace 2s linear infinite;
    }
    @-webkit-keyframes displace {
      from { background-position: left center;
      } to { background-position: 200% center; }
    }
    @keyframes displace {
      from { background-position: left center;
      } to { background-position: 200% center; }
    }
<div class="container"><textarea class="bg"></textarea></div>

0
Mohammed Moustafa On

For each background-position: 200px bottom; you need to add 1second for example if you want add 2S, you add another 200px so you make it background-position: 400px bottom; I think.

Here is the code:

.container {
  width: 160px;
  height: 91px;
}

.bg {
 width: 100%;
  height: 100%;
  background: url(http://i60.tinypic.com/2j2fhjm.jpg) fixed;
  -webkit-animation: displace 1s linear infinite;
  animation: displace 1s linear infinite;
}

@-webkit-keyframes displace {
  from { background-position: 0 bottom; }
  to { background-position: 200px bottom; }
}

@keyframes displace {
  from { background-position: 0 bottom; }
  to { background-position: 200px bottom;}
}
<div class="container">
  <textarea class="bg"></textarea>
</div>

I hope it works for your case, Let me know if you have any question.

0
wolfhammer On

.container {
  border: 1px solid black;
  display: inline-block;
  background: url(http://i60.tinypic.com/2j2fhjm.jpg) repeat-x left center;
  -webkit-animation: displace 2s linear infinite;
  animation: displace 2s linear infinite;
  background-size: 160px 100%;
}

.bg {
  float: left;
  border: 0;
  margin: 10px;
  width: 160px;
  height: 91px;
  background-color: rgba(255, 255, 255, .5);
}

@-webkit-keyframes displace {

  from {
    background-position: 0 center;
  }

  to {
    background-position: 160px center;
  }

}

@keyframes displace {

  from {
    background-position: 0 center;
  }

  to {
    background-position: 160px center;
  }

}
<div class="container">
  <textarea class="bg"></textarea>
</div>

0
Julien Grégoire On

The problem you have is with the height resizing and your different settings. To get it to work with your actual settings, the displace would have to be 2x the height of the image (since the image has a ration of 2:1). I don't think you can do only by css.

But you have different options. I'm not sure exactly which part of you display you want to keep, so here are three ways to get your translation working on resize. None have exactly your settings, because the ones you have make it hard (impossible?) to set the proper displacement. Since you're stretching the width depending on the height, it's hard to follow. Via JS it would be easy, but css has limited access to these values.

The solutions proposed keep the height constant so you can keep your displace value constant.

First one, the background-size is the size of the container in px.

Second solution, you resize textarea only on x axis.

Third you repeat on y axis and keep height and width fixed.

There are probably other workarounds that are maybe more suited to your specific needs.

.container {
  width: 160px;
  height: 80px;
   margin: 10px;
}

.bg {
  width: 100%;
  height: 100%;
  background: url(http://i60.tinypic.com/2j2fhjm.jpg) repeat-x left center;
  background-size: 160px 80px;
     padding: 0px;
  -webkit-animation: displace 2s linear infinite;
  animation: displace 2s linear infinite;
}
.bg2 {
  width: 100%;
  height: 100%;
  background: url(http://i60.tinypic.com/2j2fhjm.jpg) repeat-x left center;
  background-size: contain;
     padding: 0px;
    resize: horizontal;
  -webkit-animation: displace 2s linear infinite;
  animation: displace 2s linear infinite;
}
.bg3 {
  width: 100%;
  height: 100%;
  background: url(http://i60.tinypic.com/2j2fhjm.jpg) repeat left center;
  background-size: 160px 80px;
     padding: 0px;
  -webkit-animation: displace 2s linear infinite;
  animation: displace 2s linear infinite;
}

@-webkit-keyframes displace {
  from {
    background-position: 0 center;
  }
  to {
    background-position: 160px center;
  }
}
@-keyframes displace {
  from {
    background-position: 0 center;
  }
  to {
    background-position: 160px center;
  }
}
<div class="container">
  <textarea class="bg"></textarea>
</div>
<div class="container">
  <textarea class="bg2"></textarea>
</div>
<div class="container">
  <textarea class="bg3"></textarea>
</div>

0
tavnab On

This is similar to Ilpo's answer, except it doesn't resize the image (background-size: auto;). The animation is smooth throughout, regardless of container size.

The downside is that the width (200px) of the image needs to be known beforehand.

Since you said tilable, and since this image looks to be tilable in both directions, I also made it repeat in both dimensions. As a result, your textarea is filled with the tiled background image, animated to scroll horizontally. If you only want to tile in 1 direction, you can change repeat back to repeat-x and change the vertical position from top to whatever you need.

.container {width: 160px;height: 91px;}

.bg {
  width: 100%;
  height: 100%;
  background: url(http://i60.tinypic.com/2j2fhjm.jpg) repeat left top;
  background-size: auto;
  -webkit-animation: displace 2s linear infinite;
  animation: displace 2s linear infinite;
}

@-webkit-keyframes displace {
  from { background-position: 0px top; }
  to   { background-position: 200px top; }
}

@keyframes displace {
  from { background-position: 0px top; }
  to   { background-position: 200px top; }
}
<div class="container"><textarea class="bg"></textarea></div>