<div class="scene"></div>
<script src="https://code.jquery.com/jquery-3.1.0.slim.min.js"></script>
<script src="https://ricostacruz.com/jquery.transit/jquery.transit.min.js"></script>
html, body {
  padding: 0;
  margin: 0;
  width: 100%;
  height: 100%;
}

body::after {
  font-family: "operator mono", "sans-serif";
  content: "Hover over a block...";
  position: absolute;
  top: 25px;
  left: 25px;
}

.scene {
  position: absolute;
  left: 50%;
  top: 50%;
  margin: -192px 0 0 -192px;
  width: 384px;
  height: 384px;
  background: rgba(100, 100, 255, 0.2);
  transform: rotateX(60deg) rotateZ(60deg);
  transform-style: preserve-3d;
  transform-origin: center;
}

.block {
  position: absolute;
  left: 0;
  top: 0;
  width: 64px;
  height: 64px;
  transform-style: preserve-3d;
  transform-origin: 50% 50% 50%;
}

.x-axis,
.y-axis,
.z-axis {
  position: absolute;
  left: 0;
  top: 0;
  width: 66px;
  height: 66px;
  transform-origin: 50% 50% 50%;
  pointer-events: none;
}

.x-axis {
  /* border: solid 2px rgba(255, 0, 0, 0.3); */
}

.y-axis {
  /* border: solid 2px rgba(0, 255, 0, 0.3); */
}

.z-axis {
  /* border: solid 2px rgba(0, 0, 255, 0.3); */
}

.side {
  position: absolute;
  left: 0;
  top: 0;
  width: 64px;
  height: 64px;
  backface-visibility: hidden;
  outline: 1px solid rgba(0, 0, 0, 0.3);
}

.block:hover .side {
  outline: 1px solid rgba(0, 255, 0, 0.5);
}

.ghost {
  pointer-events: none;
}

.ghost .side {
  opacity: 0.6;
  pointer-events: none;
  -webkit-filter: brightness(1.5);
}
"use strict"

class Block {
  constructor(x, y, z) {
    this.x = x;
    this.y = y;
    this.z = z;

    this.build();
  }

  build() {
    const size = 64;
    const x = this.x * 64;
    const y = this.y * 64;
    const z = this.z * 64;

    const block = this.block = $(`<div class="block" />`)
      .css({
        transform: `
          translateX(${x}px)
          translateY(${y}px)
          translateZ(${z}px)
          scale(0.99)
        `
      });

    $(`<div class="x-axis" />`)
      .appendTo(block)
      .css({
        transform: `
          rotateX(90deg)
          rotateY(0deg)
          rotateZ(0deg)
        `
      });

    $(`<div class="y-axis" />`)
      .appendTo(block)
      .css({
        transform: `
          rotateX(0deg)
          rotateY(90deg)
          rotateZ(0deg)
        `
      });

    $(`<div class="z-axis" />`)
      .appendTo(block);
    
    this
      .createFace("top", 0, 0, size / 2, 0, 0, 0)
      .appendTo(block);

    this
      .createFace("side-1", 0, size / 2, 0, 270, 0, 0)
      .appendTo(block);

    this
      .createFace("side-2", size / 2, 0, 0, 0, 90, 0)
      .appendTo(block);

    this
      .createFace("side-3", 0, size / -2, 0, -270, 0, 0)
      .appendTo(block);

    this
      .createFace("side-4", size / -2, 0, 0, 0, -90, 0)
      .appendTo(block);

    this
      .createFace("bottom", 0, 0, size / -2, 0, 180, 0)
      .appendTo(block);
  }

  createFace(type, x, y, z, rx, ry, rz) {
    return $(`<div class="side side-${type}" />`)
      .css({
        transform: `
          translateX(${x}px)
          translateY(${y}px)
          translateZ(${z}px)
          rotateX(${rx}deg)
          rotateY(${ry}deg)
          rotateZ(${rz}deg)
        `,
        background: this.createTexture(type)
      })
      .data("block", this)
      .data("type", type);
  }

  createTexture(type) {
    return `rgba(100, 100, 255, 0.2)`;
  }
}

const DIRT_TEXTURES = {
  "top": [
    "https://github.com/assertchris-tutorials/tutorial-javascript-minecraft-editor/raw/master/textures/dirt-top-1.png",
    "https://github.com/assertchris-tutorials/tutorial-javascript-minecraft-editor/raw/master/textures/dirt-top-2.png",
    "https://github.com/assertchris-tutorials/tutorial-javascript-minecraft-editor/raw/master/textures/dirt-top-3.png"
  ],
  "side": [
    "https://github.com/assertchris-tutorials/tutorial-javascript-minecraft-editor/raw/master/textures/dirt-side-1.png",
    "https://github.com/assertchris-tutorials/tutorial-javascript-minecraft-editor/raw/master/textures/dirt-side-2.png",
    "https://github.com/assertchris-tutorials/tutorial-javascript-minecraft-editor/raw/master/textures/dirt-side-3.png",
    "https://github.com/assertchris-tutorials/tutorial-javascript-minecraft-editor/raw/master/textures/dirt-side-4.png",
    "https://github.com/assertchris-tutorials/tutorial-javascript-minecraft-editor/raw/master/textures/dirt-side-5.png"
  ]
};

class Dirt extends Block {
  createTexture(type) {
    if (type === "top" || type === "bottom") {
      const texture = DIRT_TEXTURES.top.random();

      return `url(${texture})`;
    }

    const texture = DIRT_TEXTURES.side.random();

    return `url(${texture})`;
  }
}

Block.Dirt = Dirt;

Array.prototype.random = function() {
  return this[Math.floor(Math.random() * this.length)];
};

const $scene = $(".scene");
const $body = $("body");

for (let x = 0; x < 6; x++) {
  for (let y = 0; y < 6; y++) {
    let next = new Block.Dirt(x, y, 0);
    next.block.appendTo($scene);
  }
}

function createCoordinatesFrom(side, x, y, z) {
  if (side == "top") {
    z += 1;
  }

  if (side == "side-1") {
    y += 1;
  }

  if (side == "side-2") {
    x += 1;
  }

  if (side == "side-3") {
    y -= 1;
  }

  if (side == "side-4") {
    x -= 1;
  }

  if (side == "bottom") {
    z -= 1;
  }

  return [x, y, z];
}

$body.on("click", ".side", function(e) {
  const $this = $(this);
  const previous = $this.data("block");

  const coordinates = createCoordinatesFrom(
    $this.data("type"),
    previous.x,
    previous.y,
    previous.z
  );

  const next = new Block.Dirt(...coordinates);

  next.block.appendTo($scene);
});

let ghost = null;

function removeGhost() {
  if (ghost) {
    ghost.block.remove();
    ghost = null;
  }
}

function createGhostAt(x, y, z) {
  const next = new Block.Dirt(x, y, z);

  next.block
    .addClass("ghost")
    .appendTo($scene);

  ghost = next;
}

$body.on("mouseenter", ".side", function(e) {
  removeGhost();

  const $this = jQuery(this);
  const previous = $this.data("block");

  const coordinates = createCoordinatesFrom(
    $this.data("type"),
    previous.x,
    previous.y,
    previous.z
  );

  createGhostAt(...coordinates);
});

$body.on("mouseleave", ".side", function(e) {
  removeGhost()
});
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.