Pen Settings

HTML

CSS

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. You can use the CSS from another Pen by using it's URL and the proper URL extention.

+ add another resource

JavaScript

Babel includes JSX processing.

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

Packages

Add Packages

Search for and use JavaScript packages from npm here. By selecting a package, an import statement will be added to the top of the JavaScript editor for this package.

Behavior

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.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

HTML

              
                <html lang="en">
<body>
 	<div id="topnav-container">
    <div id="topnav-animate" class="box-radius"></div>
    <nav id="topnav-menu" class="box-radius">
		  <h1 id="topnav-header" class="fl-l box-radius">Reaction Tester</h1>
     	<button id="topnav-btnStart" class="btn1 fl-r box-radius hvr-float-shadow" type="button">Start</button>
		  <button id="topnav-btnRapid" class="btn1 fl-r box-radius hvr-float-shadow" type="button">Rapid Fire</button>
     </nav>

     <div id="stage">        
        <div id="intro">
           <div id="intro-align" class="fl-l">
              <h2>Reaction Tester</h2>
              <br />
              <div class="fl-l intro-btn2-wrap">
                 <button id="intro-btnStart" class="btn2 box-radius" type="button">Start</button>
              </div>
              <p class="fl-l intro-body">Colorful spheres display one at a time. It is up to you 
                 to click down each orb as fast as you can.&nbsp;&nbsp;Your <strong>best reaction time</strong> is displayed.</p>
              <br class="clear clearfix"/>
              <div class="fl-l intro-btn2-wrap">
                 <button id="intro-btnRapid" class="btn2 box-radius" type="button">Rapid Fire</button>
              </div>
              <p class="fl-r intro-body">Think you are fast?&nbsp;You are given 30 seconds to click on as many
                 orbs as you can.&nbsp;&nbsp;We hope you enjoy this frenzy of action.</p>
           </div>
       </div>       
       <div id="game-over">GAME OVER</div>
     </div> 
 	</div>
</body>
</html>

<!-- END OF DOCUMENT -->
              
            
!

CSS

              
                /*
** reaction.css
**
**  MM/DD/YY		Rev		Who		Description
**  -----------------------------------------------------------------------------------------
**  06/27/16		1.00	KBR		style sheet for reaction tester project
** 
** TableOfContents:
** - declarations: 	css reset, base rules, common classes
** - CSS layout: 	Top Navigation
**					
** Color Table:					
** topnav-menu h1:	Reaction Tester #d9d9d9		Scores #9BA4CC
*/

/*
** CSS RESET - SIMPLIFIED
*/
html,body,div,span,applet,object,iframe,
h1,h2,h3,h4,h5,h6,p,blockquote,pre,
a,abbr,acronym,address,big,cite,code,del,
dfn,em,img,ins,kbd,q,s,samp,small,strike,
strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,
ol,ul,li,fieldset,form,label,input,textarea,button,
legend,table,caption,tbody,tfoot,thead,tr,th,td,
article,aside,canvas,details,figcaption,figure,footer,
header,hgroup,menu,nav,section,summary,time,mark,audio { 
  margin:0; 
  padding:0;
  border:0;
  font-family: 'Merienda', cursive;
  font-size:100%;
  vertical-align:baseline;
}
/*
** HTML5 display-role reset for older browsers 
*/
article, aside, details, figcaption, figure, 
footer, header, hgroup, menu, nav, section {
  display: block;
}
body {
  line-height: 1;
}
ol,ul {
  list-style: none;
}
span {
  color:slateblue;
}

/*
** BASE RULES:
*/

/* 
** Rule: Set BoxModel to include Padding + Borders
** 		 in Box Width and Box Height
*/
html {
  -webkit-box-sizing:border-box; 
     -moz-box-sizing:border-box; 
          box-sizing:border-box; 	
}
* { 
  -webkit-box-sizing:inherit;
     -moz-box-sizing:inherit;
          box-sizing:inherit;
}
html, body {
  margin: 0;
  height: 100%;

}
body {
  background-image: url("../images/GlossyDottedAbstractBackground.jpg");
  background-position: left top;
  background-size: 100% 100%;
  background-repeat: no-repeat;
  overflow:hidden;
}

/*
** COMMON CLASSES:
*/
.clearfix:after { 
   content: "."; 
   visibility: hidden; 
   display: block; 
   height: 0; 
   clear: both;
}
.clear {clear:both;}
.fl-l {float:left;}
.fl-r {float:right;}

/*
**	TOP NAVIGATION
*/
#topnav-container {
	width:100%;
	height:100px;
}

#topnav-animate {
  position:absolute;
  top:30px;
  left:30px;
  height:80px;
  width:80px;
  background-color:#d2d2d2;
  background: radial-gradient(circle at 52px 26px, #d2d2d2, #000000);
  opacity:1;
}

#topnav-menu {
  position:relative;
 	height:80px;
	width:80px;  /* animate width to 1200 in js */
	background-color:white;
	padding:5px 5px;
 	margin: 30px 623px 0 630px; /* animate left/right margins in js */
  opacity:0;
}

#topnav-header {
  width:850px;
  height:100%;
  padding: 25px;
  font-size:20px;
  color:#d9d9d9;
  text-shadow:#2e2e2e 0 2px 3px;
  background: linear-gradient(90deg, rgb(210,210,210) 50%, rgb(255,255,255) 90%);
}

#topnav-btnStart {}
#topnav-btnRapid {}

.box-radius {
	-webkit-border-radius:40px;
       -moz-border-radius:40px;
	        border-radius:40px;
}

.btn1,
.btn2 {
    color: black;
    text-shadow:#2e2e2e 0 2px 2px;
    background:rgb(210, 210, 210);
    padding:5px 25px;
}
.btn1 {
  margin:15px 10px; 
  opacity:0;
}
.btn2 {background-color:#bdbdbd;} /* bizzare. need to redefine bgcolor to match btn1 bgcolor */

/* Float Shadow */
.hvr-float-shadow {
  vertical-align: middle;
  position: relative;
  /* -webkit-transition-duration:0.3s; */
  transition-duration: .3s;
  /* -webkit-transition-property: transform; */ /* .3 second transition of css transform property */
  transition-property: transform;
}
.hvr-float-shadow:before {
  pointer-events:none;
  position:absolute;
  z-index:-1;
  content:'';
  top:100%;    /* push shadow position below element */
  left:5%;     /* center the shadow as width is reduced */
  height:10px; /* shadow height */
  width:90%;   /* shadow width */
  opacity:0;
  /* background:-webkit-radial-gradient(center, ellipse, rgba(0, 0, 0, 0.35) 0%, rgba(0, 0, 0, 0) 80%);*/
  background:radial-gradient(ellipse at center, rgba(0, 0, 0, 0.35) 0%, rgba(0, 0, 0, 0) 80%);
  /* W3C */
  /* -webkit-transition-duration:0.3s; */
  transition-duration:0.3s;
  /* -webkit-transition-property:transform, opacity; */
  transition-property:transform, opacity;
}
.hvr-float-shadow:hover,
.hvr-float-shadow:focus,
.hvr-float-shadow:visited {outline:0;} /* remove box around button */

.hvr-float-shadow:hover{
  -webkit-transform:translateY(-5px);
  transform:translateY(-5px);  /* move the element up by 5px */
}
.hvr-float-shadow:hover:before{
  opacity:1;
  -webkit-transform:translateY(5px);
  transform:translateY(5px);
  /* move the element down by 5px (it will stay in place because it's attached to the element that also moves up 5px) */
}

#stage,
#stage-game {
  position:absolute;
  top:110px;
  left:0;
  width:1280px;
  height:880px;
}

#intro {
  position:absolute;
  display:block;
  vertical-align:middle; 
  top:170px; 
  left:170px;
  height:250px;
  width: 920px;
  padding: 20px;
  
  border-radius: 10px;
  border:5px solid gray;
  opacity: 0.9;
  background-color:white;  /* default incase no gradient support */ 
  background-size: 100% 100%;
  background: linear-gradient(270deg, #ffffff, #739AC5);
  animation: IntroBgColor 9s ease 0 infinite;
}

@keyframes IntroBgColor { 
    0%{background-position:0% 50%}
    50%{background-position:100% 50%}
    100%{background-position:0% 50%}
}

#intro-align {
   position:relative;
   height:100%;
   width:100%; 
   font-size:20px;   
}

.intro-btn2-wrap {
    width: 170px;
    height:100px;
}

.intro-body {
  width:700px;
  display:inline-block;
}

#intro-btnStart,
#intro-btnRapid {
  font-size:20px;
  outline:0;
}

.circle {
  position:absolute;
  display: block;
  background-color: #F00;
  border-radius: 100%;
  height: 300px;
  width: 300px;
  margin: 0;
  background: radial-gradient(circle at 200px 100px, #F00, #000);
  
  /*animation-name: twirl;
  animation-duration: 10s;
  animation-iteration-count: infinite; */
 
}


/* Standard syntax */
@keyframes twirl {
    0% {left:0px; top:0px;}
    25%  {left:1280px; top:0px;transform: rotate(90deg);}
    50%  {left:0px; top:0px;transform: rotate(180deg);}
    75%  {left:0px; top:893px;transform: rotate(270deg);}
    100% {left:0px; top:0px;transform: rotate(360deg);}
}

/* Game Over msg ~220px width, centered dynamically to innerWidth */
#game-over {
  position:absolute;
  top:70px; 
  left:530px;
  display:block;
  vertical-align:middle;
  color: white;
  font-size:40px;
  opacity: 0.9;
}

              
            
!

JS

              
                //
// reaction.js
//

// use myStatic instead of CONST which may become a KEYWORD with ecmascript6
var myStatic = {
   SPACER : "\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0",
   MAX_TARGETS :10,
   GAME_TIME : 30
};

// GLOBAL variables are contained by this object
var glob = {
  createdTime : 0,
  clickedTime : 0,
  reactionTime : 0,
  bestTime : 0,
  gameTimer : 0,   // set from myStatic.GAME_TIME
  gameType : 1,    // 1=Start, 2=Rapid
  hitCount : 0
};

function getRandomInteger(min, max) {
  if (arguments.length < 2) {
    min = 0;
  }
  if (arguments.length < 1) {
    min = 0;
    max = 1;
  }
  return Math.floor(Math.random() * (max - min) + min);
}

function getRandomColor() {

   // Return a color Hex Code that is not too dark
   // 
   // Funny, I don't feel the get RandomInteger is.. well. random enough.
   var i = getRandomInteger(1,9)+1;
   for (;i>0;i--) { 
      var Color3 = getRandomInteger(30,255);
      var Color1 = getRandomInteger(0, 255);
      var Color2 = getRandomInteger(30,255);
   }
   return Color1.toString(16) + Color2.toString(16) + Color3.toString(16);
}

function AnimateTopNavMenu() {

  var topNavAnim = document.getElementById("topnav-animate");
  var btn1 = document.getElementsByClassName("btn1");
  var tl = new TimelineLite();
  
  $("#game-over").hide();

   // These Tweens are chained together and last method in chain requires a semi-colon.  
   // rotate ball to center, then back to edge quickly
   // topnav menu spreads wider
   // Game buttons are faded-in, button fonts swell and retract.
  tl.to (topNavAnim, 2,   { x:600, rotation:360 })
    .to (topNavAnim, 0.5, { x:30,  rotation:360, opacity:0 })
    .to ("#topnav-menu", 0.5, { marginLeft:"30px", marginRight:"23px",
       width:"1190px", opacity:0.9, ease : "easeOut" },"-=0.45")
    .to (btn1, 0.2, { opacity:0.9 })
    .to (btn1, 0.5, { fontSize:24, autoRound:false, ease:"Power4.easeInOut" },"-=0.15")
    .to (btn1, 1.0, { fontSize:20, autoRound:false, ease:"Power3.easeInOut" },"-=0.15");
  return;
}

// Timer Loop that displays Game Time and Stats once per second
function checkEndGame() {

  if (glob.gameTimer > 0) {
     displayScoreTime();
     glob.gameTimer -= 1;
     setTimeout(checkEndGame, 1000); // a recursion loop going on here.
  }
  else {
     displayScoreTime();
     endGame ();
  }
}

function createTargets(index) {

  if (glob.gameType === 1) {
     var myShape = new Shape("circle", index); // fn(shapeType,#ofShapes)
     glob.createdTime = new Date().getTime(); // shape creation Time accurate to a millisecond
     $(myShape.id).show();
     moveTarget(myShape);
  } else {
     var myShape = new Shape("circle", index); // fn(shapeType,#ofShapes)
     $(myShape.id).show();
     moveTarget(myShape);
  }
}
function displayGameScreen(screenType) {
  // Remove Splash Page, Modify Header to show Game Statistics
  if (screenType == 1) {
    $("#intro").hide();
    $("#topnav-btnStart").hide();
    $("#topnav-btnRapid").hide();
    $("#game-over").hide();
    $("h1").css("color", "black");
    if (glob.gameType === 1) {
      $("h1").text("Time Left:" + myStatic.SPACER + "Best:" + myStatic.SPACER + "Reaction Time:" + myStatic.SPACER);
    } else {
      $("h1").text("Time Left:" + myStatic.SPACER + "Hits:" + myStatic.SPACER + "Avg:" + myStatic.SPACER);
    }
    return;
  }
  if (screenType == 2) {
    $("#intro").show();
    $("#topnav-btnStart").show();
    $("#topnav-btnRapid").show();
    $("#game-over").show();
    return;
  }
}

function endGame() {

  $(".circle").remove();
  displayGameScreen(2); // end game
 }

function displayScoreTime() {
  if (glob.gameType === 1) {
     if (glob.bestTime == 0 || glob.reactionTime < glob.bestTime) {
       glob.bestTime = glob.reactionTime;
     }
     $("h1").html("Time Left:<span>" + padNum(glob.gameTimer) + "</span>Best:<span>" + padNum(glob.bestTime) 
        + "</span>Reaction Time:<span>" + padNum(glob.reactionTime) + "</span>");
  }
  if (glob.gameType === 2) {  
     var avg = 0;
     if ( glob.hitCount !== 0 ) {
        avg = (( myStatic.GAME_TIME - glob.gameTimer) / glob.hitCount).toFixed(3);
     }
     $("h1").html("Time Left:<span>" + padNum(glob.gameTimer) + "</span>Hits:<span>" + padNum(glob.hitCount) 
        + "</span>Avg:<span>" + padNum( avg ) + "</span>");
  }
}

function moveTarget (target) {

   TweenLite.defaultEase = Linear.easeNone;
   var tl = new TimelineLite();
   var animTarget = $(target.id);
   var maxX  = window.innerWidth - target.width;
   var maxY = window.innerHeight - 110 - target.height;
   var dirX, dirY = 0;
   var up, right = false;

   // set initial target path   
   if ( getRandomInteger(0,9)+1 > 5 ) {
      dirX = maxX;
      right = true;
   } else {
      dirX = 0;
      right = false;
   }
   if ( getRandomInteger(0,9)+1 > 5 ) {
      dirY = 0;
      up = true;
   } else {
      dirY = maxY;
      up = false;
   }    
   
   var secX = getRandomInteger(180,350)/100; // tween X duration  between 1.8 and 3.5 seconds
   var secY = getRandomInteger(180,350)/100; // random between 1.8 and 3.5 seconds
   
   console.log(target.posX + ", " + target.posY + ", radius: " + target.width ); 
 
   // run animation on initial target path 
   tl.set(animTarget, {x:target.posX, y:target.posY })
     .to(animTarget, secY, {y:dirY, rotation:360}, "animTarget")
     .to(animTarget, secX, {x:dirX}, "animTarget");
     
 //  if ( up === true && right === true ) {
 //      ;
 //  } 

 //  tl.to(animTarget, secX, {x:maxX, rotation:360, repeat:-1, yoyo:true})
 //    .to(animTarget, secY, {y:maxY, repeat:-1, yoyo:true}, 0);
}

function padNum (num) {

  var s = ("       " + num).slice(-7) + "  ";
  return s.replace(/\s/g, "\xa0");
  
}

// Shape constructor
// Circle is the only shape defined at this time, A radial-gradient is offset within
// it to give the circle a 3D feel.
var Shape = function (shape, shapeId) {

  var txt1 = "<div id='shape" + shapeId + "'</div>";

  this.id = "#shape" + shapeId;
  this.index = shapeId;
  // append shape element to DOM if its a NEW shape
  if ($(this.shape).length === 0) {
    $("#stage").append(txt1); //add DIV (shape)
    $(this.id).addClass("circle");
  }
  this.shape = shape;
  this.width = getRandomInteger(70, 250);
  this.height = this.width;
  this.backgroundColor = "#" + getRandomColor();
  // note: x,y is not center of shape, but the top-left corner of it.
  this.posX = getRandomInteger(0, window.innerWidth - this.width);
  this.posY = getRandomInteger(0, window.innerHeight - 110 - this.height);

  
  $(this.id).width(this.width);
  $(this.id).height(this.height);
  var radOffsetX = String(Math.floor(this.width * 0.66)) + "px";
  var radOffsetY = String(Math.floor(this.height * 0.33)) + "px";
  $(this.id).css({
    background : "radial-gradient(circle at " + radOffsetX + " " + radOffsetY +
    ", " + this.backgroundColor + ", #000"
  });
  
  // set click event handler for this object
  while (! $(this.id)) {
      console.log("waiting on DOM");
  }

  var myObj = this;       // 'this' will fall out of scope when constructor completes. save it here.
  $(this.id).on("click", function(e) {
     myObj.clickHandler(myObj);
  });
  $(this.id).on("dblclick", function(e) {
     myObj.clickHandler(myObj);
  });
};

// add prototype Click Handler for Shape
Shape.prototype.clickHandler = function (target) {

  if ( glob.gameType === 1) {
    glob.clickedTime = new Date().getTime();
    glob.reactionTime = (glob.clickedTime - glob.createdTime) / 1000
  } else {
    glob.hitCount++;
  }
  $(target).hide();
  $(target.id).remove();
  
  // FIX: a timing issue here, gameTimer would reach 0 somewhere between the Pause and the 
  //      call to createTargets(). Fixed by checking for remaining time.
   if ( glob.gameType === 1) {
      if (glob.gameTimer >0) {
         setTimeout(function() {
            createTargets(target.index);
         }, getRandomInteger(300,1000)); // Pause
         glob.createdTime = new Date().getTime(); // shape creation Time accurate to a millisecond
      } else {
         displayScoreTime();
         endGame ();
      }         
   }
   if ( glob.gameType === 2) {
      if (glob.gameTimer >0) {
         createTargets(target.index);
      } else {
         displayScoreTime();
         endGame (); 
      }
   }
   // $(target).show();    
};

function startGame(gameSelection) {

  glob.gameType = gameSelection;

  displayGameScreen(1); // new game
  
  var bestTime = 0;
  var gameTimeStart = 0;
  var gameTimeElapsed = 0;
  glob.hitCount = 0;
  gameTimeStart = new Date().getTime();

   if (glob.gameType === 1) {
     index = 1;
     // Pause between 300 to 1000ms before creating a new target 
     setTimeout(function() {
        createTargets(index);
     }, getRandomInteger(300,1000));     
  } else {
     var i=0;
     while (++i <= myStatic.MAX_TARGETS) {
        createTargets(i);
     }
  }
  checkEndGame();
}

console.log("script started, window.innerWidth= " + window.innerWidth +
  " window.innerHeight= " + window.innerHeight);
  
window.addEventListener('load', AnimateTopNavMenu());
window.removeEventListener('load', AnimateTopNavMenu());
/* Event Listener */
$("#topnav-btnStart").on("click", function() {
   glob.gameTimer= myStatic.GAME_TIME;  // set for new game
   startGame(1); 
});
$("#intro-btnStart").on("click", function() {
   glob.gameTimer= myStatic.GAME_TIME;  // set for new game 
   startGame(1);
});
$("#topnav-btnRapid").on("click", function() {
   glob.gameTimer= myStatic.GAME_TIME;  // set for new game
   startGame(2); 
});
$("#intro-btnRapid").on("click", function() { 
   glob.gameTimer= myStatic.GAME_TIME;  // set for new game
   startGame(2); 
});

              
            
!
999px

Console