JSFiddle - React, Tailwind, and code Playground

Using HTML5 Visibility API with GSAP to keep animations in sync.

by Jennifer Perrin

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.11.4/TweenMax.min.js"></script>
<span id="focus" class="active">TAB FOCUS</span> <span id="blur">TAB BLUR</span> <span id="focusW">WINDOW FOCUS</span> <span id="blurW">WINDOW BLUR</span>

<div id="wrapper">
  <div id="box">
    <div id="spot"></div>
  </div>  
</div>

<p>To Test: 
  <ul>
    <li>First click inside the Preview panel so the page gains focus ( <strong>very imporatnt</strong> )</li>
     <li>Switch between tabs</li>
     <li>Give another program focus and come back to the browser</li>
    <li>Look at the above state changes.</li>
  </ul>
</p>

CSS

body{
  background:#555;
  font-family:Arial;
  color:#999;
}

p{
  margin:160px 0 0 20px;
  font-size:12px;
}

ul{
  font-size:12px;
}

span {
  display:inline-block;
  margin:10px; 
  color:#888;
}

span.active {
  color:#FCC30A;
}

#wrapper {
  position:relative;
  margin:30px 0 0 30px;
}

#box {
  position:absolute;
  width:100px;
  height:100px;
  background:#0CB558;
  border-radius:100%;
}

#spot {
  position:relative;
  width:30px;
  height:30px;
  top:2px;
  left:2px;
  background:#FFF;
  border-radius:100%;
}

JavaScript

var $focus = $("#focus"),
    $blur = $("#blur"),
    $focusW = $("#focusW"),
    $blurW = $("#blurW");

// rotate element
var tl = new TimelineMax({paused:true,repeat:-1});

tl.to("#box",1.5,{
  css:{
      rotation:360
  },
  ease:Linear.easeNone
});
tl.play(0);


/////////////////////////////////////////
// main visibility API function 
// check if current tab is active or not
var vis = (function(){
    var stateKey, 
        eventKey, 
        keys = {
                hidden: "visibilitychange",
                webkitHidden: "webkitvisibilitychange",
                mozHidden: "mozvisibilitychange",
                msHidden: "msvisibilitychange"
    };
    for (stateKey in keys) {
        if (stateKey in document) {
            eventKey = keys[stateKey];
            break;
        }
    }
    return function(c) {
        if (c) document.addEventListener(eventKey, c);
        return !document[stateKey];
    }
})();


/////////////////////////////////////////
// check if current tab is active or not
vis(function(){
					
    if(vis()){	
        
	      setTimeout(function(){ 
          
            // tween resume() code goes here	
            tl.resume();
            
            // add remove active class
            TweenMax.set($focus,{className:'+=active'});
            TweenMax.set($blur,{className:'-=active'});
            TweenMax.set($focusW,{className:'-=active'});
            TweenMax.set($blurW,{className:'-=active'});
          
        },300);		
												
    } else {
	
        // tween pause() code goes here	
        tl.pause();
        
        // add remove active class
        TweenMax.set($blur,{className:'+=active'});
        TweenMax.set($focus,{className:'-=active'});
        TweenMax.set($focusW,{className:'-=active'});
        TweenMax.set($blurW,{className:'-=active'});
    }
});


/////////////////////////////////////////
// check if browser window has focus		
var notIE = (document.documentMode === undefined),
    isChromium =...