1

I'm currently working on a site where the stage/hero has a set of divs with looping background images (images of text), and I'm using jQuery to animate their positioning to give the appearance of an "ocean" of images moving across the screen.

var position = 0;

setInterval(function () { 
    position -= 1;
    $(".sub-image-1").css({ "background-position":+ position +"px 0px" }) 
}, 20 );

setInterval(function () { 
    position -= 1;
    $(".sub-image-2").css({ "background-position":+ position +"px 0px" }) 
}, 140 );

setInterval(function () { 
    position -= 1;
    $(".sub-image-3").css({ "background-position":+ position +"px 0px" }) 
}, 200 );
.stage-image {
    width:100vh;
    height:30vh;
}

.stage-sub-image { 
    position:absolute; top:0;
    width:100%;
    height:100%;
    background-repeat:repeat-x;
} 
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="stage-image">
  <div class="stage-sub-image sub-image-1" style="background:url(https://upload.wikimedia.org/wikipedia/commons/c/ca/Triple-Spiral-4turns_green_transparent.png)"></div>
  <div class="stage-sub-image sub-image-2" style="background:url(https://upload.wikimedia.org/wikipedia/commons/b/be/Blender3D_Lisc_lipy-Transparent.png)"></div>
  <div class="stage-sub-image sub-image-3" style="background:url(https://cdn0.iconfinder.com/data/icons/ball/256/transparent.png)"></div>
</div>

I'm curious if there is a better approach to this solution that is less browser intensive. I feel setInterval in this case adds a lot more overhead to the site than necessary. Are there better approaches to handle something like this? On top of the performance issues, the animations done through JS are very choppy and I am hoping for a smoother effect.

Here's a CodePen of what I currently have above:

https://codepen.io/stufu/pen/GvJdxq

UPDATE

@FuriousD gave a good suggestion below in using CSS to accomplish this, while this is is closer to what I was hoping for, CSS animations cause a jump when hitting the end of the background position.

Here's a Codepen based on their answer but with a new problem of "jumping" after the animation completes:

codepen.io/stufu/pen/YxXaLR

Stu Furlong
  • 3,490
  • 5
  • 34
  • 47

2 Answers2

1

You can use CSS animation. Note, the width of the image (-250px) needs to be hardcoded into the animation, but this can be generated dynamically:

var containers = $('.stage-sub-image');
var containerWidth = $(containers[0]).width();
var styleElem = document.createElement('style');
var animClass = 'animateMe';

document.body.append(styleElem);

styleElem.innerHTML =
  '@keyframes animBg {' +
    'from { background-position: 0px top; }' +
    'to { background-position: -' + containerWidth + 'px top }' +
  '}' +
  '.' + animClass + ' { animation: animBg 10s linear infinite; background-size: 100% auto; }';


containers.addClass(animClass);
.stage-sub-image {
  width: 200px;
  height: 130px;
  background-color: #909;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="stage-image">
    <div class="stage-sub-image" style="background-image:url(http://via.placeholder.com/250x130)"></div>
    <div class="stage-sub-image" style="background-image:url(http://via.placeholder.com/250x130)"></div>
    <div class="stage-sub-image" style="background-image:url(http://via.placeholder.com/250x130)"></div>
</div>
FuriousD
  • 844
  • 5
  • 16
  • Thanks for the comment @FuriousD. I didn't explain my question enough and unfortunately with the approach above hardcoding the values won't quite work. There is a "jump" once it hits the end of the width – Stu Furlong Jul 27 '17 at 04:05
  • There are a couple approaches you could use combining JS with CSS animations - for example using JS to re-trigger animations and so on. I'm not sure there's a trivial solution though that doesn't involve hard coding values. – FuriousD Jul 27 '17 at 04:39
  • I think thats part of the problem. When you hardcode the width (even if its in vw) it will skip at the end. I set up a codepen to show what happens: https://codepen.io/stufu/pen/YxXaLR The advantage with the pure JS approach is it "infinitely" changes the background position to infinity, CSS will restart it. But I have a hard time swallowing the setInterval – Stu Furlong Jul 27 '17 at 04:52
  • One approach could be to set the background image to 100% width of its container. You can then generate the CSS animation dynamically (it would read something like `from { background-position-x: 0px } to { background-position-x: -(bgContainer.width()) + 'px'`) and inject this into the page.It's more work but more performant than `setInterval` – FuriousD Jul 27 '17 at 05:10
  • @FurdiousD - If you could provide an example of your thoughts I'd be much appreciated. I'm having a difficult time implementing a solution like this – Stu Furlong Jul 27 '17 at 07:03
  • 1
    Have a look at my updated answer. The drawback of this approach is that the background image will always be 100% width of it's container, so scaling will occur. If the background image needs to retain its original dimensions then you could incorporate some code to determine the size of the background image and use that to generate the animation CSS instead of the container width. See this answer: https://stackoverflow.com/questions/3098404/get-the-size-of-a-css-background-image-using-javascript – FuriousD Jul 27 '17 at 23:56
0

I want to post an update that I was able to successfully accomplish this with a lot of help from the answer from @FuriousD. Ultimately his insights led to this and I am accepting his answer, however I wanted to provide a working version of what I've accomplished through pure CSS.

Basically this, requires knowing the dimensions of your background image. Coding the image with into the CSS animation will allow it to scroll smoothly and with a good amount of scalability. Here's my result:

CSS:

@keyframes animBg1 {
  from {
    background-position: 0px top;
  }
  to {
    background-position: -613px top;
  }
}

@keyframes animBg2 {
  from {
    background-position: 0px top;
  }
  to {
    background-position: -327px top;
  }
}
@keyframes animBg3 {
  from {
    background-position: 0px top;
  }
  to {
    background-position: -256px top;
  }
}

.stage-image {
  .stage-sub-image { 
    position:absolute; top:0;
    width:100vw;
    height:100vh;
    background-repeat:repeat-x;
    animation-fill-mode: forwards;
    animation-iteration-count: infinite;
    animation-timing-function: linear;
  }
  .sub-image-1 {
    animation-name: animBg1;
    animation-duration: 5s;
  }
  .sub-image-2 {
    animation-name: animBg2;
    animation-duration: 2s;
  }
  .sub-image-3 {
    animation-name: animBg3;
    animation-duration: 3s;
  }
}

HTML

<div class="stage-image">
    <div class="stage-sub-image sub-image-1" style="background-image:url(https://upload.wikimedia.org/wikipedia/commons/c/ca/Triple-Spiral-4turns_green_transparent.png)"></div>
    <div class="stage-sub-image sub-image-2" style="background-image:url(https://upload.wikimedia.org/wikipedia/commons/1/16/Rainbow_trout_transparent.png)"></div>
    <div class="stage-sub-image sub-image-3" style="background-image:url(https://cdn0.iconfinder.com/data/icons/ball/256/transparent.png)"></div>
</div>

Codepen example

Stu Furlong
  • 3,490
  • 5
  • 34
  • 47