Bouncing Cubes Loader

by Marc Malignan

HTML

<div id="perspective">
    <div id="pane"></div>
</div>
<div id="controls">
    <form>
        <table>
            <tr>
                <td>Cubes</td>
                <td><input id="entities" type="range" min="1" max="12" step="1" value="5"/></td>
                <td>5</td>
            </tr>
            <tr>
                <td>Cube Size</td>
                <td><input id="size" type="range" min="10" max="100" step="5" value="40"/></td>
                <td>40</td>
            </tr>
            <tr>
                <td>Spacing</td>
                <td><input id="spacing" type="range" min="0" max="50" step="1" value="5"/></td>
                <td>5</td>
            </tr>
            <tr>
                <td>Perspective</td>
                <td><input id="pers" type="range" min="100" max="1500" step="50" value="600"/></td>
                <td>600</td>
            </tr>
        </table>
    </form>
</div>
<a id="toggle" class="hide" href="#">Hide</a>

<link href='http://fonts.googleapis.com/css?family=Roboto' rel='stylesheet' type='text/css'>

CSS

@-webkit-keyframes jump {
    0% { top: 0; }
    10% { top: -6px; }
    20% { top: 0; }
}
@keyframes jump {
    0% { top: 0; }
    10% { top: -6px; }
    20% { top: 0; }
}
@-webkit-keyframes shadow {
    0% { box-shadow: 0 0 5px rgba(0,0,0,.8); }
    10% { box-shadow: 3px 3px 10px rgba(0,0,0,.5); }
    20% { box-shadow: 0 0 5px rgba(0,0,0,.8); }
}
@keyframes shadow {
    0% { box-shadow: 0 0 5px rgba(0,0,0,.8); }
    10% { box-shadow: 3px 3px 10px rgba(0,0,0,.5); }
    20% { box-shadow: 0 0 5px rgba(0,0,0,.8); }
}

html {
    height: 100%;
}
body {
    margin: 0;
    background: radial-gradient(#ccc, #54958A, #2C3A4E);
    font-family: 'Lato', sans-serif;
    font-size: 14px;
    overflow: hidden;
}

#perspective {
    position: absolute;
    left:50%; top:50%;
}

#pane {
    width:100%; height:100%;
    transform-style: preserve-3d;
    -webkit-transform: rotateX(45deg) rotateY(180deg) rotateZ(45deg);
    transform: rotateX(45deg) rotateY(180deg) rotateZ(45deg);
}

.line {
    transform-style: preserve-3d;
    -webkit-transform: rotateX(90deg);
    transform: rotateX(90deg);
}
.line:after {
    content: '';
    display: block;
    clear: both;
}

.cube {
    float: left;
    position: relative;
    top: 0;
    transform-style: preserve-3d;
    -webkit-transform: rotateY(90deg);
    transform: rotateY(90deg);
    -webkit-animation: jump 1.6s cubic-bezier(.5,-.5,1,.5) infinite;
    animation: jump 1.6s cubic-bezier(.5,-.5,1,.5) infinite;
}

.cube .face {
    position: absolute;
    width:100%; height:100%;
    background: #2C3A4E;
    box-shadow: inset 0 0 1px #54958A, inset 0 0 20px #465468;
    text-align: center;
    line-height: 40px;
}

.face.back {
    display: none;
	-webkit-transform: rotateY(180deg);
    transform: rotateY(180deg);
}
.face.right {
	-webkit-transform: rotateY(-270deg) translateX(100%);
	-webkit-transform-origin: top right;
    transform: rotateY(-270deg) translateX(100%);
	transform-origin: top right;
}
.face.left {
    display:...

JavaScript

/* use the control panel to change these values */
var nbCubes = null;
var cubeSize = null;
var cubeMargin = null;
var perspective = null;

function initParams() {
    nbCubes = parseInt($('#entities').val());
    cubeSize = parseInt($('#size').val());
    cubeMargin = parseInt($('#spacing').val());
    perspective = parseInt($('#pers').val());
}

function buildCubes() {
    
    var persSize = nbCubes * (cubeSize + cubeMargin*2);
  
    $('#pane')
    	.empty()
    	.css('padding-left', cubeSize+'px')
       .css('margin-left', (-cubeSize/2)+'px');

    $('#perspective')
        .css('width', persSize+'px')
        .css('height', persSize+'px')
        .css('margin-left', (-persSize/2)+'px')
        .css('margin-top', (-persSize/2)+'px')
        .css('-webkit-perspective', perspective)
        .css('perspective', perspective);
    
    var lineDelay = 0;
    var cubeDelay = 0;
    
    for(var i=0; i<nbCubes; i++) {
        var line = $('<div class="line"></div>');
        for(var j=0; j<nbCubes; j++) {
            
            var delay = lineDelay + cubeDelay;
            
            var cube = $('<div class="cube"><div class="face front"></div><div class="face back"></div><div class="face top"></div><div class="face bottom"></div><div class="face left"></div><div class="face right"></div></div>');
            
            cube
                .css('-webkit-animation-delay', delay+'s')
                .css('animation-delay', delay+'s');
            
            line.append(cube);
            cubeDelay += .1;
        }
        $('#pane').append(line);
        lineDelay += .1;
        cubeDelay = 0;
    }
    
    $('.cube')
        .css('width', cubeSize+'px')
        .css('height', cubeSize+'px')
        .css('margin', cubeMargin+'px');
}

initParams();
buildCubes();

$('#controls input').on('input', function(e) {
    var value = $(this).val();
    $(this).parent('td').next('td').html(value);
});

$('#controls input').on('change', function(e) {
   ...