3
\$\begingroup\$

I'm working on a personal game project to hone my CSS, HTML, and vanilla JS skills. I would appreciate any feedback on my small dice game project.

ceelo-js Codepen

The main logic of the game is run by this function:

getScore: function(results) {
let message = '';
if (results.toString() === WIN) {
  message = `${WIN} You Win!`;
} else if (results.toString() === LOSS) {
  message = `${LOSS} You Lose`;
} else if (results[0] === results[1] && results[1] === results[2]) {
  message = `Trips! ${results[0]}`;
} else if (results[0] === results[1]) {
  message = `You scored: ${results[2]}`;
} else if (results[1] === results[2]) {
  message = `You scored: ${results[0]}`;
} else {
  message = 'Roll again...';
}
return message;
},

Essentially a player rolls until two dice match, the outlier is the player's score. Special cases are: '1,2,3': instant loss '4,5,6': instant win If a player rolls all the same number, the opponent would have to roll three matching numbers that are higher than the first player's score i.e.('4,4,4' > '1,1,1'). The only roll that can beat'6,6,6' is '4,5,6'.

I still need to add the logic for playing against a human or NPC.

Here is the full code:

index.html

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8" />
  <meta name="description" content="Ceelo: Three Dice" />
  <title>Ceelo</title>
  <link rel="stylesheet" href="styles.css" />
  <link rel="stylesheet" href="dice.css">
  <link rel="stylesheet" href="animations.css">
</head>

<body>
  <div class="score">Let's Roll</div>
  <div class="scene">
    <div class="dice-display">
      <div class="dice dice-one idle">
        <div class="face face-front">
          <div class="front-pip-1">&#11044</div>
          <div class="front-pip-2">&#11044</div>
          <div class="front-pip-3">&#11044</div>
        </div>
        <div class="face face-back">
          <div class="back-pip-1">&#11044</div>
          <div class="back-pip-2">&#11044</div>
          <div class="back-pip-3">&#11044</div>
          <div class="back-pip-4">&#11044</div>
        </div>
        <div class="face face-right">
          <div class="right-pip-1">&#11044</div>
          <div class="right-pip-2">&#11044</div>
          <div class="right-pip-3">&#11044</div>
          <div class="right-pip-4">&#11044</div>
          <div class="right-pip-5">&#11044</div>
        </div>
        <div class="face face-left">
          <div class="left-pip-1">&#11044</div>
          <div class="left-pip-2">&#11044</div>
        </div>
        <div class="face face-top">
          <div class="top-pip-1">&#11044</div>
          <div class="top-blank-1"></div>
          <div class="top-blank-2"></div>
        </div>
        <div class="face face-bottom">
          <div class="bottom-pip-1">&#11044</div>
          <div class="bottom-pip-2">&#11044</div>
          <div class="bottom-pip-3">&#11044</div>
          <div class="bottom-pip-4">&#11044</div>
          <div class="bottom-pip-5">&#11044</div>
          <div class="bottom-pip-6">&#11044</div>
        </div>
      </div>
    </div>
    <div class="dice-display">
      <div class="dice dice-two idle">
        <div class="face face-front">
          <div class="front-pip-1">&#11044</div>
          <div class="front-pip-2">&#11044</div>
          <div class="front-pip-3">&#11044</div>
        </div>
        <div class="face face-back">
          <div class="back-pip-1">&#11044</div>
          <div class="back-pip-2">&#11044</div>
          <div class="back-pip-3">&#11044</div>
          <div class="back-pip-4">&#11044</div>
        </div>
        <div class="face face-right">
          <div class="right-pip-1">&#11044</div>
          <div class="right-pip-2">&#11044</div>
          <div class="right-pip-3">&#11044</div>
          <div class="right-pip-4">&#11044</div>
          <div class="right-pip-5">&#11044</div>
        </div>
        <div class="face face-left">
          <div class="left-pip-1">&#11044</div>
          <div class="left-pip-2">&#11044</div>
        </div>
        <div class="face face-top">
          <div class="top-pip-1">&#11044</div>
          <div class="top-blank-1"></div>
          <div class="top-blank-2"></div>
        </div>
        <div class="face face-bottom">
          <div class="bottom-pip-1">&#11044</div>
          <div class="bottom-pip-2">&#11044</div>
          <div class="bottom-pip-3">&#11044</div>
          <div class="bottom-pip-4">&#11044</div>
          <div class="bottom-pip-5">&#11044</div>
          <div class="bottom-pip-6">&#11044</div>
        </div>
      </div>
    </div>
    <div class="dice-display">
      <div class="dice dice-three idle">
        <div class="face face-front">
          <div class="front-pip-1">&#11044</div>
          <div class="front-pip-2">&#11044</div>
          <div class="front-pip-3">&#11044</div>
        </div>
        <div class="face face-back">
          <div class="back-pip-1">&#11044</div>
          <div class="back-pip-2">&#11044</div>
          <div class="back-pip-3">&#11044</div>
          <div class="back-pip-4">&#11044</div>
        </div>
        <div class="face face-right">
          <div class="right-pip-1">&#11044</div>
          <div class="right-pip-2">&#11044</div>
          <div class="right-pip-3">&#11044</div>
          <div class="right-pip-4">&#11044</div>
          <div class="right-pip-5">&#11044</div>
        </div>
        <div class="face face-left">
          <div class="left-pip-1">&#11044</div>
          <div class="left-pip-2">&#11044</div>
        </div>
        <div class="face face-top">
          <div class="top-pip-1">&#11044</div>
          <div class="top-blank-1"></div>
          <div class="top-blank-2"></div>
        </div>
        <div class="face face-bottom">
          <div class="bottom-pip-1">&#11044</div>
          <div class="bottom-pip-2">&#11044</div>
          <div class="bottom-pip-3">&#11044</div>
          <div class="bottom-pip-4">&#11044</div>
          <div class="bottom-pip-5">&#11044</div>
          <div class="bottom-pip-6">&#11044</div>
        </div>
      </div>
    </div>
  </div>
  <div class="controls">
    <button id="roll">Roll</button>
  </div>
  <script src="main.js"></script>
</body>

</html>

main.js

'using-strict';

const LOSS = '1,2,3';
const WIN = '4,5,6';
const INITIAL_TRANSFORM_STATE = 'idle'

var roll = function() {
  let sides = 6;
  return Math.floor(sides * Math.random()) + 1;
};

const view = {
  roll: document.getElementById('roll'),
  dice1: {o: document.querySelector('.dice-one'), state: INITIAL_TRANSFORM_STATE},
  dice2: {o: document.querySelector('.dice-two'), state: INITIAL_TRANSFORM_STATE},
  dice3: {o: document.querySelector('.dice-three'), state: INITIAL_TRANSFORM_STATE},
  score: document.querySelector('.score'),
  updateDice: function(results) {

    handleDiceAnimation(this.dice1);
    handleDiceAnimation(this.dice2);
    handleDiceAnimation(this.dice3);

    setTimeout(()=>{
      showResult(this.dice1, results[0]);
      showResult(this.dice2, results[1]);
      showResult(this.dice3, results[2]);
    },820)
  },
  updateScore: function(message) {
    this.score.textContent = message;
  },
};

const game = {
  turn: function() {
    let rollResult = [roll(), roll(), roll()];
    let resultSorted = [...rollResult].sort(); // sort results and return new array
    view.updateDice(rollResult);
    setTimeout(()=>{
      view.updateScore(this.getScore(resultSorted));
    }, 1000);
  },
  getScore: function(results) {
    let message = '';
    if (results.toString() === WIN) {
      message = `${WIN} You Win!`;
    } else if (results.toString() === LOSS) {
      message = `${LOSS} You Lose`;
    } else if (results[0] === results[1] && results[1] === results[2]) {
      message = `Trips! ${results[0]}`;
    } else if (results[0] === results[1]) {
      message = `You scored: ${results[2]}`;
    } else if (results[1] === results[2]) {
      message = `You scored: ${results[0]}`;
    } else {
      message = 'Roll again...';
    }
    return message;
  },
};

view.roll.addEventListener('click', () => game.turn(), false);

var handleDiceAnimation = dice => {
   if (dice.state === 'idle'){
      dice.o.classList.remove('idle');
    }

    dice.o.classList.remove('spin');
    void dice.o.offsetWidth;
    dice.o.classList.add('spin');
}

var showResult = (dice, value) => {
  dice.o.classList.remove(dice.state);
  void dice.o.offsetWidth;

  if (value === 1) {
    dice.o.classList.add('show-top');
    dice.state = 'show-top';
  } else if (value === 2) {
    dice.o.classList.add('show-left');
    dice.state = 'show-left';
  } else if (value === 3) {
    dice.o.classList.add('show-front');
    dice.state = 'show-front';
  } else if (value === 4) {
    dice.o.classList.add('show-back');
    dice.state = 'show-back';
  } else if (value === 5) {
    dice.o.classList.add('show-right');
    dice.state = 'show-right';
  } else if (value === 6) {
    dice.o.classList.add('show-bottom');
    dice.state = 'show-bottom';
  }
}

animations.css

.idle {
  animation: idle linear infinite 6s;
}

@keyframes idle {
    from {
        transform: translateZ(-75px) rotateX(0deg) rotateY(0deg);
    }

    to {
        transform: translateZ(-75px) rotateX(360deg) rotateY(360deg);
    }
}

.spin {
    animation: spin 0.8s linear 1;
}

@keyframes spin {
  0% {
    -webkit-transform: translateZ(-75px) scale(1) rotate3d(-1, 1, 0, 0deg);
            transform: translateZ(-75px) scale(1) rotate3d(-1, 1, 0, 0deg);
  }
  50% {
    -webkit-transform: translateZ(-75px) scale(1.4) rotate3d(-1, 1, 0, 180deg);
            transform: translateZ(-75px) scale(1.4) rotate3d(-1, 1, 0, 180deg);
  }
  100% {
    -webkit-transform: translateZ(-75px) scale(1) rotate3d(-1, 1, 0, 360deg);
            transform: translateZ(-75px) scale(1) rotate3d(-1, 1, 0, 360deg);
  }
}

.show-top    { transform: translateZ(-75px) rotateX(-90deg); }
.show-front  { transform: translateZ(-75px) rotateY(   0deg); }
.show-right  { transform: translateZ(-75px) rotateY( -90deg); }
.show-back   { transform: translateZ(-75px) rotateY(-180deg); }
.show-left   { transform: translateZ(-75px) rotateY(  90deg); }
.show-top    { transform: translateZ(-75px) rotateX( -90deg); }
.show-bottom { transform: translateZ(-75px) rotateX(  90deg); }

dice.css

.dice-display {
    width: 150px;
    height: 150px;
    perspective: 450px;
}

.dice {
    width: 100%;
    height: 100%;
    position: relative;
    transform-style: preserve-3d;
    transform: translateZ(-75px);
    transition: transform 0.4s;
}

.dice-one {
}

.dice-two {
}

.dice-three {
}

.face {
    position: absolute;
    width: 150px;
    height: 150px;
    display: grid;
    grid-template-columns: 50px, 50px, 50px;
    grid-template-rows: 50px, 50px, 50px;
    border: 1px solid hsla(0, 100%, 50%, 0.2);
    color: whitesmoke;
    font-size: 24pt;
}

/* 3 */
.face-front {
    background: hsla(0, 100%, 50%, 0.6);
    transform: rotateY(0deg) translateZ(75px);
}

.front-pip-1 {
    grid-column: 3 / 4;
    grid-row: 1 / 2;
    place-self: center;
}
.front-pip-2 {
    grid-column: 2 / 3;
    grid-row: 2 / 3;
    place-self: center;
}
.front-pip-3 {
    grid-column: 1 / 2;
    grid-row: 3 / 4;
    place-self: center;
}

/* 4 */
.face-back {
    background: hsla(0, 100%, 50%, 0.6);
    transform: rotateY(180deg) translateZ(75px);
}

.back-pip-1 {
    grid-column: 1 / 2;
    grid-row: 1 / 2;
    place-self: center;
}

.back-pip-2 {
    grid-column: 3 / 4;
    grid-row: 1 / 2;
    place-self: center;
}

.back-pip-3 {
    grid-column: 1 / 2;
    grid-row: 3 / 4;
    place-self: center;
}

.back-pip-4 {
    grid-column: 3 / 4;
    grid-row: 3 / 4;
    place-self: center;
}

/* 5 */
.face-right {
    background: hsla(0, 100%, 50%, 0.6);
    transform: rotateY(90deg) translateZ(75px);
}

.right-pip-1 {
    grid-column: 1 / 2;
    grid-row: 1 / 2;
    place-self: center;
}

.right-pip-2 {
    grid-column: 3 / 4;
    grid-row: 1 / 2;
    place-self: center;
}

.right-pip-3 {
    grid-column: 2 / 3;
    grid-row: 2 / 3;
    place-self: center;
}

.right-pip-4 {
    grid-column: 1 / 2;
    grid-row: 3 / 4;
    place-self: center;
}

.right-pip-5 {
    grid-column: 3 / 4;
    grid-row: 3 / 4;
    place-self: center;
}

/* 2 */
.face-left {
    background: hsla(0, 100%, 50%, 0.6);
    transform: rotateY(-90deg) translateZ(75px);
}

.left-pip-1 {
    grid-column: 3 / 4;
    grid-row: 1 / 2;
    place-self: center;
}

.left-pip-2 {
    grid-column: 1 / 2;
    grid-row: 3 / 4;
    place-self: center;
}

/* 1 */
.face-top {
    background: hsla(0, 100%, 50%, 0.6);
    transform: rotateX(90deg) translateZ(75px);
}

.top-pip-1 {
    grid-column: 2 / 3;
    grid-row: 2 / 3;
    place-self: center;
}

.top-blank-1 {
    grid-column: 1 / 4;
    grid-row: 1 / 2;
    place-self: center;
}

.top-blank-2 {
    grid-column: 1 / 4;
    grid-row: 3 / 4;
    place-self: center;
}

/* 6 */
.face-bottom {
    background: hsla(0, 100%, 50%, 0.6);
    transform: rotateX(-90deg) translateZ(75px);
}

.bottom-pip-1 {
    grid-column: 1 / 2;
    grid-row: 1 / 2;
    place-self: center;
}

.bottom-pip-2 {
    grid-column: 1 / 2;
    grid-row: 2 / 3;
    place-self: center;
}

.bottom-pip-3 {
    grid-column: 1 / 2;
    grid-row: 3 / 4;
    place-self: center;
}

.bottom-pip-4 {
    grid-column: 3 / 4;
    grid-row: 1 / 2;
    place-self: center;
}

.bottom-pip-5 {
    grid-column: 3 / 4;
    grid-row: 2 / 3;
    place-self: center;
}

.bottom-pip-6 {
    grid-column: 3 / 4;
    grid-row: 3 / 4;
    place-self: center;
}

styles.css

* {
  box-sizing: border-box;
}

body {
  background: repeating-linear-gradient(#071a1e 0%, #274249) fixed;
  display: grid;
  grid-template-columns: 20% auto 20%;
  grid-template-rows: 25% auto 25%;
}

.score {
  grid-column: 2 / 3;
  grid-row: 1 / 2;
  place-self: center;
  color: gainsboro;
  font-size: 30pt;
  margin-bottom: 15px;
}

.scene {
  display: flex;
  justify-content: space-evenly;
  grid-column: 2 / 3;
  grid-row: 2 / 3;
}


.controls {
  grid-column: 2 / 3;
  grid-row: 3 / 4;
  place-self: center;
}

#roll {
  margin-top: 50px;
}
```
\$\endgroup\$
1
\$\begingroup\$

Code style

  • Its "use strict"; not 'using-strict';
  • Use object function shorthand for object functions. eg you use { foo: function() {},}can be {foo(){},}
  • Avoid long lists of if ... else if ... by using lookups. (see example)
  • When defining functions that are available across the current scope, if they are arrow functions make them constants const name = (foo) => {} and put them at the top of the scope (manually hoist them). Or use a function declaration that is automatically hoisted for you. function name(foo){}.

  • Avoid using function expressions eg var roll = function() { should be function roll() {

  • When you have variables that only differ by a post-fixed number, dice1, dice2, dice3 that is a good sign that they can be better handled in an array. You can still give them names, but keeping an array referencing the same can simplify the code at times.

  • Use variable aliases to reduce noisy and verbose code. For example in the function game.getScore you are repeatedly referencing by literal index into results. If you assigned 3 aliases the code becomes a lot cleaner See example game.roll line starts with const [r1, r2, r3] =...

  • Why the expression void dice.o.offsetWidth? It does nothing but evaluate to undefined. You may as well have added the line undefined; in its place.

Code logic and design

Your encapsulation of the abstracts used in the game can be improved.

The fundamental working unit is the dice yet you define its behavior all over the place rather than creating an object to encapsulate its state and behavior.

Then you have the view and the game with roles that are a little vague. view calling functions outside its encapsulation, and game creating a (view-able) message rather than setting a state that view would convert to a message.

CSS, HTML, and animation

I noticed that as the page width gets small the dice are overlapping. Adding a margin to the dice would separate them, however that would mean you have to change the layout a little.

For this reason and more, I am not reviewing the CSS and HTML as personally animating anything but the most basic FX is severally limiting when done via CSS and HTML. I would have opted for WebGL to render and JS to define and animate as it provides much more flexibility and quality, though I understand its not everyone's cup of tea.

Example

I have rewritten your code to better encapsulate the data and behavior. As an example only and is not the only way to go about it. It's main purpose is to give examples of the points in this answer.

"use strict";
const LOSS = '123', WIN = '456';
const FACE_NAMES = ",top,left,front,back,right,bottom".split(",");
const dice = name => {
  const el = document.querySelector(".dice-" + name);
  const animate = name => (el.classList.remove(state), el.classList.add(name), name);
  var value, state = "idle";          
  return {
    get val() { return "" + value },
    roll() { value = Math.floor(6 * Math.random()) + 1 },
    show() { state = animate("show-" + FACE_NAMES[value]) },
    spin() { state = animate("spin") },    
  };
}
const game = {
  score: document.querySelector('.score'),
  dice: [dice("one"), dice("two"), dice("three")],
  roll() { 
    game.callEach("roll");
    game.callEach("spin");
    setTimeout(game.callEach ,820, "show");
    const [r1,r2,r3] = [game.dice[0].val, game.dice[1].val, game.dice[2].val].sort();
    if (r1 + r2 + r3 === WIN) { return `${WIN} You Win!` }
    if (r1 + r2 + r3 === LOSS) { return `${LOSS} You lose!` }
    if (r1 === r2 && r2 === r3) { return `Trips! ${r1}` }
    if (r1 === r2) { return  `You scored: ${r3}` }
    if (r3 === r2) { return  `You scored: ${r1}` }
    return  "Roll Again";
  },
  callEach(call) { game.dice.forEach(dice => dice[call]()) },
  turn() {
    const message = game.roll();
    setTimeout(() => game.score.textContent = message, 1000);
  }
};
document.getElementById('roll').addEventListener('click', game.turn);
* {
  box-sizing: border-box;
}

body {
  background: repeating-linear-gradient(#071a1e 0%, #274249) fixed;
  display: grid;
  grid-template-columns: 20% auto 20%;
  grid-template-rows: 25% auto 25%;
}

.score {
  grid-column: 2 / 3;
  grid-row: 1 / 2;
  place-self: center;
  color: gainsboro;
  font-size: 30pt;
  margin-bottom: 15px;
}

.scene {
  display: flex;
  justify-content: space-evenly;
  grid-column: 2 / 3;
  grid-row: 2 / 3;
}


.controls {
  grid-column: 2 / 3;
  grid-row: 3 / 4;
  place-self: center;
}

#roll {
  margin-top: 50px;
}
.dice-display {
    width: 150px;
    height: 150px;
    perspective: 450px;
    margin:26px;
}

.dice {
    width: 100%;
    height: 100%;
    position: relative;
    transform-style: preserve-3d;
    transform: translateZ(-75px);
    transition: transform 0.4s;
}

.dice-one {
}

.dice-two {
}

.dice-three {
}

.face {
    position: absolute;
    width: 150px;
    height: 150px;
    display: grid;
    grid-template-columns: 50px, 50px, 50px;
    grid-template-rows: 50px, 50px, 50px;
    border: 1px solid hsla(0, 100%, 50%, 0.2);
    color: whitesmoke;
    font-size: 24pt;
}

/* 3 */
.face-front {
    background: hsla(0, 100%, 50%, 0.6);
    transform: rotateY(0deg) translateZ(75px);
}

.front-pip-1 {
    grid-column: 3 / 4;
    grid-row: 1 / 2;
    place-self: center;
}
.front-pip-2 {
    grid-column: 2 / 3;
    grid-row: 2 / 3;
    place-self: center;
}
.front-pip-3 {
    grid-column: 1 / 2;
    grid-row: 3 / 4;
    place-self: center;
}

/* 4 */
.face-back {
    background: hsla(0, 100%, 50%, 0.6);
    transform: rotateY(180deg) translateZ(75px);
}

.back-pip-1 {
    grid-column: 1 / 2;
    grid-row: 1 / 2;
    place-self: center;
}

.back-pip-2 {
    grid-column: 3 / 4;
    grid-row: 1 / 2;
    place-self: center;
}

.back-pip-3 {
    grid-column: 1 / 2;
    grid-row: 3 / 4;
    place-self: center;
}

.back-pip-4 {
    grid-column: 3 / 4;
    grid-row: 3 / 4;
    place-self: center;
}

/* 5 */
.face-right {
    background: hsla(0, 100%, 50%, 0.6);
    transform: rotateY(90deg) translateZ(75px);
}

.right-pip-1 {
    grid-column: 1 / 2;
    grid-row: 1 / 2;
    place-self: center;
}

.right-pip-2 {
    grid-column: 3 / 4;
    grid-row: 1 / 2;
    place-self: center;
}

.right-pip-3 {
    grid-column: 2 / 3;
    grid-row: 2 / 3;
    place-self: center;
}

.right-pip-4 {
    grid-column: 1 / 2;
    grid-row: 3 / 4;
    place-self: center;
}

.right-pip-5 {
    grid-column: 3 / 4;
    grid-row: 3 / 4;
    place-self: center;
}

/* 2 */
.face-left {
    background: hsla(0, 100%, 50%, 0.6);
    transform: rotateY(-90deg) translateZ(75px);
}

.left-pip-1 {
    grid-column: 3 / 4;
    grid-row: 1 / 2;
    place-self: center;
}

.left-pip-2 {
    grid-column: 1 / 2;
    grid-row: 3 / 4;
    place-self: center;
}

/* 1 */
.face-top {
    background: hsla(0, 100%, 50%, 0.6);
    transform: rotateX(90deg) translateZ(75px);
}

.top-pip-1 {
    grid-column: 2 / 3;
    grid-row: 2 / 3;
    place-self: center;
}

.top-blank-1 {
    grid-column: 1 / 4;
    grid-row: 1 / 2;
    place-self: center;
}

.top-blank-2 {
    grid-column: 1 / 4;
    grid-row: 3 / 4;
    place-self: center;
}

/* 6 */
.face-bottom {
    background: hsla(0, 100%, 50%, 0.6);
    transform: rotateX(-90deg) translateZ(75px);
}

.bottom-pip-1 {
    grid-column: 1 / 2;
    grid-row: 1 / 2;
    place-self: center;
}

.bottom-pip-2 {
    grid-column: 1 / 2;
    grid-row: 2 / 3;
    place-self: center;
}

.bottom-pip-3 {
    grid-column: 1 / 2;
    grid-row: 3 / 4;
    place-self: center;
}

.bottom-pip-4 {
    grid-column: 3 / 4;
    grid-row: 1 / 2;
    place-self: center;
}

.bottom-pip-5 {
    grid-column: 3 / 4;
    grid-row: 2 / 3;
    place-self: center;
}

.bottom-pip-6 {
    grid-column: 3 / 4;
    grid-row: 3 / 4;
    place-self: center;
}
.idle {
  animation: idle linear infinite 6s;
}

@keyframes idle {
    from {
        transform: translateZ(-75px) rotateX(0deg) rotateY(0deg);
    }

    to {
        transform: translateZ(-75px) rotateX(360deg) rotateY(360deg);
    }
}

.spin {
	animation: spin 0.8s linear 1;
}

@keyframes spin {
  0% {
    -webkit-transform: translateZ(-75px) scale(1) rotate3d(-1, 1, 0, 0deg);
            transform: translateZ(-75px) scale(1) rotate3d(-1, 1, 0, 0deg);
  }
  50% {
    -webkit-transform: translateZ(-75px) scale(1.4) rotate3d(-1, 1, 0, 180deg);
            transform: translateZ(-75px) scale(1.4) rotate3d(-1, 1, 0, 180deg);
  }
  100% {
    -webkit-transform: translateZ(-75px) scale(1) rotate3d(-1, 1, 0, 360deg);
            transform: translateZ(-75px) scale(1) rotate3d(-1, 1, 0, 360deg);
  }
}

.show-top    { transform: translateZ(-75px) rotateX(-90deg); }
.show-front  { transform: translateZ(-75px) rotateY(   0deg); }
.show-right  { transform: translateZ(-75px) rotateY( -90deg); }
.show-back   { transform: translateZ(-75px) rotateY(-180deg); }
.show-left   { transform: translateZ(-75px) rotateY(  90deg); }
.show-top    { transform: translateZ(-75px) rotateX( -90deg); }
.show-bottom { transform: translateZ(-75px) rotateX(  90deg); }
<div class="score">Let's Roll</div>
  <div class="scene">
    <div class="dice-display">
      <div class="dice dice-one idle">
        <div class="face face-front">
          <div class="front-pip-1">&#11044</div>
          <div class="front-pip-2">&#11044</div>
          <div class="front-pip-3">&#11044</div>
        </div>
        <div class="face face-back">
          <div class="back-pip-1">&#11044</div>
          <div class="back-pip-2">&#11044</div>
          <div class="back-pip-3">&#11044</div>
          <div class="back-pip-4">&#11044</div>
        </div>
        <div class="face face-right">
          <div class="right-pip-1">&#11044</div>
          <div class="right-pip-2">&#11044</div>
          <div class="right-pip-3">&#11044</div>
          <div class="right-pip-4">&#11044</div>
          <div class="right-pip-5">&#11044</div>
        </div>
        <div class="face face-left">
          <div class="left-pip-1">&#11044</div>
          <div class="left-pip-2">&#11044</div>
        </div>
        <div class="face face-top">
          <div class="top-pip-1">&#11044</div>
          <div class="top-blank-1"></div>
          <div class="top-blank-2"></div>
        </div>
        <div class="face face-bottom">
          <div class="bottom-pip-1">&#11044</div>
          <div class="bottom-pip-2">&#11044</div>
          <div class="bottom-pip-3">&#11044</div>
          <div class="bottom-pip-4">&#11044</div>
          <div class="bottom-pip-5">&#11044</div>
          <div class="bottom-pip-6">&#11044</div>
        </div>
      </div>
    </div>
    <div class="dice-display">
      <div class="dice dice-two idle">
        <div class="face face-front">
          <div class="front-pip-1">&#11044</div>
          <div class="front-pip-2">&#11044</div>
          <div class="front-pip-3">&#11044</div>
        </div>
        <div class="face face-back">
          <div class="back-pip-1">&#11044</div>
          <div class="back-pip-2">&#11044</div>
          <div class="back-pip-3">&#11044</div>
          <div class="back-pip-4">&#11044</div>
        </div>
        <div class="face face-right">
          <div class="right-pip-1">&#11044</div>
          <div class="right-pip-2">&#11044</div>
          <div class="right-pip-3">&#11044</div>
          <div class="right-pip-4">&#11044</div>
          <div class="right-pip-5">&#11044</div>
        </div>
        <div class="face face-left">
          <div class="left-pip-1">&#11044</div>
          <div class="left-pip-2">&#11044</div>
        </div>
        <div class="face face-top">
          <div class="top-pip-1">&#11044</div>
          <div class="top-blank-1"></div>
          <div class="top-blank-2"></div>
        </div>
        <div class="face face-bottom">
          <div class="bottom-pip-1">&#11044</div>
          <div class="bottom-pip-2">&#11044</div>
          <div class="bottom-pip-3">&#11044</div>
          <div class="bottom-pip-4">&#11044</div>
          <div class="bottom-pip-5">&#11044</div>
          <div class="bottom-pip-6">&#11044</div>
        </div>
      </div>
    </div>
    <div class="dice-display">
      <div class="dice dice-three idle">
        <div class="face face-front">
          <div class="front-pip-1">&#11044</div>
          <div class="front-pip-2">&#11044</div>
          <div class="front-pip-3">&#11044</div>
        </div>
        <div class="face face-back">
          <div class="back-pip-1">&#11044</div>
          <div class="back-pip-2">&#11044</div>
          <div class="back-pip-3">&#11044</div>
          <div class="back-pip-4">&#11044</div>
        </div>
        <div class="face face-right">
          <div class="right-pip-1">&#11044</div>
          <div class="right-pip-2">&#11044</div>
          <div class="right-pip-3">&#11044</div>
          <div class="right-pip-4">&#11044</div>
          <div class="right-pip-5">&#11044</div>
        </div>
        <div class="face face-left">
          <div class="left-pip-1">&#11044</div>
          <div class="left-pip-2">&#11044</div>
        </div>
        <div class="face face-top">
          <div class="top-pip-1">&#11044</div>
          <div class="top-blank-1"></div>
          <div class="top-blank-2"></div>
        </div>
        <div class="face face-bottom">
          <div class="bottom-pip-1">&#11044</div>
          <div class="bottom-pip-2">&#11044</div>
          <div class="bottom-pip-3">&#11044</div>
          <div class="bottom-pip-4">&#11044</div>
          <div class="bottom-pip-5">&#11044</div>
          <div class="bottom-pip-6">&#11044</div>
        </div>
      </div>
    </div>
  </div>
  <div class="controls">
    <button id="roll">Roll</button>
  </div>

\$\endgroup\$
  • \$\begingroup\$ Thanks for the detailed answer! I will take your review under heavy consideration. In regards to your question about void dice.o.offsetWidth , its a way to restart a css animation by triggering a reflow. link \$\endgroup\$ – Arban Nichols May 5 at 19:53
  • 1
    \$\begingroup\$ @ArbanNichols Thought that a little strange you where not removing dice.o.state from the class list in handleDiceAnimation . I have updated the answer to have a running example. Now also remembers "spin" as a state and you will see that you do not need it (voiding property) as removing the "spin" class means you are causing a reflow when it is added back on click \$\endgroup\$ – Blindman67 May 5 at 21:52
  • \$\begingroup\$ const animate = name => (el.classList.remove(state), el.classList.add(name), name); I'm lost on this line of code. Is it a list of function calls? \$\endgroup\$ – Arban Nichols May 6 at 1:59
  • 1
    \$\begingroup\$ @ArbanNichols I am using a comma operator developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… to separate 3 expressions, the last , name is the returned value. \$\endgroup\$ – Blindman67 May 6 at 2:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.