Quay số

Cập nhật: 01/02/2025
Lượt xem: 0

<!DOCTYPE html>
<html lang="vi">
<head>
  <meta charset="UTF-8">
  <title>Ứng dụng Quay Số Trúng Thưởng</title>
  <style>
    /* Reset & cơ bản */
    body {
      font-family: Arial, sans-serif;
      background: #f4f4f4;
      margin: 0;
      padding: 20px;
    }
    h1, h2 {
      text-align: center;
    }
    .container {
      display: flex;
      flex-wrap: wrap;
      justify-content: space-around;
    }
    .controls, .wheel-container, .history {
      background: #fff;
      padding: 15px;
      margin: 10px;
      border-radius: 5px;
      box-shadow: 0 2px 5px rgba(0,0,0,0.1);
    }
    .controls {
      flex: 1 1 300px;
      max-width: 350px;
    }
    .wheel-container {
      flex: 1 1 400px;
      text-align: center;
    }
    .history {
      flex: 1 1 300px;
      max-height: 300px;
      overflow-y: auto;
    }
    canvas {
      border: 2px solid #333;
      border-radius: 50%;
      background: #eee;
    }
    .result {
      margin-top: 15px;
      font-size: 1.2em;
      font-weight: bold;
      text-align: center;
      color: #007700;
    }
    input[type="text"] {
      width: 80%;
      padding: 5px;
      margin-bottom: 10px;
    }
    input[type="range"] {
      width: 100%;
    }
    button {
      padding: 7px 15px;
      margin: 5px 0;
      cursor: pointer;
    }
    .prize-list {
      list-style: none;
      padding: 0;
      max-height: 150px;
      overflow-y: auto;
      border: 1px solid #ccc;
      margin-bottom: 10px;
    }
    .prize-list li {
      padding: 5px;
      border-bottom: 1px dashed #ddd;
      display: flex;
      justify-content: space-between;
      align-items: center;
    }
    .prize-list li:last-child {
      border-bottom: none;
    }
    .prize-list button {
      font-size: 0.8em;
      margin-left: 5px;
    }
    /* Hiệu ứng nhấp nháy cho phần thắng */
    @keyframes blink {
      0% { opacity: 1; }
      50% { opacity: 0.2; }
      100% { opacity: 1; }
    }
  </style>
</head>
<body>
  <h1>Ứng dụng Quay Số Trúng Thưởng</h1>
  <div class="container">
    <!-- Phần điều khiển nhập đối tượng và cài đặt thời gian quay -->
    <div class="controls">
      <h2>Quản lý Đối tượng Quay</h2>
      <input type="text" id="prizeInput" placeholder="Nhập đối tượng/quà thưởng">
      <button id="addPrizeBtn">Thêm đối tượng</button>
      <ul id="prizeList" class="prize-list"></ul>
      
      <h2>Thiết lập Thời gian Quay</h2>
      <label for="spinTimeSlider">Thời gian quay (giây): <span id="spinTimeDisplay">5</span></label>
      <input type="range" id="spinTimeSlider" min="5" max="8" step="0.1" value="5">
      <br>
      <button id="spinBtn">Quay Ngay!</button>
      <div class="result" id="resultDisplay"></div>
    </div>
    
    <!-- Phần hiển thị bánh xe quay -->
    <div class="wheel-container">
      <h2>Bánh Xe May Mắn</h2>
      <canvas id="wheelCanvas" width="400" height="400"></canvas>
    </div>
    
    <!-- Lịch sử quay -->
    <div class="history">
      <h2>Lịch Sử Quay</h2>
      <ul id="historyList"></ul>
    </div>
  </div>
  
  <!-- Các thẻ âm thanh: Hãy thay đổi src nếu cần -->
  <audio id="tickSound" src="tick.mp3"></audio>
  <audio id="winSound" src="win.mp3"></audio>
  
  <script>
  (function() {
    // Các biến toàn cục
    let prizes = []; // Danh sách đối tượng/quà thưởng (dạng string)
    const canvas = document.getElementById('wheelCanvas');
    const ctx = canvas.getContext('2d');
    const spinBtn = document.getElementById('spinBtn');
    const spinTimeSlider = document.getElementById('spinTimeSlider');
    const spinTimeDisplay = document.getElementById('spinTimeDisplay');
    const resultDisplay = document.getElementById('resultDisplay');
    const prizeInput = document.getElementById('prizeInput');
    const addPrizeBtn = document.getElementById('addPrizeBtn');
    const prizeList = document.getElementById('prizeList');
    const historyList = document.getElementById('historyList');
    const tickSound = document.getElementById('tickSound');
    const winSound = document.getElementById('winSound');
    
    let currentAngle = 0;       // Góc hiện tại của bánh xe (radians)
    let targetAngle = 0;        // Góc mục tiêu sau khi quay
    let initialAngle = 0;       // Góc ban đầu trước khi quay
    let startTime = null;       // Thời điểm bắt đầu quay (timestamp)
    let spinDuration = 0;       // Thời gian quay (giây) do slider chỉ định
    let spinning = false;       // Cờ cho biết đang quay hay không
    let winningIndex = null;    // Vị trí đối tượng trúng thưởng
    let lastTickIndex = -1;     // Để phát âm thanh tick mỗi khi thay đổi phần

    // Cập nhật giá trị hiển thị của slider
    spinTimeSlider.addEventListener('input', function() {
      spinTimeDisplay.textContent = spinTimeSlider.value;
    });

    // Cập nhật danh sách đối tượng/quà thưởng trong giao diện
    function updatePrizeList() {
      prizeList.innerHTML = '';
      prizes.forEach((prize, index) => {
        const li = document.createElement('li');
        li.textContent = prize;
        // Nút Sửa
        const editBtn = document.createElement('button');
        editBtn.textContent = "Sửa";
        editBtn.onclick = function() {
          const newVal = prompt("Sửa đối tượng", prize);
          if (newVal !== null && newVal.trim() !== "") {
            prizes[index] = newVal.trim();
            updatePrizeList();
            drawWheel();
          }
        };
        // Nút Xóa
        const delBtn = document.createElement('button');
        delBtn.textContent = "Xóa";
        delBtn.onclick = function() {
          prizes.splice(index, 1);
          updatePrizeList();
          drawWheel();
        };
        li.appendChild(editBtn);
        li.appendChild(delBtn);
        prizeList.appendChild(li);
      });
    }
    
    addPrizeBtn.addEventListener('click', function() {
      const val = prizeInput.value.trim();
      if (val !== "") {
        prizes.push(val);
        prizeInput.value = "";
        updatePrizeList();
        drawWheel();
      }
    });

    // Hàm vẽ bánh xe quay dựa trên danh sách đối tượng
    // Nếu truyền highlight=true và blink=true, thì đối tượng trúng thưởng sẽ được highlight nhấp nháy
    function drawWheel(highlight = false, blink = false) {
      if (prizes.length === 0) {
        // Nếu không có đối tượng nào, xóa canvas
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        return;
      }
      const numSlices = prizes.length;
      const centerX = canvas.width / 2;
      const centerY = canvas.height / 2;
      const radius = Math.min(centerX, centerY) - 10;
      const sliceAngle = 2 * Math.PI / numSlices;
      
      ctx.clearRect(0, 0, canvas.width, canvas.height);
      
      // Vẽ các mảnh bánh xe
      for (let i = 0; i < numSlices; i++) {
        const startAngle = i * sliceAngle;
        const endAngle = startAngle + sliceAngle;
        ctx.beginPath();
        // Lưu ý: cộng currentAngle để xoay bánh xe
        ctx.moveTo(centerX, centerY);
        ctx.arc(centerX, centerY, radius, startAngle + currentAngle, endAngle + currentAngle);
        ctx.closePath();
        // Màu sắc: dùng HSL để phân phối màu đều
        ctx.fillStyle = `hsl(${i * 360 / numSlices}, 70%, 60%)`;
        ctx.fill();
        // Nếu đang highlight và đây là phần trúng thưởng, vẽ viền nhấp nháy
        if (highlight && i === winningIndex && blink) {
          ctx.strokeStyle = "red";
          ctx.lineWidth = 5;
          ctx.stroke();
        } else {
          ctx.strokeStyle = "#fff";
          ctx.lineWidth = 2;
          ctx.stroke();
        }
        // Vẽ nhãn đối tượng
        ctx.save();
        ctx.translate(centerX, centerY);
        // Xoay đến giữa mảnh bánh xe
        const textAngle = startAngle + sliceAngle / 2 + currentAngle;
        ctx.rotate(textAngle);
        ctx.textAlign = "right";
        ctx.fillStyle = "#000";
        ctx.font = "16px Arial";
        ctx.fillText(prizes[i], radius - 10, 5);
        ctx.restore();
      }
      
      // Vẽ hình tròn ở trung tâm
      ctx.beginPath();
      ctx.arc(centerX, centerY, 30, 0, 2 * Math.PI);
      ctx.fillStyle = "#fff";
      ctx.fill();
      ctx.strokeStyle = "#000";
      ctx.stroke();
      
      // Vẽ mũi tên chỉ (ở phía trên bánh xe)
      ctx.beginPath();
      ctx.moveTo(centerX, centerY - radius - 20);
      ctx.lineTo(centerX - 15, centerY - radius - 5);
      ctx.lineTo(centerX + 15, centerY - radius - 5);
      ctx.closePath();
      ctx.fillStyle = "black";
      ctx.fill();
    }

    // Hàm animateSpin: dùng requestAnimationFrame để cập nhật chuyển động bánh xe
    function animateSpin(timestamp) {
      if (!startTime) startTime = timestamp;
      const elapsed = (timestamp - startTime) / 1000; // tính theo giây
      let t = elapsed / spinDuration;
      if (t > 1) t = 1;
      // Sử dụng hàm easing (easeOutCubic) để tạo chuyển động mượt dần
      const eased = 1 - Math.pow(1 - t, 3);
      currentAngle = initialAngle + (targetAngle - initialAngle) * eased;
      drawWheel();
      
      // Phát âm thanh tick khi bánh xe qua từng phần:
      const numSlices = prizes.length;
      const sliceAngle = 2 * Math.PI / numSlices;
      let pointerAngle = (-Math.PI / 2 - currentAngle) % (2 * Math.PI);
      if (pointerAngle < 0) pointerAngle += 2 * Math.PI;
      const currentTickIndex = Math.floor(pointerAngle / sliceAngle);
      if (currentTickIndex !== lastTickIndex) {
        lastTickIndex = currentTickIndex;
        if (tickSound) {
          tickSound.currentTime = 0;
          tickSound.play();
        }
      }
      
      if (t < 1) {
        requestAnimationFrame(animateSpin);
      } else {
        // Quay hoàn tất
        spinning = false;
        startTime = null;
        initialAngle = currentAngle;
        if (winSound) {
          winSound.currentTime = 0;
          winSound.play();
        }
        // Xác định đối tượng trúng thưởng dựa trên winningIndex đã chọn trước
        const winningPrize = prizes[winningIndex];
        resultDisplay.textContent = `Trúng: ${winningPrize} (Thời gian quay: ${spinDuration} giây)`;
        // Lưu kết quả vào lịch sử
        const li = document.createElement('li');
        const now = new Date();
        li.textContent = `${now.toLocaleTimeString()} - ${winningPrize} (${spinDuration} giây)`;
        historyList.prepend(li);
        // Bắt đầu hiệu ứng nhấp nháy highlight phần thắng
        startBlinking();
        spinBtn.disabled = false;
      }
    }
    
    // Hàm quay bánh xe: tính toán góc quay mục tiêu dựa trên đối tượng trúng thưởng ngẫu nhiên
    function spinWheel() {
      if (spinning || prizes.length === 0) return;
      spinning = true;
      resultDisplay.textContent = "";
      spinBtn.disabled = true;
      lastTickIndex = -1;
      spinDuration = parseFloat(spinTimeSlider.value);
      initialAngle = currentAngle;
      
      // Chọn ngẫu nhiên chỉ số đối tượng trúng thưởng
      winningIndex = Math.floor(Math.random() * prizes.length);
      // Chọn số vòng quay ngẫu nhiên (ví dụ 4 hoặc 5 vòng)
      const rounds = Math.floor(Math.random() * 2) + 4;
      const sliceAngle = 2 * Math.PI / prizes.length;
      // Tính góc mục tiêu sao cho tâm của phần trúng thưởng (winningIndex) sẽ nằm ngay dưới mũi tên
      // Công thức: targetAngle = -π/2 - (winningIndex + 0.5)*sliceAngle + rounds*2π
      targetAngle = -Math.PI / 2 - (winningIndex + 0.5) * sliceAngle + rounds * 2 * Math.PI;
      startTime = null;
      requestAnimationFrame(animateSpin);
    }
    
    spinBtn.addEventListener('click', spinWheel);
    
    // Hàm hiệu ứng nhấp nháy cho phần thắng
    function startBlinking() {
      let blink = false;
      let blinkCount = 0;
      const blinkInterval = setInterval(() => {
        blink = !blink;
        drawWheel(true, blink);
        blinkCount++;
        if (blinkCount > 6) { // Hiệu ứng nhấp nháy khoảng 3 giây (mỗi 500ms)
          clearInterval(blinkInterval);
          drawWheel();
        }
      }, 500);
    }
    
    // Vẽ bánh xe ban đầu (nếu có đối tượng)
    drawWheel();
    
  })();
  </script>
</body>
</html>

Có thể bạn quan tâm
0902 013 868