Skip to content

Project 06: 轰炸座位表 (JS)

完整代码

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

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="stylesheet" href="./style.css">
  <script src="./index.js"></script>
  <title>轰炸座位表</title>
</head>

<body>
  <main>
    <section id="control">
      <input type="number" id="target" placeholder="人数">
      <button id="start-button">开始</button>
    </section>
    <section id="seat-table"></section>
  </main>
</body>

</html>
css
body {
  margin: 0;
  padding: 0;
}

#control {
  display: flex;
  justify-content: center;
  gap: 1rem;
  margin-top: 1rem;
}

#target {
  width: 4rem;
  text-align: center;
}

#seat-table {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  padding: 1rem 0;
}

.row {
  display: flex;
  justify-content: center;
  gap: 1rem;
}

.seat {
  border: 1px dashed #999999;
  padding: 0.5rem 1rem;
  border-radius: 0.25rem;
  transition: background-color 150ms ease, transform 300ms ease-in-out;
}
javascript
const rows = 8;
const cols = 6;
let target = 1;
const threshold = 4;
const seatTable = document.querySelector("#seat-table");
const heatMap = new Map();

for (let i = 0; i < rows; i++) {
  const row = document.createElement("div");
  row.className = "row";
  seatTable.appendChild(row);
  for (let j = 0; j < cols; j++) {
    const seat = document.createElement("div");
    seat.className = "seat";
    seat.textContent = `${i + 1}/${j + 1}`;
    row.appendChild(seat);
  }
}

document.querySelector("#start-button").addEventListener('click', run);

async function wait(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

async function run() {
  let current = 0;
  const newTarget = parseInt(document.querySelector("#target").value);
  if (!isNaN(newTarget) && newTarget > 0) {
    target = newTarget;
  }

  heatMap.clear();

  for (let i = 0; i < rows; i++) {
    for (let j = 0; j < cols; j++) {
      const seat = seatTable.querySelector(`.row:nth-child(${i + 1}) .seat:nth-child(${j + 1})`);
      seat.style.backgroundColor = "white";
      seat.style.transform = "scale(1)";
    }
  }

  while (current < target) {
    await wait(45);
    const randomRow = Math.floor(Math.random() * rows);
    const randomCol = Math.floor(Math.random() * cols);
    const seat = seatTable.querySelector(`.row:nth-child(${randomRow + 1}) .seat:nth-child(${randomCol + 1})`);
    const id = `${randomRow},${randomCol}`;
    if (!heatMap.has(id)) {
      heatMap.set(id, 0);
    }
    const heat = heatMap.get(id) + 1;
    if (heat > threshold) {
      continue;
    }
    heatMap.set(id, heat);
    const hue = heat === threshold ? 40 : ((heat === threshold - 1) ? 80 : 120);
    const color = `hsl(${hue},50%,${100 - heat * 30 / threshold}%)`;
    seat.style.backgroundColor = color;
    if (heat >= threshold) {
      seat.style.transform = "scale(1.2)";
      current++;
    }
  }

  await wait(300);

  for (let i = 0; i < rows; i++) {
    for (let j = 0; j < cols; j++) {
      const id = `${i},${j}`;
      if (heatMap.get(id) !== threshold) {
        seatTable.querySelector(`.row:nth-child(${i + 1}) .seat:nth-child(${j + 1})`).style.backgroundColor = "white";
      }
    }
  }
}

Built by Vitepress | Apache 2.0 Licensed