<!DOCTYPE html>
<html lang="th">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Wheel of Names - RUTS CoMS</title>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.11.5/gsap.min.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Inter', sans-serif;
background: linear-gradient(135deg, #87CEEB 0%, #3182ce 100%);
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
background: white;
border-radius: 20px;
box-shadow: 0 20px 40px rgba(0,0,0,0.1);
overflow: hidden;
}
.header {
background: linear-gradient(135deg, #87CEEB 0%, #3182ce 100%);
color: white;
padding: 20px 30px;
text-align: center;
}
.header h1 {
font-size: 2.5em;
font-weight: 700;
margin-bottom: 10px;
}
.header p {
font-size: 1.1em;
opacity: 0.9;
}
.main-content {
display: flex;
gap: 40px;
padding: 40px;
align-items: flex-start;
}
.wheel-section {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
}
.wheel-container {
position: relative;
margin-bottom: 30px;
}
#wheelCanvas {
border-radius: 50%;
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
background: white;
}
.wheel-pointer {
position: absolute;
top: 50%;
right: -15px;
transform: translateY(-50%);
width: 0;
height: 0;
border-left: 20px solid #ff4757;
border-top: 15px solid transparent;
border-bottom: 15px solid transparent;
z-index: 10;
filter: drop-shadow(0 2px 4px rgba(0,0,0,0.3));
}
.spin-button {
background: linear-gradient(135deg, #ff6b6b, #ee5a52);
color: white;
border: none;
padding: 15px 40px;
font-size: 1.2em;
font-weight: 600;
border-radius: 50px;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 5px 15px rgba(255, 107, 107, 0.4);
}
.spin-button:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(255, 107, 107, 0.6);
}
.spin-button:disabled {
background: #ccc;
cursor: not-allowed;
transform: none;
box-shadow: none;
}
.controls {
flex: 0 0 350px;
background: #f8f9fa;
border-radius: 15px;
padding: 30px;
}
.controls h3 {
color: #2c3e50;
margin-bottom: 20px;
font-size: 1.3em;
}
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
margin-bottom: 8px;
color: #555;
font-weight: 500;
}
.form-group textarea {
width: 100%;
height: 200px;
padding: 15px;
border: 2px solid #e9ecef;
border-radius: 10px;
font-family: inherit;
font-size: 14px;
resize: vertical;
transition: border-color 0.3s ease;
}
.form-group textarea:focus {
outline: none;
border-color: #667eea;
}
.form-group input[type="text"],
.form-group input[type="number"] {
width: 100%;
padding: 12px 15px;
border: 2px solid #e9ecef;
border-radius: 8px;
font-family: inherit;
transition: border-color 0.3s ease;
}
.form-group input[type="text"]:focus,
.form-group input[type="number"]:focus {
outline: none;
border-color: #667eea;
}
.checkbox-group {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 15px;
}
.checkbox-group input[type="checkbox"] {
width: 18px;
height: 18px;
accent-color: #667eea;
}
.action-buttons {
display: flex;
gap: 10px;
margin-top: 20px;
}
.btn {
flex: 1;
padding: 12px 20px;
border: none;
border-radius: 8px;
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
}
.btn-primary {
background: #667eea;
color: white;
}
.btn-primary:hover {
background: #5a67d8;
}
.btn-secondary {
background: #e9ecef;
color: #495057;
}
.btn-secondary:hover {
background: #dee2e6;
}
.winner-display {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.8);
display: none;
justify-content: center;
align-items: center;
z-index: 1000;
}
.winner-content {
background: white;
padding: 40px;
border-radius: 20px;
text-align: center;
max-width: 500px;
animation: winnerPop 0.5s ease-out;
}
.winner-content h2 {
color: #2c3e50;
font-size: 2em;
margin-bottom: 20px;
}
.winner-name {
font-size: 3em;
font-weight: 700;
color: #ff6b6b;
margin: 20px 0;
text-shadow: 2px 2px 4px rgba(0,0,0,0.1);
}
.winner-actions {
display: flex;
gap: 15px;
justify-content: center;
margin-top: 30px;
}
.winner-btn {
padding: 12px 24px;
border: none;
border-radius: 8px;
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
}
.winner-btn-remove {
background: #ff4757;
color: white;
}
.winner-btn-keep {
background: #2ed573;
color: white;
}
.winner-btn-close {
background: #747d8c;
color: white;
}
@keyframes winnerPop {
0% { transform: scale(0.5); opacity: 0; }
100% { transform: scale(1); opacity: 1; }
}
.names-counter {
background: #e3f2fd;
color: #1976d2;
padding: 10px 15px;
border-radius: 8px;
font-weight: 500;
margin-bottom: 15px;
text-align: center;
}
@media (max-width: 768px) {
.main-content {
flex-direction: column;
padding: 20px;
}
.controls {
flex: none;
}
#wheelCanvas {
width: 300px;
height: 300px;
}
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>💬 RUTS CoMS</h1>
<p>ใส่ชื่อและหมุนวงล้อเพื่อสุ่มผู้โชคดี</p>
</div>
<div class="main-content">
<div class="wheel-section">
<div class="wheel-container">
<canvas id="wheelCanvas" width="400" height="400"></canvas>
<div class="wheel-pointer"></div>
</div>
<button class="spin-button" id="spinBtn" onclick="spinWheel()">
🎯 หมุนวงล้อ
</button>
</div>
<div class="controls">
<h3>⚙️ ตั้งค่าวงล้อ</h3>
<div class="names-counter" id="namesCounter">
จำนวนชื่อ: 0
</div>
<div class="form-group">
<label for="namesInput">📝 รายชื่อ (หนึ่งชื่อต่อบรรทัด)</label>
<textarea id="namesInput" placeholder="ใส่ชื่อที่นี่... ตัวอย่าง: สมชาย สมหญิง สมศักดิ์"></textarea>
</div>
<div class="form-group">
<label for="wheelTitle">🏷️ ชื่อวงล้อ</label>
<input type="text" id="wheelTitle" value="วงล้อสุ่มชื่อ">
</div>
<div class="form-group">
<label for="spinDuration">⏱️ ระยะเวลาการหมุน (วินาที)</label>
<input type="number" id="spinDuration" min="2" max="10" value="4">
</div>
<div class="checkbox-group">
<input type="checkbox" id="soundEnabled" checked>
<label for="soundEnabled">🔊 เปิดเสียง</label>
</div>
<div class="checkbox-group">
<input type="checkbox" id="confettiEnabled" checked>
<label for="confettiEnabled">🎉 เปิดเอฟเฟกต์ชนะ</label>
</div>
<div class="action-buttons">
<button class="btn btn-secondary" onclick="clearNames()">🗑️ ล้างทั้งหมด</button>
<button class="btn btn-primary" onclick="shuffleNames()">🔀 สลับตำแหน่ง</button>
</div>
<div class="action-buttons" style="margin-top: 10px; justify-content: center;">
<button class="btn btn-primary" style="min-width: 180px;" onclick="saveWheel()">💾 บันทึกวงล้อ</button>
</div>
<div class="action-buttons" style="margin-top: 10px; justify-content: center;">
<button class="btn btn-secondary" onclick="shareWheel()">🔗 แชร์วงล้อ</button>
</div>
</div>
</div>
</div>
<!-- Winner Display Modal -->
<div class="winner-display" id="winnerModal">
<div class="winner-content">
<h2>🎉 ผู้โชคดี!</h2>
<div class="winner-name" id="winnerName"></div>
<p>คุณต้องการลบชื่อนี้ออกจากรายการหรือไม่?</p>
<div class="winner-actions">
<button class="winner-btn winner-btn-remove" onclick="removeWinner()">
❌ ลบชื่อออก
</button>
<button class="winner-btn winner-btn-keep" onclick="keepWinner()">
✅ เก็บชื่อไว้
</button>
<button class="winner-btn winner-btn-close" onclick="closeWinnerModal()">
🚪 ปิด
</button>
</div>
</div>
</div>
<script>
const canvas = document.getElementById('wheelCanvas');
const ctx = canvas.getContext('2d');
const namesInput = document.getElementById('namesInput');
const spinBtn = document.getElementById('spinBtn');
const namesCounter = document.getElementById('namesCounter');
const winnerModal = document.getElementById('winnerModal');
const winnerName = document.getElementById('winnerName');
let names = [];
let isSpinning = false;
let currentRotation = 0;
let lastWinner = '';
let lastWinnerIndex = -1;
let wheelId = 0; // ID ของวงล้อในฐานข้อมูล
// เสียงเอฟเฟกต์
const tickSound = new Audio('data:audio/wav;base64,UklGRnoGAABXQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0YQoGAACBhYqFbF1fdJivrJBhNjVgodDbq2EcBj+a2/LDciUFLIHO8tiJNwgZaLvt559NEAxQp+PwtmMcBjiR1/LMeSwFJHfH8N2QQAoUXrTp66hVFApGn+DyvmwhBSqA0vDaizEIFGq+8+WBQQ');
const winSound = new Audio('data:audio/wav;base64,UklGRnoGAABXQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0YQoGAACBhYqFbF1fdJivrJBhNjVgodDbq2EcBj+a2/LDciUFLIHO8tiJNwgZaLvt559NEAxQp+PwtmMcBjiR1/LMeSwFJHfH8N2QQAoUXrTp66hVFApGn+DuvmwhBSqA0vDaizEIFGq+8+GTRC');
// สีสำหรับวงล้อ
const wheelColors = [
'#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7',
'#DDA0DD', '#FFA07A', '#98D8C8', '#F7DC6F', '#BB8FCE',
'#85C1E9', '#F8C471', '#82E0AA', '#F1948A', '#85CBDB'
];
function updateNamesFromInput() {
const inputValue = namesInput.value.trim();
names = inputValue ? inputValue.split('\n').filter(name => name.trim() !== '') : [];
updateNamesCounter();
drawWheel();
}
function updateNamesCounter() {
namesCounter.textContent = `จำนวนชื่อ: ${names.length}`;
spinBtn.disabled = names.length === 0;
}
function drawWheel() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
const centerX = canvas.width / 2;
const centerY = canvas.height / 2;
const radius = Math.min(centerX, centerY) - 10;
if (names.length === 0) {
// วาดวงล้อว่าง
ctx.beginPath();
ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI);
ctx.fillStyle = '#f0f0f0';
ctx.fill();
ctx.strokeStyle = '#ddd';
ctx.lineWidth = 2;
ctx.stroke();
ctx.fillStyle = '#999';
ctx.font = '20px Inter';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText('ใส่ชื่อเพื่อเริ่มต้น', centerX, centerY);
return;
}
const angleStep = (2 * Math.PI) / names.length;
for (let i = 0; i < names.length; i++) {
const startAngle = i * angleStep;
const endAngle = startAngle + angleStep;
// วาดส่วนของวงล้อ
ctx.beginPath();
ctx.moveTo(centerX, centerY);
ctx.arc(centerX, centerY, radius, startAngle, endAngle);
ctx.closePath();
ctx.fillStyle = wheelColors[i % wheelColors.length];
ctx.fill();
ctx.strokeStyle = '#fff';
ctx.lineWidth = 3;
ctx.stroke();
// วาดข้อความ
ctx.save();
ctx.translate(centerX, centerY);
ctx.rotate(startAngle + angleStep / 2);
ctx.fillStyle = '#fff';
ctx.font = 'bold 16px Inter';
ctx.textAlign = 'left';
ctx.textBaseline = 'middle';
const text = names[i].length > 12 ? names[i].substring(0, 12) + '...' : names[i];
ctx.fillText(text, radius * 0.3, 0);
ctx.restore();
}
// วาดวงกลมตรงกลาง
ctx.beginPath();
ctx.arc(centerX, centerY, 20, 0, 2 * Math.PI);
ctx.fillStyle = '#fff';
ctx.fill();
ctx.strokeStyle = '#ddd';
ctx.lineWidth = 2;
ctx.stroke();
}
function spinWheel() {
if (isSpinning || names.length === 0) return;
isSpinning = true;
spinBtn.disabled = true;
spinBtn.textContent = '⏳ กำลังหมุน...';
const duration = parseInt(document.getElementById('spinDuration').value);
const rotations = 3 + Math.random() * 3; // 3-6 รอบ
const finalRotation = currentRotation + (rotations * 360);
gsap.to(canvas, {
rotation: finalRotation,
duration: duration,
ease: "power4.out",
onUpdate: function() {
currentRotation = gsap.getProperty(canvas, "rotation");
if (document.getElementById('soundEnabled').checked && Math.random() < 0.1) {
tickSound.currentTime = 0;
tickSound.play().catch(() => {});
}
},
onComplete: function() {
isSpinning = false;
spinBtn.disabled = false;
spinBtn.textContent = '🎯 หมุนวงล้อ';
// คำนวณผู้ชนะ
const normalizedRotation = ((currentRotation % 360) + 360) % 360;
const angleStep = 360 / names.length;
const winnerIndex = Math.floor((360 - normalizedRotation + angleStep/2) / angleStep) % names.length;
lastWinner = names[winnerIndex];
lastWinnerIndex = winnerIndex;
showWinner(lastWinner);
if (document.getElementById('soundEnabled').checked) {
winSound.play().catch(() => {});
}
if (document.getElementById('confettiEnabled').checked) {
createConfetti();
}
}
});
}
function showWinner(winner) {
winnerName.textContent = winner;
winnerModal.style.display = 'flex';
}
function removeWinner() {
if (lastWinnerIndex >= 0) {
names.splice(lastWinnerIndex, 1);
namesInput.value = names.join('\n');
updateNamesCounter();
drawWheel();
}
closeWinnerModal();
}
function keepWinner() {
closeWinnerModal();
}
function closeWinnerModal() {
winnerModal.style.display = 'none';
}
function clearNames() {
if (confirm('คุณแน่ใจหรือไม่ที่จะลบชื่อทั้งหมด?')) {
names = [];
namesInput.value = '';
updateNamesCounter();
drawWheel();
}
}
function shuffleNames() {
if (names.length > 1) {
for (let i = names.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[names[i], names[j]] = [names[j], names[i]];
}
namesInput.value = names.join('\n');
drawWheel();
}
}
function createConfetti() {
const colors = ['#ff6b6b', '#4ecdc4', '#45b7d1', '#96ceb4', '#ffeaa7'];
const confettiCount = 50;
for (let i = 0; i < confettiCount; i++) {
const confetti = document.createElement('div');
confetti.style.position = 'fixed';
confetti.style.left = Math.random() * 100 + '%';
confetti.style.top = '-10px';
confetti.style.width = Math.random() * 10 + 5 + 'px';
confetti.style.height = confetti.style.width;
confetti.style.backgroundColor = colors[Math.floor(Math.random() * colors.length)];
confetti.style.pointerEvents = 'none';
confetti.style.zIndex = '9999';
confetti.style.borderRadius = '50%';
document.body.appendChild(confetti);
gsap.to(confetti, {
y: window.innerHeight + 100,
rotation: Math.random() * 360,
duration: Math.random() * 2 + 2,
ease: "power2.in",
onComplete: function() {
confetti.remove();
}
});
}
}
function saveWheel() {
if (names.length === 0) {
alert('❌ กรุณาใส่ชื่ออย่างน้อยหนึ่งชื่อก่อนบันทึก');
return;
}
const wheelData = {
wheel_id: wheelId,
wheel_name: document.getElementById('wheelTitle').value || 'วงล้อสุ่มชื่อ',
names: JSON.stringify(names),
settings: JSON.stringify({
spinDuration: parseInt(document.getElementById('spinDuration').value),
soundEnabled: document.getElementById('soundEnabled').checked,
confettiEnabled: document.getElementById('confettiEnabled').checked
})
};
// สร้าง FormData สำหรับส่งข้อมูล
const formData = new FormData();
Object.keys(wheelData).forEach(key => {
formData.append(key, wheelData[key]);
});
fetch('save_wheel.php', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
wheelId = data.wheel_id;
alert('✅ บันทึกวงล้อสำเร็จแล้ว!');
// อัพเดท URL ถ้าเป็นวงล้อใหม่
if (wheelId && !window.location.search.includes('id=')) {
const newUrl = window.location.pathname + '?id=' + wheelId;
window.history.pushState({}, '', newUrl);
}
} else {
alert('❌ เกิดข้อผิดพลาด: ' + (data.message || 'ไม่สามารถบันทึกได้'));
}
})
.catch(error => {
console.error('Error:', error);
alert('❌ เกิดข้อผิดพลาดในการเชื่อมต่อ');
});
}
function shareWheel() {
if (!wheelId) {
alert('❌ กรุณาบันทึกวงล้อก่อนแชร์');
return;
}
const shareUrl = window.location.origin + window.location.pathname + '?id=' + wheelId;
if (navigator.clipboard) {
navigator.clipboard.writeText(shareUrl).then(() => {
alert('✅ คัดลอกลิงก์แชร์เรียบร้อยแล้ว!\n\n' + shareUrl);
}).catch(() => {
showShareDialog(shareUrl);
});
} else {
showShareDialog(shareUrl);
}
}
function showShareDialog(url) {
const modal = document.createElement('div');
modal.style.cssText = `
position: fixed; top: 0; left: 0; width: 100%; height: 100%;
background: rgba(0,0,0,0.8); display: flex; justify-content: center;
align-items: center; z-index: 10000;
`;
modal.innerHTML = `
<div style="background: white; padding: 30px; border-radius: 15px; max-width: 500px; text-align: center;">
<h3 style="margin-bottom: 20px;">🔗 ลิงก์แชร์วงล้อ</h3>
<input type="text" value="${url}" readonly style="width: 100%; padding: 10px; margin: 10px 0; border: 2px solid #ddd; border-radius: 5px;">
<div style="margin-top: 20px;">
<button onclick="this.parentElement.parentElement.parentElement.remove()" style="padding: 10px 20px; background: #667eea; color: white; border: none; border-radius: 5px; cursor: pointer;">ปิด</button>
</div>
</div>
`;
document.body.appendChild(modal);
modal.querySelector('input').select();
}
function loadWheelData() {
const urlParams = new URLSearchParams(window.location.search);
const id = urlParams.get('id');
if (id) {
wheelId = parseInt(id);
// จำลองการโหลดข้อมูล - ในการใช้งานจริงต้องดึงจาก PHP
// ตัวอย่างข้อมูล:
console.log('Loading wheel ID:', wheelId);
// ข้อมูลจะถูกโหลดผ่าน PHP ในไฟล์ wheel.php
}
}
// Event Listeners
namesInput.addEventListener('input', updateNamesFromInput);
// ปิด modal เมื่อคลิกข้างนอก
winnerModal.addEventListener('click', function(e) {
if (e.target === winnerModal) {
closeWinnerModal();
}
});
// เริ่มต้น
document.addEventListener('DOMContentLoaded', function() {
loadWheelData(); // โหลดข้อมูลจาก URL ถ้ามี
updateNamesFromInput();
drawWheel();
});
</script>
</body>
</html>