菜单

szdxdxdx
szdxdxdx
发布于 2024-01-07 / 97 阅读
0
0

音乐唱片

介绍

使用 CSS 绘制一张唱片

点击唱片,唱片旋转并播放音乐

如果唱片没有旋转,则说明你当前使用的浏览器不支持 CSS 旋转动画。本示例暂不考虑 CSS 的兼容性问题

效果展示

<style>
  /* config */
  #ccc.record-player {
    --record-radius: 42vmin;
  }

  /* 唱片机容器 */
  #ccc.record-player {
    --record-diameter: calc(2 * var(--record-radius));
    --record-grooves-width: max(0.1px, calc(var(--record-radius) / 100));
    --record-shadow: calc(4 * var(--record-grooves-width));
    --record-img-radius: calc(0.5 * var(--record-radius));

    position: relative;
    width: calc(var(--record-diameter) + 2 * var(--tonearm-width));
    height: calc(var(--record-diameter) + 2 * var(--tonearm-width));

    border-radius: calc(0.5 * var(--padding-width));
  }

  /* 唱片 [begin] */
  #ccc.record-player .record-container {
    padding: var(--record-shadow);
    display: block;
    overflow: hidden;
    background:
      radial-gradient(
        #00000055 var(--record-radius),
        transparent calc(var(--record-radius) + var(--record-shadow))
      );
  }
  #ccc.record-player .record {
    width: var(--record-diameter);
    height: var(--record-diameter);

    box-sizing: border-box;
    border: var(--record-grooves-width) solid #000000;
    border-radius: 50%;
    background:
      conic-gradient(
        #00000022 15%,
        #2a2a2a22 25%,
        #00000022 35%,
        #00000022 60%,
        #2a2a2a22 75%,
        #00000022 85%
      ),
      radial-gradient(
        #0a0a0aff 41%,
        #44444422 41%,
        #44444422 48%,
        #20202022 48%,
        #20202022 64%,
        #4a4a4a22 64%
      ),
      repeating-radial-gradient(
        #000000 0,
        #000000 calc(0.5 * var(--record-grooves-width)),
        #1a1a1a calc(0.5 * var(--record-grooves-width)),
        #1a1a1a var(--record-grooves-width)
      ),
      #000000;
  }
  #ccc.record-player .record:hover {
    cursor: pointer;
  }
  /* 唱片 [end] */

  /* 唱片封面 */
  #ccc.record-player .record img {
    position: absolute;
    left: 0;
    right: 0;
    top: 0;
    bottom: 0;
    margin: auto;

    width: calc(2 * var(--record-img-radius));
    height: calc(2 * var(--record-img-radius));
    box-sizing: border-box;

    object-fit: cover;
    user-select: none;
    pointer-events: none;

    border-radius: 50%;
    border: calc(2.5 * var(--record-grooves-width)) solid #a78d0b;
    background-color: #222;
  }

  /* 亮光 */
  #ccc.record-player .light {
    position: absolute;
    left: 0;
    top: 0;

    width: calc(var(--record-diameter) + 2 * var(--record-shadow));
    height: calc(var(--record-diameter) + 2 * var(--record-shadow));

    pointer-events: none;

    border-radius: 50%;
    mask-image: radial-gradient(
      transparent var(--record-img-radius),
      #000000 0
    );
    background:
      conic-gradient(
        #ffffff01 25%,
        #ffffff22 35%,
        transparent 45%,
        transparent 70%,
        #ffffff22 85%,
        #ffffff01 95%
      );
  }
</style>

<style>
  /* 唱片旋转 [begin] */
  @keyframes record-rotation-animation {
    from {
      transform: rotate(0deg);
    }
    to {
      transform: rotate(360deg);
    }
  }
  .record-player .record {
    --record-animation: record-rotation-animation 4s linear infinite;
  }
  .record-player:has(input[type="checkbox"]:checked) .record {
    animation: var(--record-animation);
  }
  .record-player:has(input[type="checkbox"]:not(:checked)) .record {
    animation: var(--record-animation) paused;
  }
  /* 唱片旋转 [end] */
</style>

<div><p>
    选择歌曲:
    <select id="selector" style="min-width: 12em; border: 1px solid #f60; outline: none;"></select>
    更多我的音乐作品 -> 
    <a href="https://space.bilibili.com/387314477/channel/seriesdetail?sid=3957845&ctype=0" target="_Blank">我的 bilibili 个人空间</a>
    </p>
    </div>

<div style="margin: auto; width: fit-content;"><div class="record-player" id="ccc">
    <label class="record-container">
      <input type="checkbox" hidden="">
      <div class="record">
        <img src="" draggable="false">
      </div>
      <div class="light"></div>
    </label>
    <audio>
      <source src="" type="audio/mpeg">
    </audio></div></div>

<script>
(function() {
  const record_player = document.getElementById("ccc");
  const checkbox = record_player.querySelector("input[type='checkbox']");
  const img = record_player.querySelector("img");
  const audio = record_player.querySelector("audio");
  const select = document.getElementById("selector")

  function reset() {
    audio.currentTime = 0;
    audio.pause();
    checkbox.checked = false;
  }

  audio.addEventListener("timeupdate", () => {
    if (audio.currentTime >= audio.duration)
      reset()
  });

  checkbox.addEventListener("change", () => {
    if (checkbox.checked)
      audio.play();
    else
      audio.pause();
  });

  const music_list = [
    {
      "name": "【全是爱】嫣汐 & 琴歌",
      "img": `/upload/%5B封面%5D%5B全是爱%5D%5B琴歌%20嫣汐%5D%5B袅袅虚拟歌手%20Muta%5D%5B2023-12-31%5D.bmp`,
      "audio": `/upload/%5B翻唱%5D%5B全是爱%5D%5B琴歌%20嫣汐%5D%5B袅袅虚拟歌手%20Muta%5D%5B2023-12-31%5D.mp3`
    }, {
      "name": "【错错错】嫣汐 & 琉璃",
      "img": `/upload/%5B封面%5D%5B错错错%5D%5B嫣汐%20琉璃%5D%5BMuta%20AISingers%5D%5B2024-05-06%5D.bmp`,
      "audio": `/upload/%5B翻唱%5D%5B错错错%5D%5B嫣汐%20琉璃%5D%5BMuta%20AISingers%5D%5B2024-05-06%5D.mp3`
    }, {
      "name": "【无法原谅】嫣汐",
      "img": `/upload/%5B封面%5D%5B无法原谅%5D%5B嫣汐%5D%5BAISingers%20Muta%5D%5B2022-01-21%5D.jpg`,
      "audio": `/upload/%5B翻唱%5D%5B无法原谅%5D%5B嫣汐%5D%5BAISingers%20Muta%5D%5B2022-01-21%5D.mp3`
    }, {
      "name": "【游山恋】心华",
      "img": `/upload/%5B封面%5D%5B游山恋%5D%5B心华%5D%5BVocaloid4%5D%5B2023-07-17%5D.jpg`,
      "audio": `/upload/%5B翻唱%5D%5B游山恋%5D%5B心华%5D%5BVocaloid4%5D%5B2023-07-17%5D.mp3`
    }
  ];

  for (let i = 0; i < music_list.length; i++) {
    const music = music_list[i];
    const option = document.createElement("option");
    option.textContent = music["name"];
    option.value = i;
    select.appendChild(option);
  }

  select.addEventListener("change", (e) => {
    const music = music_list[select.selectedIndex];
    reset();
    img.src = music["img"];
    audio.src = music["audio"];
  })

  select.selectedIndex = 0;
  select.dispatchEvent(new Event("change"));
})();
</script>

代码实现

你需要自定义 ./img.png./music.mp3

<!DOCTYPE html>
<html lang="zh-CN">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>唱片</title>

  <style>
    body {
      margin: 0;
      padding: 0;
      width: 100vw;
      height: 100vh;
      background-color: #f0f0f0;
    }
  </style>

  <style>
    .record-player {
      --record-radius: 45vmin;
    }
    
    /* 唱片机容器 */
    .container {
      margin: auto;
      width: fit-content;
    }

    .record-player {
      --record-diameter: calc(2 * var(--record-radius));
      --record-grooves-width: max(0.1px, calc(var(--record-radius) / 100));
      --record-shadow: calc(4 * var(--record-grooves-width));
      --record-img-radius: calc(0.5 * var(--record-radius));

      position: relative;
      width: calc(var(--record-diameter) + 2 * var(--tonearm-width));
      height: calc(var(--record-diameter) + 2 * var(--tonearm-width));

      border-radius: calc(0.5 * var(--padding-width));
    }

    /* 唱片 [begin] */
    .record-player .record-container {
      padding: var(--record-shadow);
      display: block;
      overflow: hidden;
      background:
        radial-gradient(
          #00000055 var(--record-radius),
          transparent calc(var(--record-radius) + var(--record-shadow))
        );
    }
    .record-player .record {
      width: var(--record-diameter);
      height: var(--record-diameter);

      box-sizing: border-box;
      border: var(--record-grooves-width) solid #000000;
      border-radius: 50%;
      background:
        conic-gradient(
          #00000022 15%,
          #2a2a2a22 25%,
          #00000022 35%,
          #00000022 60%,
          #2a2a2a22 75%,
          #00000022 85%
        ),
        radial-gradient(
          #0a0a0aff 41%,
          #44444422 41%,
          #44444422 48%,
          #20202022 48%,
          #20202022 64%,
          #4a4a4a22 64%
        ),
        repeating-radial-gradient(
          #000000 0,
          #000000 calc(0.5 * var(--record-grooves-width)),
          #1a1a1a calc(0.5 * var(--record-grooves-width)),
          #1a1a1a var(--record-grooves-width)
        ),
        #000000;
    }
    .record-player .record:hover {
      cursor: pointer;
    }
    /* 唱片 [end] */

    /* 唱片封面 */
    .record-player .record img {
      position: absolute;
      left: 0;
      right: 0;
      top: 0;
      bottom: 0;
      margin: auto;

      width: calc(2 * var(--record-img-radius));
      height: calc(2 * var(--record-img-radius));
      box-sizing: border-box;

      object-fit: cover;
      user-select: none;
      pointer-events: none;

      border-radius: 50%;
      border: calc(2.5 * var(--record-grooves-width)) solid #a78d0b;
      background-color: #222;
    }

    /* 亮光 */
    .record-player .light {
      position: absolute;
      left: 0;
      top: 0;

      width: calc(var(--record-diameter) + 2 * var(--record-shadow));
      height: calc(var(--record-diameter) + 2 * var(--record-shadow));

      pointer-events: none;

      border-radius: 50%;
      mask-image: radial-gradient(
        transparent var(--record-img-radius),
        #000000 0
      );
      background:
        conic-gradient(
          #ffffff01 25%,
          #ffffff22 35%,
          transparent 45%,
          transparent 70%,
          #ffffff22 85%,
          #ffffff01 95%
        );
    }
  </style>

  <style>
    /* 唱片旋转 [begin] */
    @keyframes record-rotation-animation {
      from {
        transform: rotate(0deg);
      }
      to {
        transform: rotate(360deg);
      }
    }
    .record-player .record {
      --record-animation: record-rotation-animation 4s linear infinite;
    }
    .record-player:has(input[type="checkbox"]:checked) .record {
      animation: var(--record-animation);
    }
    .record-player:has(input[type="checkbox"]:not(:checked)) .record {
      animation: var(--record-animation) paused;
    }
    /* 唱片旋转 [end] */
  </style>
</head>

<body>
  <div class="container">

    <div class="record-player" id="ccc">
      <label class="record-container">
        <input type="checkbox" hidden>
        <div class="record">
          <img src="./img.png" draggable="false">
        </div>
        <div class="light"></div>
      </label>
      <audio>
        <source src="./music.mp3" type="audio/mpeg">
      </audio>
    </div>

  </div>

  <script>
    const record_player = document.getElementById("ccc");
    const checkbox = record_player.querySelector("input[type='checkbox']");
    const audio = record_player.querySelector("audio");

    audio.load();

    audio.addEventListener("timeupdate", () => {
      if (audio.currentTime >= audio.duration) {
        audio.currentTime = 0;
        audio.pause();
        checkbox.checked = false;
      }
    });

    checkbox.addEventListener("change", () => {
      if (checkbox.checked) {
        audio.play();
      } else {
        audio.pause();
      }
    });
  </script>
</body>

</html>

评论