css Audio - Active file-generic CSS - Active Generic - Active HTML - Active JS - Active SVG - Active Text - Active file-generic Video - Active header Love html icon-new-collection icon-person icon-team numbered-list123 pop-out spinner split-screen star tv

Pen Settings

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URL's added here will be added as <link>s in order, and before the CSS in the editor. If you link to another Pen, it will include the CSS from that Pen. If the preprocessor matches, it will attempt to combine them before processing.

+ add another resource

You're using npm packages, so we've auto-selected Babel for you here, which we require to process imports and make it all work. If you need to use a different JavaScript preprocessor, remove the packages in the npm tab.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Use npm Packages

We can make npm packages available for you to use in your JavaScript. We use webpack to prepare them and make them available to import. We'll also process your JavaScript with Babel.

⚠️ This feature can only be used by logged in users.

Code Indentation

     

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

HTML Settings

Here you can Sed posuere consectetur est at lobortis. Donec ullamcorper nulla non metus auctor fringilla. Maecenas sed diam eget risus varius blandit sit amet non magna. Donec id elit non mi porta gravida at eget metus. Praesent commodo cursus magna, vel scelerisque nisl consectetur et.

            
              <h1>WAAPI Scrolling Marquee</h1>
<h2>( Hover the marquee to play/pause )</h2>
<div class="container">
  <div class="grid marquee">
    <div class="cell w66">
      <div class="content">
        Start
      </div>
    </div>
    <div class="cell w33">
      <div class="content">
        1
      </div>
    </div>
    <div class="cell w25">
      <div class="content">
        2
      </div>
    </div>
    <div class="cell w25">
      <div class="content">
        3
      </div>
    </div>
    <div class="cell w50">
      <div class="content">
        4
      </div>
    </div>
    <div class="cell w100">
      <div class="content">
        5
      </div>
    </div>
    <div class="cell w16">
      <div class="content">
        6
      </div>
    </div>
    <div class="cell w83">
      <div class="content">
        7
      </div>
    </div>
    <div class="cell w25">
      <div class="content">
        8
      </div>
    </div>
    <div class="cell w75">
      <div class="content">
        9
      </div>
    </div>
    <div class="cell w66">
      <div class="content">
        10
      </div>
    </div>
    <div class="cell w33">
      <div class="content">
        Finish
      </div>
    </div>
  </div>
</div>
            
          
!
            
              
* {
  -webkit-box-sizing: border-box;
  box-sizing: border-box;
}

body {
  background: #08f;
  margin: 0;
}

.no-wrap {
  white-space: nowrap!important;
}

.container{
  max-width: 992px;
  margin: 0 auto;
  overflow: hidden;
}

/* quickest, dirtiest grid possible */
.grid {
  display: block;
  font-size: 0; /* if anybody knows a browser in which this doesn't work, would love to hear from you in the comments */
}

.cell {
  display: inline-block;
  font-size: 2rem; /* fix that elegant/ugly font-size: 0 hack */
  font-weight: 700;
  position: relative;
  width: 100%;
  height: 30vh;
  padding: 1rem .5rem;
  text-align: center;
  cursor: pointer;
}

.cell .content {
  display: block;
  position: absolute;
  top: .5rem;
  bottom: .5rem;
  left: .5rem;
  right: .5rem;
  padding: 1rem;
  color: #555;
  background: #fff;
  box-shadow: 0 2px 10px -10px #ccc;
}

/* calc it up */
.cell.w16 {
  width: 16%;
  width: -webkit-calc(100% / 6);
  width: calc(100% / 6)
}

.cell.w25 {
  width: 25%
}

.cell.w33 {
  width: 33%
  width: -webkit-calc(100% / 3);
  width: calc(100% / 3)
}

.cell.w50 {
  width: 50%
}

.cell.w66 {
  width: 66%;
  width: -webkit-calc(100% / (3 / 2));
  width: calc(100% / (3 / 2))
}

.cell.w75 {
  width: 75%
}

.cell.w83 {
  width: 83%;
  width: -webkit-calc(100% / (6 / 5));
  width: calc(100% / (6 / 5))
}

.cell.w100 {
  width: 100%
}

h1,h2 {
  text-align:center;
  color:#fff;
}
h1 {
  font-size: 3rem;
  margin-bottom: .15rem;
}
h2 {
  font-size: 1.3rem;
  margin-top: .15rem;
  margin-bottom: 15vh;
}
            
          
!
            
              ;(function(){
  
  // we need to set this here for now.
  var marquee;

  // first, let's grab the element we're going to move around
  var marquee_el = document.querySelector( '.grid.marquee' );
  var children = marquee_el.querySelectorAll( '.cell');

  // the key here is not to animate all of the cells in the grid, which might be products or what have you; we have a grid constrained by a container, so we can just translate the entire grid and only the part of the grid that fits into the container will be visible anyway
  function createMarquee(){
    
    // just to be doubly sure there's an animation method...
    if ('animate' in marquee_el && typeof marquee_el.animate === 'function') {
      
      // we're going to recreate the marquee animation when the viewport is resized
      // so get rid of any existing animation first
      if( typeof marquee !== 'undefined' ) marquee.cancel();

      // set this dynamically, so the thing will gracefully degrade to a typical grid of items
      marquee_el.style.whiteSpace = 'nowrap';

      // create a variable for the distance by which the grid element will be transformed
      var displacement = 0;

      // the width of all the elements in the marquee
      // it's important to tot up the child element widths because if overflow is hidden,
      // the clientWidth of the grid_to_animate element will be that of the parent element
      for ( var j = 0; j < children.length; ++j ) displacement += children[j].clientWidth;

      // crucial: subtract the width of the container
      // take the opportunity to round the displacement value down to the nearest pixel
      // the browser may thank you for this by not blurring the shit out of your text 
      displacement = (displacement - marquee_el.clientWidth) << 0;

      // by using the variable 'marquee' we created in the parent scope,
      // we can easily use the reference to pause/cancel the animation later if necessary
      marquee = marquee_el.animate([
        // these are your keyframes, if you are familiar with the CSS syntax
        // so your 'from' or '0%' keyframe translates to 'offset: 0'
        // 'to'/'100%' translates to 'offset: 1'
        // and anything in betwen like '54%' will be 'offset: .54'
        { transform: 'matrix(1, 0.00, 0.00, 1, 0, 0)', offset: 0 },
        { transform: 'matrix(1, 0.00, 0.00, 1,' + -displacement + ', 0)', offset: 1 }
        // you don't have to use matrix, I just like it
      ],
      {
        // animation-duration = 1 second for each element in marquee
        // arbitrary decision
        duration: children.length * 4e3,

        // could be 'ease', 'cubic-bezier(.4,0,.2,1)', etc.
        easing: 'linear',

        // useful if you don't want the animation to start until your content has loaded from, say, a REST API and you want to speculate a reasonable time for that to take
        delay: 0,

        // kind of crucial for a marquee...
        iterations: Infinity,

        // invert animation after completion, so it scrolls backwards */
        direction: 'alternate',

        // you would use this if your animation is set to occur only a finite number of times, and you wanted the animated element to finish at the end keyframe, rather than the first keyframe
        fill: 'forwards'
      });
    } 
  }


  // quick check for the WAAPI method
  // you could also do if (typeof grid_to_animate.animate !== 'undefined')
  // but this is cleaner in my opinion
  if ('animate' in marquee_el && typeof marquee_el.animate === 'function') {
    
    // okay, let's fire up the marquee!
    createMarquee();

    // now for the playing/pausing
    marquee_el.addEventListener('mouseenter', pauseMarquee, false);
    marquee_el.addEventListener('mouseleave', playMarquee, false);
    
    // and resizing
    window.addEventListener('resize', debounce( createMarquee ), false);
    
  } else {
      // let's say hello to those using Safari
      // or indeed users of IE, not-recently-updated FF, very old Chrome, old Opera, etc.
      document.querySelector('h1').innerHTML = 'Your browser does not appear to <br> support the Web Animation API';
      document.querySelector('h2').innerHTML = 'So you see a grid of items like this';
  }

  // pretty self-explanatory
  function playMarquee(){
    if( marquee.playState === 'paused' ) marquee.play();
  }
  
  // pretty self-explanatory
  function pauseMarquee(){
    if( marquee.playState === 'running' ) marquee.pause();
  }

  // a debouncing function using requestAnimationFrame
  // this is just an easy-to-use wrapper I like to use for event handlers
  function debounce(func){
    var scheduled, context, args;
    return function(){
      context = this; args = [];
      for(var i = 0; i < arguments.length; ++i) args[i] = arguments[i];
      !!scheduled && window.cancelAnimationFrame(scheduled);
      scheduled = window.requestAnimationFrame(function(){
        func.apply(context, args);
        scheduled = null;
      });
    }
  }
})();
            
          
!
999px
🕑 One or more of the npm packages you are using needs to be built. You're the first person to ever need it! We're building it right now and your preview will start updating again when it's ready.
Loading ..................

Console