4

I'm trying to create an infinite horizontal "scroll" like a marquee effect (like this one, for example).

This is my code:

.parent {
  border: 1px solid black;
  width: 100%;
  height: 2rem;
}

.container {
  height: 100%;
  display: flex;
  padding-left: 10%;
  border: 1px solid tomato;
  animation: marquee 5s linear infinite;
}

@keyframes marquee {
  0% {
    transform: translate(0%, 0);
  }
  100% {
    transform: translate(-100%, 0);
  }
}

.child1 {
  width: 10rem;
  height: 100%;
  background-color: #84B7DF;
}
.child2 {
  width: 18rem;
  height: 100%;
  background-color: #f58db6;
}
.child3 {
  width: 13rem;
  height: 100%;
  background-color: #ffc410;
}
.child4 {
  width: 21rem;
  height: 100%;
  background-color: #C8E7C1;
}
<div class="parent">
  <div class="container">
    <div class="child1"></div>
    <div class="child2"></div>
    <div class="child3"></div>
    <div class="child4"></div>
  </div>
</div>

As you can see, it works but not perfectly. I would like that as soon as the green rectangle has shifted, the blue (slightly spaced) one immediately appears, I don't want there to ever be a whole white screen.

I hope is clear what I mean...

Thanks a lot!

Lazar Nikolic
  • 4,261
  • 1
  • 22
  • 46
  • You have to manually loop the elements. Which means duplicating the ones of the beginning and appending it to the back. You do that on and on and then you have the effect. – Code Spirit Jun 14 '19 at 09:25
  • Adjust the widths of the `.child` elements in such a way that their sum equals to the width of `.container` – nimsrules Jun 14 '19 at 09:26
  • @Nimsrules Can you give me an example? That is a simple example, suppose I don't have fixed width of children elements, but their width depends on their content.. –  Jun 14 '19 at 09:30
  • @CodeSpirit Can you post an example? –  Jun 14 '19 at 09:30
  • Check out this post - https://stackoverflow.com/questions/36433961/css3-marquee-effect-without-empty-space – nimsrules Jun 14 '19 at 11:34

2 Answers2

2

You could just add one more container element with same children, and then use display: flex with overflow: hidden on parent element. Also you can set width of the .container element to be larger then the window width using vw units and flex property.

Adjust width and padding properties on container if you have to.

.parent {
  border: 1px solid black;
  width: 100%;
  height: 2rem;
  display: flex;
  overflow: hidden;
}

.container {
  height: 100%;
  flex: 0 0 120vw;
  display: flex;
  padding-right: 10%;
  border: 1px solid tomato;
  animation: marquee 5s linear infinite;
}

@keyframes marquee {
  0% {
    transform: translate(0%, 0);
  }
  100% {
    transform: translate(-100%, 0);
  }
}

.child1 {
  width: 10rem;
  height: 100%;
  background-color: #84B7DF;
}

.child2 {
  width: 18rem;
  height: 100%;
  background-color: #f58db6;
}

.child3 {
  width: 13rem;
  height: 100%;
  background-color: #ffc410;
}

.child4 {
  width: 21rem;
  height: 100%;
  background-color: #C8E7C1;
}
<div class="parent">
  <div class="container">
    <div class="child1"></div>
    <div class="child2"></div>
    <div class="child3"></div>
    <div class="child4"></div>
  </div>

  <div class="container other">
    <div class="child1"></div>
    <div class="child2"></div>
    <div class="child3"></div>
    <div class="child4"></div>
  </div>
</div>

Another solution is to add padding-right width vw units on container.

.parent {
  border: 1px solid black;
  width: 100%;
  height: 2rem;
  display: flex;
  overflow: hidden;
}

.container {
  height: 100%;
  display: flex;
  padding-right: 50vw;
  border: 1px solid tomato;
  animation: marquee 5s linear infinite;
}

@keyframes marquee {
  0% {
    transform: translate(0%, 0);
  }
  100% {
    transform: translate(-100%, 0);
  }
}

.child1 {
  width: 10rem;
  height: 100%;
  background-color: #84B7DF;
}

.child2 {
  width: 18rem;
  height: 100%;
  background-color: #f58db6;
}

.child3 {
  width: 13rem;
  height: 100%;
  background-color: #ffc410;
}

.child4 {
  width: 21rem;
  height: 100%;
  background-color: #C8E7C1;
}
<div class="parent">
  <div class="container">
    <div class="child1"></div>
    <div class="child2"></div>
    <div class="child3"></div>
    <div class="child4"></div>
  </div>

  <div class="container other">
    <div class="child1"></div>
    <div class="child2"></div>
    <div class="child3"></div>
    <div class="child4"></div>
  </div>
</div>

Javascript / jQuery solution, you can first create clone of the original element and append it to parent. Create a function that will decrease left position of the elements with setInterval function. If the offset is less then -width of the same element that means that element is off the screen. In that case you should move element to the end of the window or to the end of the other element with some offset.

const parent = $(".parent");
const container = $(".container");
const offset = 250;

const clone = cloner(container, parent, offset);

function cloner(element, parent, offset) {
  const clone = element.clone();
  const width = element.width();

  clone.css({left: width + offset})
  parent.append(clone)
  return clone;
}

function move(element, size = 1) {
  const position = element.position().left;
  const width = element.width();

  if (position < -width) {
    const next = element.siblings().first();
    const nPosition = next.position().left;
    const nWidth = next.width();
    const wWidth = $(window).width();

    if (nPosition + nWidth < wWidth) {
      element.css({left: wWidth})
    } else {
      element.css({left: nPosition + nWidth + offset})
    }

  } else {
    element.css({left: position - size})
  }
}

window.mover = setInterval(() => {
  move(container)
  move(clone)
}, 5)
.parent {
  border: 1px solid black;
  width: 100%;
  height: 2rem;
  display: flex;
  overflow: hidden;
  position: relative;
}

.parent>div {
  position: absolute;
  left: 0;
}

.container {
  height: 100%;
  display: flex;
  border: 1px solid tomato;
}

.child1 {
  width: 10rem;
  height: 100%;
  background-color: #84B7DF;
}

.child2 {
  width: 18rem;
  height: 100%;
  background-color: #f58db6;
}

.child3 {
  width: 13rem;
  height: 100%;
  background-color: #ffc410;
}

.child4 {
  width: 21rem;
  height: 100%;
  background-color: #C8E7C1;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="parent">
  <div class="container">
    <div class="child1"></div>
    <div class="child2"></div>
    <div class="child3"></div>
    <div class="child4"></div>
  </div>
</div>
Nenad Vracar
  • 118,580
  • 15
  • 151
  • 176
  • It's not a good solution. Test it in a large screen.. https://drive.google.com/open?id=1powjOI_n8QUtzdfOP-OODvgOlEbJ8opi –  Jun 14 '19 at 09:45
  • @Buster You could set width of the container to be larger then window width with `flex`. – Nenad Vracar Jun 14 '19 at 10:12
  • Sorry, I don't understand –  Jun 14 '19 at 10:16
  • Can yoy explain better what `flex: 0 0 120vw;` means? Why `120vw`? –  Jun 14 '19 at 10:19
  • See https://developer.mozilla.org/en-US/docs/Web/CSS/flex, `120vw` is just random pick, the point is that it is larger then window, you should customize those based on your actual example and animation speed and padding. – Nenad Vracar Jun 14 '19 at 10:21
  • I test you code in a codepen. In my case screen width is 1300 so I use `1300vw` and greater values than `1300`. In each case it doesn't work. What I'm wrong? –  Jun 14 '19 at 10:26
  • `100vw` is full window width so `1300vw` is `window width * 13` – Nenad Vracar Jun 14 '19 at 10:28
  • @Buster I've added another solution with jquery. – Nenad Vracar Jun 14 '19 at 15:05
0
.marquee {
    position: relative;
    width: 100%;
    height: 1.5em;
    line-height: 1.5em;
    overflow: hidden;
    > div {
        position: absolute;
        display: flex;
        flex-wrap: nowrap;
        animation: marquee 10s linear infinite;
        transform: translateX(100%);
        > * {
            display: inline;
            white-space: nowrap;
            &:last-child {
                padding-right: 100%;
            }
        }
    }
}
@keyframes marquee {
    0% {
        -moz-transform: translateX(100%);
        -webkit-transform: translateX(100%);
        transform: translateX(100%);
    }
    100% {
        -moz-transform: translateX(-100%);
        -webkit-transform: translateX(-100%);
        transform: translateX(-100%);
    }
}