การสร้าง ตัวโหลด (Loader) รูปก้นหอยสีรุ้ง หมุนได้ และมี โลโก้เล็กๆ อยู่ตรงกลาง โดยใช้ <canvas> วาดกราฟิกและใช้ CSS จัดตำแหน่งให้สวยงาม
ผมจะแบ่งอธิบายทีละส่วน
1. โครงสร้าง HTML
<div class="loader">
→ กล่องหลักที่เก็บทั้ง canvas (ก้นหอยสีรุ้ง) และ โลโก้<canvas>
→ ใช้สำหรับวาดเส้นก้นหอย<img>
→ โลโก้ที่วางอยู่ตรงกลาง
2. CSS การจัดตำแหน่งและสไตล์
ผลลัพธ์:
- ทั้ง loader จะอยู่ตรงกลางหน้าจอ
<canvas>
จะเต็มพื้นที่ 100x100 พิกเซล- โลโก้จะถูกจัดให้อยู่กึ่งกลางพอดี เหมือนวางอยู่บน "หัวใจ" ของก้นหอย
3. ส่วน JavaScript วาดก้นหอยสีรุ้ง
- กำหนด canvas context สำหรับวาด 2D
- คำนวณ center (cx, cy) เพื่อใช้หมุนก้นหอยรอบจุดกลาง
ฟังก์ชันวาดก้นหอย
อธิบายการคำนวณ:
ใช้สมการ ก้นหอยแบบ Archimedean spiral:
โดย r
= รัศมี, t
= สัดส่วนความคืบหน้า, a
กำหนดโดย maxRadius
และ turns
- ใช้
HSL
เพื่อไล่โทนสีรุ้งจากแดง → ส้ม → เหลือง → เขียว → น้ำเงิน → ม่วง
ฟังก์ชันหมุนแอนิเมชัน
rotation
เพิ่มขึ้นเรื่อยๆ → ทำให้ก้นหอยหมุนrequestAnimationFrame
→ ให้เบราว์เซอร์วาดต่อเนื่องอย่างลื่นไหล (เฟรมต่อเฟรม)
สรุปการทำงาน
- HTML → กำหนดโครงสร้าง loader (canvas + logo)
- CSS → จัดตำแหน่งให้อยู่กึ่งกลาง และซ้อนโลโก้ตรงกลาง
- JavaScript → คำนวณจุดบนเส้นก้นหอย, ไล่สีรุ้ง, และหมุนด้วยการอัปเดตค่า
rotation
ทุกเฟรม
ผลลัพธ์สุดท้าย: ได้ก้นหอยสีรุ้งหมุนไม่หยุด พร้อมโลโก้ตรงกลาง เหมาะใช้เป็น Preloader Animation
ตัวอย่างการใช้งาน
<!DOCTYPE html> <html lang="th"> <head> <meta charset="UTF-8"> <title>Rainbow Spiral Loader</title> <style> body { background: #4e4e4e; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; } .loader { position: relative; width: 100px; height: 100px; } .loader canvas { position: absolute; top: 0; left: 0; } .loader img { position: absolute; top: 50%; left: 50%; width: 35px; /* ขนาดโลโก้ */ height: 35px; transform: translate(-50%, -50%); border-radius: 50%; } </style> </head> <body> <div class="loader"> <canvas id="spiral" width="100" height="100"></canvas> <!-- เปลี่ยน src ให้เป็นโลโก้ของคุณ --> <img src="https://semicon.github.io/img/web/logoxxx.png" alt="logo"> </div> <script> const canvas = document.getElementById('spiral'); const ctx = canvas.getContext('2d'); const w = canvas.width; const h = canvas.height; const cx = w / 2; const cy = h / 2; let rotation = 0; function drawSpiral(rot) { ctx.clearRect(0, 0, w, h); ctx.save(); ctx.translate(cx, cy); ctx.rotate(rot); ctx.lineWidth = 2; const maxRadius = 45; const turns = 4.5; const steps = 1000; for (let i = 0; i < steps; i++) { const t1 = i / steps; const t2 = (i + 1) / steps; const theta1 = t1 * turns * Math.PI * 2; const theta2 = t2 * turns * Math.PI * 2; const radius1 = t1 * maxRadius; const radius2 = t2 * maxRadius; const x1 = radius1 * Math.cos(theta1); const y1 = radius1 * Math.sin(theta1); const x2 = radius2 * Math.cos(theta2); const y2 = radius2 * Math.sin(theta2); const hue = (t1 * 360) % 360; // ไล่โทนสีรุ้งตามระยะ ctx.beginPath(); ctx.strokeStyle = `hsl(${hue}, 100%, 50%)`; ctx.moveTo(x1, y1); ctx.lineTo(x2, y2); ctx.stroke(); } ctx.restore(); } function animate() { rotation += 0.05; // ความเร็วหมุน drawSpiral(rotation); requestAnimationFrame(animate); } animate(); </script> </body> </html>