JSFiddle - React, Tailwind, and code Playground

by levchenko_d

HTML

<div id="section">
<div id="test"></div>  
<div id="test2"></div>  
<div id="test3"></div>  
<div id="test4"></div>  
</div>


<div id="section2" class="s2">
<div id="test5" class="s2-box"></div>  
<div id="test6" class="s2-box"></div>  
<div id="test7" class="s2-box"></div> 
<div id="test8" class="s2-box"></div>
<div id="test9" class="s2-box"></div>  
<div id="test10" class="s2-box"></div>  
<div id="test11" class="s2-box"></div> 
<div id="test12" class="s2-box"></div>
</div>

CSS

#test,#test2,#test3,#test4{
  position: relative;
  height: 50px;
  width: 50px;
  background: red;
  left: 100px;
  top: -100px;
}

#test2{
  background: blue;
  left: 300px;
  top: 100px; 
}

#test3{
  background: green;
}


#test4{
  background: purple;
  top: 0;
  position: absolute;
}

#section{
  position: relative;
  height: 500px;
  background: silver;
  margin-top: 700px;
  overflow: hidden;
}

body{
  min-height: 2000px;
}


.s2 {
  margin-top: 400px;
  padding: 20px 50px;
  position: relative;
  overflow: hidden;
  background: #f2f2f2;
}

.s2:after{
  content: '';
  display: table;
  clear: both;
}

.s2-box{
  float: left;
  margin: 20px calc((100% - (50px * 4)) / 8);
  width: 50px;
  height: 50px;
  background: #00b0ef;
  border-radius: 12px;
  box-shadow: 0 1px 2px rgba(0,0,0,0.3);
}

JavaScript

/*
	@options.element{DOM}(Required)[] - animation target
  @options.section{DOM}(Optional)[] - parent section of @element. If set, then scroll coordinates will be relative to @section
  @options.values{[Object]}(Optional)[[]] - [start, end] properties of css transform values 
 	@options.velocityFn{Function}(Optional)[] - custom velocity function
*/

if ( !window.requestAnimationFrame ) {

	window.requestAnimationFrame = ( function() {

		return window.webkitRequestAnimationFrame ||
		window.mozRequestAnimationFrame ||
		window.oRequestAnimationFrame ||
		window.msRequestAnimationFrame ||
		function( /* function FrameRequestCallback */ callback, /* DOMElement Element */ element ) {

			window.setTimeout( callback, 1000 / 60 );

		};

	} )();

}

function parseIntSafe(n){
  return (parseInt(n, 10)||0);
}

function viewportHeight(){
  return Math.max(parseIntSafe(document.documentElement.clientHeight), parseIntSafe(window.innerHeight) || 0) + 'px';
}

function viewportWidth(){
  return Math.max(parseIntSafe(document.documentElement.clientWidth), parseIntSafe(window.innerWidth) || 0) + 'px';
}

function getScrollTop(obj){
  if(obj === document.body){
    return document.documentElement.scrollTop || document.body.scrollTop;
  } else if(obj === window){
    return window.scrollY;
  } else {
    return obj.scrollTop;
  }
}

function getPosition(element){
  var rect = element.getBoundingClientRect(),
      body = document.body,
      docElem = document.documentElement,
      scrollTop = window.pageYOffset || docElem.scrollTop || body.scrollTop,
      scrollLeft = window.pageXOffset || docElem.scrollLeft || body.scrollLeft,
      clientTop = docElem.clientTop || body.clientTop || 0,
      clientLeft = docElem.clientLeft || body.clientLeft || 0,
      x = rect.left + scrollLeft - clientLeft,
      y  = rect.top +  scrollTop - clientTop;

    return {
      x: Math.round(x),
      y: Math.round(y)
    };
}

function getSize(element){
  return {
    x :...