ralphilius
2/21/2018 - 7:06 AM

Pure CSS Ticker (Horizontal) by Lewis Carey https://codepen.io/lewismcarey/pen/GJZVoG

Pure CSS Ticker (Horizontal) by Lewis Carey https://codepen.io/lewismcarey/pen/GJZVoG

<h1>Pure CSS Ticker (No-JS)</h1>
<h2>A smooth horizontal news like ticker using CSS transform on infinite loop</h2>

<div class="ticker-wrap">
<div class="ticker">
  <div class="ticker__item">Letterpress chambray brunch.</div>
  <div class="ticker__item">Vice mlkshk crucifix </div>
  <div class="ticker__item">Ugh PBR&B kale </div>
  <div class="ticker__item">Gluten-free mumblecore </div>
  <div class="ticker__item">Authentic bitters </div>
  <div class="ticker__item">Letterpress chambray brunch.</div>
  <div class="ticker__item">Vice mlkshk </div>
  <div class="ticker__item">Ugh PBR&B kale chips Echo Park.</div>
  <div class="ticker__item">Gluten-free </div>
  <div class="ticker__item">Authentic bitters </div>
</div>
</div>

<p>So, annoyingly, most JS solutions don't do horizontal tickers on an infinite loop, nor do they render all that smoothly.</p>
<p>The difficulty with CSS was getting the animation to transform the entire items 100% yet include an offset that was only the width of the browser (and not the items full width).</p>
<p>Setting the start of the animation to anything less than zero (e.g. -100%) is unreliable as it is based on the items width, and may not offset the full width of the browser or creates too large an offset</p>
<p>Padding left on the wrapper allows us the correct initial offset, but you still get a 'jump' as it then loops too soon. (The full text does not travel off-screen)</p>
<p>This is where adding display:inline-block to the item parent, where the natural behaviour of the element exists as inline, gives an opportunity to add padding-right 100% here. The padding is taken from the parent (as its treated as inline) which usefully is the wrapper width.</p>
<p><b>Magically*</b> we now have perfect 100% offset, a true 100% translate (width of items) and enough padding in the element to ensure all items leave the screen before it repeats! (width of browser)</p>
<p>*Why this works: The inside of an inline-block is formatted as a block box, and the element itself is formatted as an atomic inline-level box. <br>Uses `box-sizing: content-box`<br>
Padding is calculated on the width of the containing box.<br>
So as both the ticker and the items are formatted as nested inline, the padding must be calculated by the ticker wrap.</p>

<p>Ticker content c/o <a href="http://hipsum.co/">Hipsum.co</a></p>
* { box-sizing: border-box; }
$duration: 30s;

@-webkit-keyframes ticker {
  0% {
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
    visibility: visible;
  }

  100% {
    -webkit-transform: translate3d(-100%, 0, 0);
    transform: translate3d(-100%, 0, 0);
  }
}

@keyframes ticker {
  0% {
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
    visibility: visible;
  }

  100% {
    -webkit-transform: translate3d(-100%, 0, 0);
    transform: translate3d(-100%, 0, 0);
  }
}

.ticker-wrap {
  
  position: fixed;
  bottom: 0;
  width: 100%;
  overflow: hidden;
  height: 1.2rem;
  background-color: rgba(#000, 0.9); 
  padding-left: 100%;
  box-sizing: content-box;

  .ticker {

    display: inline-block;
    height: 1rem;
    line-height: 1rem;  
    white-space: nowrap;
    padding-right: 100%;
    box-sizing: content-box;

    -webkit-animation-iteration-count: infinite; 
            animation-iteration-count: infinite;
    -webkit-animation-timing-function: linear;
            animation-timing-function: linear;
   -webkit-animation-name: ticker;
           animation-name: ticker;
    -webkit-animation-duration: $duration;
            animation-duration: $duration;

    &__item {

      display: inline-block;

      padding: 0 1rem;
      font-size: 0.7rem;
      color: white;   

    }

  }

}



body { padding-bottom: 5rem; }
h1,h2,p {padding: 0 5%;}
* {
  box-sizing: border-box;
}
@-webkit-keyframes ticker {
  0% {
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
    visibility: visible;
  }
  100% {
    -webkit-transform: translate3d(-100%, 0, 0);
    transform: translate3d(-100%, 0, 0);
  }
}
@keyframes ticker {
  0% {
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
    visibility: visible;
  }
  100% {
    -webkit-transform: translate3d(-100%, 0, 0);
    transform: translate3d(-100%, 0, 0);
  }
}
.ticker-wrap {
  position: fixed;
  bottom: 0;
  width: 100%;
  overflow: hidden;
  height: 1.2rem;
  background-color: rgba(0, 0, 0, 0.9);
  padding-left: 100%;
  box-sizing: content-box;
}
.ticker-wrap .ticker {
  display: inline-block;
  height: 1rem;
  line-height: 1rem;
  white-space: nowrap;
  padding-right: 100%;
  box-sizing: content-box;
  -webkit-animation-iteration-count: infinite;
  animation-iteration-count: infinite;
  -webkit-animation-timing-function: linear;
  animation-timing-function: linear;
  -webkit-animation-name: ticker;
  animation-name: ticker;
  -webkit-animation-duration: 30s;
  animation-duration: 30s;
}
.ticker-wrap .ticker__item {
  display: inline-block;
  padding: 0 1rem;
  font-size: 0.7rem;
  color: white;
}
body {
  padding-bottom: 5rem;
}
h1, h2, p {
  padding: 0 5%;
}