JSFiddle - React, Tailwind, and code Playground

by Julian

HTML

<div id="container">
      
      <ul class="nav">
        <li><a data-face="icloud" href="#">iCloud</a></li>
        <li><a data-face="phone" href="#">Phone</a></li>
        <li><a data-face="logo-l" href="#">Logo-l</a></li>
        <li><a data-face="logo-r" href="#">Logo-r</a></li>
        <li><a data-face="apple" href="#">Apple</a></li>
        <li><a data-face="base" href="#">Base</a></li>
      </ul>
      
      <div class="cube">
        
        <div class="face icloud" data-face="icloud">
          
          <section>
            <img src="//placehold.it/300x300/ccc&amp;text=icloud">
          </section>
          
          <div class="face phone" data-face="phone">
            <section>
              <img src="//placehold.it/300x300/555&amp;text=phone">
            </section>
          </div>
          
          <div class="face logo-l" data-face="logo-l">
            <section>
              <img src="//placehold.it/300x300/666&amp;text=logo-l">
            </section>
          </div>
          
          <div class="face logo-r" data-face="logo-r">
            <section>
              <img src="//placehold.it/300x300/333&amp;text=logo-r">
            </section>
            <div class="face apple" data-face="apple">
              <section>
                <img src="//placehold.it/300x300/222&amp;text=apple">
              </section>
            </div>
          </div>
          
          <div class="face base" data-face="base">
            <section>
              <img src="//placehold.it/300x300/000&amp;text=base">
            </section>
          </div>
          
        </div>
        
      </div>
      
    </div>

SCSS

section {
  display: block;
}

$width: 300px;
$height: 300px;

* {
  margin: 0;
  padding: 0;
}

html, body, #container {
  height: 100%;
  overflow: hidden;
}

#container {
  -webkit-perspective: 1000px;
}

.cube {
  width: $width;
  
  height: $height;
  position: absolute;
  top: 20%;
  left: 20%;
  margin-left: 0-$width/2;
  margin-top: 0-$height/2;
  -webkit-transform-style: preserve-3d;
  -webkit-backface-visibility: hidden;
  -webkit-transform-origin: $width/2 $height/2 $width/2*-1;
  -webkit-transform: rotateY(0);
  -webkit-transition: -webkit-transform 0.3s ease;
  -webkit-transition-duration: 0.3s;
  .face {
    position: absolute;
    width: $width;
    height: $height;
    -webkit-transform-origin: 0px 0px;
    -webkit-transform-style: preserve-3d;
    -webkit-backface-visibility: hidden;
    background: rgba(0,0,0,0.1);
    > section {
      
      position: absolute;
      width: $width;
      height: $height;
      outline: 1px solid #000;
    }
  } 
  .icloud {
    .phone {
      
      top: 0 - $height;
      -webkit-transform-origin: 0 300px;
      -webkit-transform: rotateX(90deg);
    }
    .logo-l {
      -webkit-transform: rotateY(-90deg);
      -webkit-transform-origin: 300px 0;
      left: 0 - $width;
    }
    .logo-r {
      -webkit-transform: rotateY(90deg);
      left: $width;
      .apple {
        left: $width;
        -webkit-transform: rotateY(90deg);
      } 
    }
    .base {
      top: $height;
      -webkit-transform: rotateX(-90deg);
    }
  }
}

JavaScript

/*global $:false */
/*global WebKitCSSMatrix:false */
(function(w,d,undefined) {
    
  /**
   * Vector
  * (Helper class)
   */
    var Vector = function(x, y, z) {
        this.x = x;
        this.y = y;
        this.z = z;
    };
  
  // http://blog.joelambert.co.uk/2011/05/10/handling-3d-geometry-with-3d-css3-transforms/
    WebKitCSSMatrix.prototype.transformVector = function(v) {
        var xOut = this.m11*v.x + this.m12*v.y + this.m13*v.z;
        var yOut = this.m21*v.x + this.m22*v.y + this.m23*v.z;
        var zOut = this.m31*v.x + this.m32*v.y + this.m33*v.z;

        return new Vector(xOut, yOut, zOut);
    };
    
    var facingDirection = function(matrix3dString) {
      var values = matrix3dString.match(/\(([\-0-9,.\ ]+)\)/)[1].split(", ");
        if (values[0] != 0)
            return values[0] == -1 ? "-x" : "x";
        if (values[4] != 0)
            return values[4] == -1 ? "y" : "y";
        if (values[8] != 0)
            return values[8] == -1 ? "z" : "z";
    }
    
  /**
   * Cube
   */
  var Cube = function(options) {
    
    var that = this;
    this.$ = options.$;
    this.$faces = options.$faces;
    this.$currentFace = null;
    this.moving = false;
    this.directions = {
      'up': {key: 40},
      'right': {key: 37},
      'down': {key: 38},
      'left': {key: 39}
    };
    
    // Absolute positions
    // (Used for clicking directly to an element)
    this.positions = {
      'icloud': {x: 0, y: 0},
      'phone': {x: 270, y: 0},
      'logo-l': {x: 0, y: 90},
      'logo-r': {x: 0, y: 270},
      'base': {x: 90, y: 0},
      'apple': {x: 0, y: 180}
    };
    
    // Move the cube using a vector
    // (rather than a relative angle)
    $(window).keyup(function(e) {
      var direction;
      if(that.moving === true) {
        return false;
      }
      $.each(that.directions, function(direction) {
        if(e.which === this.key) {
          that[direction].call(that);
        }
      });
    });
    
    // Go to an...