codepens/css-only-interactive-3d-dice
Dym Sohin f132183c1d init 2023-10-06 23:12:53 +02:00
..
dist init 2023-10-06 23:12:53 +02:00
src init 2023-10-06 23:12:53 +02:00
README.markdown init 2023-10-06 23:12:53 +02:00
license.txt init 2023-10-06 23:12:53 +02:00

README.markdown

CSS only interactive 3D dice

A Pen created on CodePen.io. Original URL: https://codepen.io/HunorMarton/pen/mdERrLy.

Find me @ https://twitter.com/HunorBorbely

A few ideas that I used in this pen:

Interaction

You can write CSS statements that depend on the state of a radio button. Under each "button" there's a set of radio buttons each representing a turn (0°, 90°, 180°, 270°). With CSS I make sure that in every case the radio button leading to the next step is on top.

By default only the first radio buttons is visible (according with the default state). Then on top I show the one leading to the next state.

  • 0° checkbox checked -> Display the checkbox for 90°
  • 90° checkbox checked -> Display the checkbox for 180°
  • 180° checkbox checked -> Display the checkbox for 270°
  • 270° checkbox checked -> Do not display next checkbox but as the 0° is always in the background that will show
.radio {
  display: none;
}

.x-radio[value="1"],
.x-radio[value="1"]:checked ~ .x-radio[value="2"],
.x-radio[value="2"]:checked ~ .x-radio[value="3"],
.x-radio[value="3"]:checked ~ .x-radio[value="4"] {
  display: block;
}

....

Then the rotation of the dice is derived based on the value of these checkboxes by recreating every possible combination like this:

.y-radio[value="1"]:checked
  ~ .z-radio[value="1"]:checked
  ~ .x-radio[value="1"]:checked
  ~ .dice {
  transform: rotateY(0deg)
    rotateZ(0deg)
    rotateX(0deg);
}

.y-radio[value="1"]:checked
  ~ .z-radio[value="1"]:checked
  ~ .x-radio[value="2"]:checked
  ~ .dice {
  transform: rotateY(0deg)
    rotateZ(0deg)
    rotateX(90deg);
}

....

A 3D dice

Making a 3D dice is essentially just 6 divs representing the 6 sides then rotating them in 3D space:

.dice {
  transform-origin: center center;
  transform-style: preserve-3d;

  .box {
    width: 150px;
    height: 150px;
    position: relative;

    & > * {
      position: absolute;
      width: 150px;
      height: 150px;
    }

    .front {
      transform: translateX(-$size / 2) translateZ($size / 2);
    }
    .back {
      transform: translateX(-$size / 2) rotateX(180deg) translateZ($size / 2);
    }
    .right {
      transform: translateX(-$size / 2) rotateY(90deg) translateZ($size / 2);
    }
    .left {
      transform: translateX(-$size / 2) rotateY(-90deg) translateZ($size / 2);
    }
    .top {
      transform: translateX(-$size / 2) rotateX(90deg) translateZ($size / 2);
    }
    .bottom {
      transform: translateX(-$size / 2) rotateX(-90deg) translateZ($size / 2);
    }
  }
}

A circular arrow

The arrow consists of an arrow head and a bunch of tail parts that are individually rotated and spaced with CSS to make it look like a continous circle.

The pug code generating the arrow head and tail parts:

.arrow
  .head
  - var n = 0;
  while n++ < 30
    .tail-part

The CSS code putting the peaces together:

.arrow {
  position: relative;
  transform-origin: center center;
  transform-style: preserve-3d;

  .head {
    position: absolute;
    transform: translateZ($radius);

    ...
  }

  .tail-part {
    position: absolute;

    &:nth-of-type(1) {
      transform: rotateX(1 * -10deg) translateZ(40px);
    }

    &:nth-of-type(2) {
      transform: rotateX(2 * -10deg) translateZ(40px);
    }

    ...
  }
}