สร้าง ตัวโหลด (Loader) รูปก้นหอยสีรุ้ง หมุนได้ และมี โลโก้



การสร้าง ตัวโหลด (Loader) รูปก้นหอยสีรุ้ง หมุนได้ และมี โลโก้เล็กๆ อยู่ตรงกลาง โดยใช้ <canvas> วาดกราฟิกและใช้ CSS จัดตำแหน่งให้สวยงาม

ผมจะแบ่งอธิบายทีละส่วน

1. โครงสร้าง HTML


<div class="loader"> <canvas id="spiral" width="100" height="100"></canvas> <img src="https://semicon.github.io/img/web/logoxxx.png" alt="logo"> </div>
  • <div class="loader"> → กล่องหลักที่เก็บทั้ง canvas (ก้นหอยสีรุ้ง) และ โลโก้
  • <canvas> → ใช้สำหรับวาดเส้นก้นหอย
  • <img> → โลโก้ที่วางอยู่ตรงกลาง

2. CSS การจัดตำแหน่งและสไตล์


body { background: #000; /* พื้นหลังสีดำ */ 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%; /* ทำให้โลโก้เป็นวงกลม */ }

ผลลัพธ์:

  • ทั้ง loader จะอยู่ตรงกลางหน้าจอ
  • <canvas> จะเต็มพื้นที่ 100x100 พิกเซล
  • โลโก้จะถูกจัดให้อยู่กึ่งกลางพอดี เหมือนวางอยู่บน "หัวใจ" ของก้นหอย


3. ส่วน JavaScript วาดก้นหอยสีรุ้ง


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;

  • กำหนด canvas context สำหรับวาด 2D
  • คำนวณ center (cx, cy) เพื่อใช้หมุนก้นหอยรอบจุดกลาง


ฟังก์ชันวาดก้นหอย


function drawSpiral(rot) { ctx.clearRect(0, 0, w, h); // ลบภาพเดิมออก ctx.save(); ctx.translate(cx, cy); // ย้ายจุดอ้างอิงไปที่กลาง ctx.rotate(rot); // หมุนทั้งรูปตามค่า rot ctx.lineWidth = 2; const maxRadius = 45; // รัศมีสูงสุด const turns = 4.5; // จำนวนรอบของก้นหอย const steps = 1000; // จำนวน segment ที่ใช้ต่อเส้น for (let i = 0; i < steps; i++) { const t1 = i / steps; const t2 = (i + 1) / steps; // มุมเริ่มและมุมถัดไป (radian) const theta1 = t1 * turns * Math.PI * 2; const theta2 = t2 * turns * Math.PI * 2; // รัศมีเริ่มและรัศมีถัดไป const radius1 = t1 * maxRadius; const radius2 = t2 * maxRadius; // แปลง polar → Cartesian 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%)`; // hsl → สีรุ้ง ctx.moveTo(x1, y1); ctx.lineTo(x2, y2); ctx.stroke(); } ctx.restore(); }

อธิบายการคำนวณ:

  • ใช้สมการ ก้นหอยแบบ Archimedean spiral:

r=at

           โดย r = รัศมี, t = สัดส่วนความคืบหน้า, a กำหนดโดย maxRadius และ turns

  • ใช้ HSL เพื่อไล่โทนสีรุ้งจากแดง → ส้ม → เหลือง → เขียว → น้ำเงิน → ม่วง

ฟังก์ชันหมุนแอนิเมชัน


function animate() { rotation += 0.05; // ความเร็วหมุน drawSpiral(rotation); requestAnimationFrame(animate); // วนเรียกซ้ำ } animate();

  • rotation เพิ่มขึ้นเรื่อยๆ → ทำให้ก้นหอยหมุน
  • requestAnimationFrame → ให้เบราว์เซอร์วาดต่อเนื่องอย่างลื่นไหล (เฟรมต่อเฟรม)


สรุปการทำงาน

  1. HTML → กำหนดโครงสร้าง loader (canvas + logo)
  2. CSS → จัดตำแหน่งให้อยู่กึ่งกลาง และซ้อนโลโก้ตรงกลาง
  3. 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>

    



แสดงความคิดเห็น (0)
ใหม่กว่า เก่ากว่า