การสร้าง ตัวโหลด (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>