我做的骰子每次都能掷到 6

226 阅读1分钟

前言

今天我们用前端 CSS 技术来做一个骰子。

代码

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>投掷版本</title>
    <style>
      * {
        padding: 0px;
        margin: 0px;
      }

      body {
        background: #000;
      }

      .sides-container {
        perspective: 2000px; /* 定义 3D 元素距视图的距离 */
        perspective-origin: 50% 50%; /* 定义 3D 元素所基于的 X 轴和 Y 轴 */
      }

      .sides {
        width: 300px;
        height: 300px;
        position: relative;
        transform-style: preserve-3d; /* 子元素将保留其 3D 位置 */
        margin: 300px auto;
      }

      .side {
        position: absolute;
        width: 300px;
        height: 300px;
        padding: 20px;
        border-radius: 20px;
        background-color: #fff;
      }

      .point {
        width: 60px;
        height: 60px;
        display: inline-block;
        border-radius: 50%;
      }

      .point-red {
        background-color: red;
      }

      .point-blue {
        background-color: blue;
      }

      .side-one {
        transform: translateZ(-340px);
        display: flex;
        justify-content: center;
        align-items: center;
      }

      .side-two {
        transform-origin: top;
        transform: rotateX(-90deg);
        display: flex;
        justify-content: space-between;
        align-items: center;
      }

      .side-three {
        transform-origin: left;
        transform: rotateY(90deg);
        display: flex;
        justify-content: space-between;
      }
      .side-three .point-two {
        align-self: center;
      }
      .side-three .point-three {
        align-self: flex-end;
      }

      .side-four {
        transform-origin: right;
        transform: rotateY(-90deg);
        display: flex;
        flex-direction: column;
        justify-content: space-between;
      }
      .side-four .column {
        display: flex;
        justify-content: space-between;
      }

      .side-five {
        transform-origin: bottom;
        transform: rotateX(90deg);
        display: flex;
        flex-direction: column;
        justify-content: space-between;
      }
      .side-five .column {
        display: flex;
        justify-content: space-between;
      }
      .side-five .two {
        justify-content: center;
      }

      .side-six {
        display: flex;
        flex-direction: column;
        justify-content: space-between;
      }
      .side-six .column {
        display: flex;
        justify-content: space-around;
      }

      #throw-text {
        color: #fff;
        cursor: pointer;
      }
    </style>
  </head>
  <body>
    <div id="throw-text">
      开始投掷
    </div>
    <div class="sides-container">
      <div class="sides">
        <div class="side side-one">
          <span class="point point-red"></span>
        </div>
        <div class="side side-two">
          <span class="point point-blue"></span>
          <span class="point point-blue"></span>
        </div>
        <div class="side side-three">
          <span class="point point-blue"></span>
          <span class="point point-blue point-two"></span>
          <span class="point point-blue point-three"></span>
        </div>
        <div class="side side-four">
          <div class="column">
            <span class="point point-red"></span>
            <span class="point point-red"></span>
          </div>
          <div class="column">
            <span class="point point-red"></span>
            <span class="point point-red"></span>
          </div>
        </div>
        <div class="side side-five">
          <div class="column">
            <span class="point point-blue"></span>
            <span class="point point-blue"></span>
          </div>
          <div class="column two">
            <span class="point point-blue"></span>
          </div>
          <div class="column">
            <span class="point point-blue"></span>
            <span class="point point-blue"></span>
          </div>
        </div>
        <div class="side side-six">
          <div class="column">
            <span class="point point-blue"></span>
            <span class="point point-blue"></span>
          </div>
          <div class="column">
            <span class="point point-blue"></span>
            <span class="point point-blue"></span>
          </div>
          <div class="column">
            <span class="point point-blue"></span>
            <span class="point point-blue"></span>
          </div>
        </div>
      </div>
    </div>
    <script>
      const getKeyframes = () => {
        const getDeg = () => {
          return Math.floor(Math.random() * 360);
        };

        const getTransform = percentage => {
          return `${percentage}% {
          transform: rotateY(${getDeg()}deg) rotateX(${getDeg()}deg);
        }`;
        };

        const percentageList = [0, 20, 40, 60, 80, 100];

        // 设置各关键帧骰子的转动位置
        let keyframes = `@keyframes keyframes {`;
        percentageList.forEach(percentage => {
          keyframes += getTransform(percentage);
        });
        keyframes += "}";

        return keyframes;
      };

      // 将关键帧样式添加到 html 中
      const setKeyframesStyle = () => {
        const keyframesStyleId = "keyframes";

        const keyframesStyle = document.getElementById("keyframesStyleId");
        if (keyframesStyle) {
          keyframesStyle.remove();
        }

        const style = document.createElement("style");
        style.id = keyframesStyleId;
        style.innerText = getKeyframes();
        document.getElementsByTagName("head")[0].appendChild(style);
      };

      document.getElementById("throw-text").addEventListener("click", () => {
        setKeyframesStyle();

        const sidesElement = document.getElementsByClassName("sides")[0];

        const time = 0.5;
        const count = 2;
        // 设置动画
        sidesElement.style.animation = `keyframes ${time}s linear 0s ${count}`;
        setTimeout(() => {
          sidesElement.style.animation = undefined;
        }, time * count * 1000);
      });
    </script>
  </body>
</html>

总结

涉及到的主要 CSS 知识有:

  • 3D 视图(perspective)
  • 2D/3D 转换(transform)
  • flex 布局