Hướng dẫn code vòng quay may mắn

Tạo vòng quay may mắn bằng HTML, CSS và JavaScript với giao diện trực quan, hiệu ứng quay mượt và kết quả ngẫu nhiên. Bạn có thể tùy chỉnh số ô, nội dung phần thưởng, màu sắc, tốc độ quay và xử lý kết quả sau khi quay. Các bước chính: HTML: Tạo vòng quay

22/09/2026 Quản trị viên 37 Lập trình game
<!DOCTYPE html>
<html lang="vi">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">

<title>Vòng Quay May Mắn - MUATHEMEWPGIARE.COM</title>

<style>
/* =========================================================
   MUATHEMEWPGIARE.COM
   LUCKY WHEEL
   HTML + CSS + JAVASCRIPT
========================================================= */

* {
    box-sizing: border-box;
}

body {
    margin: 0;
    min-height: 100vh;
    font-family: Arial, Helvetica, sans-serif;
    background:
        radial-gradient(circle at 50% 0%, #42109a 0%, transparent 45%),
        linear-gradient(135deg, #090014, #16002f 55%, #08000f);
    color: #fff;
}

/* APP */
.lucky-wheel-app {
    width: 100%;
    max-width: 900px;
    margin: 0 auto;
    padding: 45px 20px;
    text-align: center;
}

/* HEADER */
.lucky-header {
    margin-bottom: 30px;
}

.lucky-header .brand {
    display: inline-block;
    margin-bottom: 10px;
    font-size: 13px;
    font-weight: 800;
    letter-spacing: 3px;
    color: #d9b6ff;
    text-transform: uppercase;
}

.lucky-header h1 {
    margin: 0;
    font-size: clamp(28px, 5vw, 48px);
    font-weight: 900;
    line-height: 1.1;
    background: linear-gradient(90deg, #fff, #d79cff, #fff);
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
}

.lucky-header p {
    margin: 14px auto 0;
    max-width: 620px;
    color: #c7b8d8;
    font-size: 15px;
    line-height: 1.7;
}

/* WHEEL AREA */
.wheel-area {
    position: relative;
    width: min(86vw, 560px);
    aspect-ratio: 1;
    margin: 35px auto;
}

/* GLOW */
.wheel-area::before {
    content: "";
    position: absolute;
    inset: -25px;
    border-radius: 50%;
    background: rgba(125, 0, 255, .18);
    filter: blur(35px);
    z-index: 0;
}

/* CANVAS */
#wheelCanvas {
    position: relative;
    width: 100%;
    height: 100%;
    display: block;
    border-radius: 50%;
    z-index: 2;

    box-shadow:
        0 0 0 8px #210046,
        0 0 0 12px #8736ff,
        0 0 45px rgba(150, 60, 255, .65),
        0 25px 70px rgba(0,0,0,.6);
}

/* POINTER */
.wheel-pointer {
    position: absolute;
    z-index: 10;
    top: -5px;
    left: 50%;
    transform: translateX(-50%);

    width: 0;
    height: 0;

    border-left: 22px solid transparent;
    border-right: 22px solid transparent;
    border-top: 48px solid #fff;

    filter:
        drop-shadow(0 0 8px #fff)
        drop-shadow(0 5px 8px rgba(0,0,0,.7));
}

.wheel-pointer::after {
    content: "";
    position: absolute;

    width: 13px;
    height: 13px;

    left: -6.5px;
    top: -43px;

    border-radius: 50%;
    background: #ff2bd6;
    box-shadow: 0 0 15px #ff2bd6;
}

/* CENTER BUTTON */
.wheel-center {
    position: absolute;
    z-index: 8;

    width: 92px;
    height: 92px;

    top: 50%;
    left: 50%;

    transform: translate(-50%, -50%);

    border-radius: 50%;

    display: flex;
    align-items: center;
    justify-content: center;

    background:
        radial-gradient(circle, #fff 0%, #eee 45%, #c9c9c9 100%);

    border: 7px solid #ffcc00;

    box-shadow:
        0 0 0 5px #56159b,
        0 0 25px rgba(255, 204, 0, .8),
        inset 0 4px 10px rgba(255,255,255,.8);

    color: #3b006d;

    font-size: 14px;
    font-weight: 900;

    text-transform: uppercase;

    user-select: none;
}

/* SPIN BUTTON */
.spin-button {
    position: relative;

    border: 0;
    outline: none;

    padding: 18px 50px;

    border-radius: 999px;

    cursor: pointer;

    font-size: 17px;
    font-weight: 900;

    color: #fff;

    background:
        linear-gradient(135deg, #7b2cff, #d20cff);

    box-shadow:
        0 10px 30px rgba(142, 33, 255, .4),
        inset 0 1px rgba(255,255,255,.35);

    transition:
        transform .2s ease,
        box-shadow .2s ease,
        opacity .2s ease;
}

.spin-button:hover {
    transform: translateY(-3px) scale(1.02);

    box-shadow:
        0 15px 40px rgba(173, 55, 255, .6),
        inset 0 1px rgba(255,255,255,.45);
}

.spin-button:active {
    transform: translateY(1px) scale(.98);
}

.spin-button:disabled {
    cursor: not-allowed;
    opacity: .55;
}

/* RESULT */
.result-box {
    margin: 30px auto 0;

    max-width: 500px;

    padding: 22px;

    border-radius: 20px;

    background: rgba(255,255,255,.06);

    border: 1px solid rgba(255,255,255,.1);

    backdrop-filter: blur(12px);

    box-shadow: 0 15px 50px rgba(0,0,0,.25);
}

.result-label {
    color: #bca9ce;
    font-size: 13px;
    text-transform: uppercase;
    letter-spacing: 2px;
}

.result-prize {
    margin-top: 7px;

    font-size: clamp(24px, 5vw, 38px);

    font-weight: 900;

    color: #ffd84d;

    text-shadow:
        0 0 15px rgba(255,216,77,.5);
}

/* FOOTER */
.lucky-footer {
    margin-top: 35px;

    color: #88799a;

    font-size: 12px;
    letter-spacing: .5px;
}

.lucky-footer strong {
    color: #c18cff;
}

/* MOBILE */
@media(max-width:600px) {

    .lucky-wheel-app {
        padding: 30px 14px;
    }

    .wheel-area {
        width: min(88vw, 430px);
    }

    .wheel-center {
        width: 72px;
        height: 72px;
        font-size: 11px;
        border-width: 5px;
    }

    .wheel-pointer {
        border-left-width: 17px;
        border-right-width: 17px;
        border-top-width: 38px;
    }

    .spin-button {
        width: 100%;
        max-width: 330px;
    }
}
</style>
</head>

<body>

<div class="lucky-wheel-app">

    <header class="lucky-header">

        <div class="brand">
            MUATHEMEWPGIARE.COM
        </div>

        <h1>🎁 Vòng Quay May Mắn</h1>

        <p>
            Quay thử vận may và nhận ngay phần thưởng hấp dẫn.
            Giao diện hiện đại, responsive và chạy hoàn toàn bằng HTML,
            CSS và JavaScript.
        </p>

    </header>


    <div class="wheel-area">

        <div class="wheel-pointer"></div>

        <canvas id="wheelCanvas"></canvas>

        <div class="wheel-center">
            QUAY
        </div>

    </div>


    <button
        type="button"
        class="spin-button"
        id="spinButton"
    >
        🎯 QUAY NGAY
    </button>


    <div class="result-box">

        <div class="result-label">
            Kết quả
        </div>

        <div
            class="result-prize"
            id="resultPrize"
        >
            Chúc bạn may mắn!
        </div>

    </div>


    <div class="lucky-footer">

        Vòng quay được phát triển bởi
        <strong>MUATHEMEWPGIARE.COM</strong>

    </div>

</div>


<script>
/* =========================================================
   MUATHEMEWPGIARE.COM
   LUCKY WHEEL JAVASCRIPT
========================================================= */

(function () {

    "use strict";

    /* -----------------------------------------------------
       CẤU HÌNH PHẦN THƯỞNG
    ----------------------------------------------------- */

    const prizes = [
        "GIẢM 5%",
        "GIẢM 10%",
        "GIẢM 15%",
        "GIẢM 20%",
        "GIẢM 25%",
        "GIẢM 30%",
        "GIẢM 35%",
        "GIẢM 40%"
    ];


    /* -----------------------------------------------------
       MÀU VÒNG QUAY
    ----------------------------------------------------- */

    const colors = [
        "#7B2CFF",
        "#FF2D95",
        "#FF6B00",
        "#FFC400",
        "#00C853",
        "#00B8D9",
        "#304FFE",
        "#9C27B0"
    ];


    /* -----------------------------------------------------
       DOM
    ----------------------------------------------------- */

    const canvas =
        document.getElementById("wheelCanvas");

    const ctx =
        canvas.getContext("2d");

    const spinButton =
        document.getElementById("spinButton");

    const resultPrize =
        document.getElementById("resultPrize");


    /* -----------------------------------------------------
       BIẾN
    ----------------------------------------------------- */

    let rotation = 0;

    let spinning = false;

    let animationFrame = null;

    const TWO_PI = Math.PI * 2;


    /* -----------------------------------------------------
       CANVAS RESPONSIVE
    ----------------------------------------------------- */

    function resizeCanvas() {

        const rect =
            canvas.getBoundingClientRect();

        const size =
            Math.min(rect.width, rect.height);

        const dpr =
            Math.max(1, window.devicePixelRatio || 1);

        canvas.width =
            size * dpr;

        canvas.height =
            size * dpr;

        ctx.setTransform(
            dpr,
            0,
            0,
            dpr,
            0,
            0
        );

        drawWheel(size);

    }


    /* -----------------------------------------------------
       VẼ VÒNG QUAY
    ----------------------------------------------------- */

    function drawWheel(size) {

        const center =
            size / 2;

        const radius =
            size / 2 - 7;

        ctx.clearRect(
            0,
            0,
            size,
            size
        );


        /* vòng ngoài */

        ctx.beginPath();

        ctx.arc(
            center,
            center,
            radius,
            0,
            TWO_PI
        );

        ctx.fillStyle =
            "#fff";

        ctx.fill();


        /* các ô */

        const segmentAngle =
            TWO_PI / prizes.length;


        for (
            let i = 0;
            i < prizes.length;
            i++
        ) {

            const startAngle =
                rotation +
                i * segmentAngle -
                Math.PI / 2;

            const endAngle =
                startAngle +
                segmentAngle;


            /* segment */

            ctx.beginPath();

            ctx.moveTo(
                center,
                center
            );

            ctx.arc(
                center,
                center,
                radius,
                startAngle,
                endAngle
            );

            ctx.closePath();

            ctx.fillStyle =
                colors[i % colors.length];

            ctx.fill();


            /* viền */

            ctx.strokeStyle =
                "rgba(255,255,255,.8)";

            ctx.lineWidth = 2;

            ctx.stroke();


            /* text */

            const middleAngle =
                startAngle +
                segmentAngle / 2;

            ctx.save();

            ctx.translate(
                center,
                center
            );

            ctx.rotate(
                middleAngle
            );

            ctx.textAlign =
                "right";

            ctx.textBaseline =
                "middle";

            let fontSize =
                Math.max(
                    12,
                    Math.min(
                        22,
                        size / 22
                    )
                );

            ctx.font =
                "900 " +
                fontSize +
                "px Arial";

            ctx.fillStyle =
                "#ffffff";

            ctx.shadowColor =
                "rgba(0,0,0,.45)";

            ctx.shadowBlur = 4;

            ctx.fillText(
                prizes[i],
                radius - 18,
                0
            );

            ctx.restore();

        }


        /* vòng trong */

        ctx.beginPath();

        ctx.arc(
            center,
            center,
            radius * .12,
            0,
            TWO_PI
        );

        ctx.fillStyle =
            "#ffffff";

        ctx.fill();


        ctx.beginPath();

        ctx.arc(
            center,
            center,
            radius * .075,
            0,
            TWO_PI
        );

        ctx.fillStyle =
            "#741EFF";

        ctx.fill();


        /* viền ngoài */

        ctx.beginPath();

        ctx.arc(
            center,
            center,
            radius,
            0,
            TWO_PI
        );

        ctx.strokeStyle =
            "#ffffff";

        ctx.lineWidth = 6;

        ctx.stroke();

    }


    /* -----------------------------------------------------
       RANDOM
    ----------------------------------------------------- */

    function random() {

        if (
            window.crypto &&
            window.crypto.getRandomValues
        ) {

            const buffer =
                new Uint32Array(1);

            window.crypto.getRandomValues(
                buffer
            );

            return (
                buffer[0] /
                4294967296
            );

        }

        return Math.random();

    }


    /* -----------------------------------------------------
       EASING
       QUAY NHANH → CHẬM DẦN
    ----------------------------------------------------- */

    function easeOutCubic(t) {

        return 1 -
            Math.pow(
                1 - t,
                3
            );

    }


    /* -----------------------------------------------------
       SPIN
    ----------------------------------------------------- */

    function spin() {

        if (spinning) {
            return;
        }


        spinning = true;

        spinButton.disabled = true;

        resultPrize.textContent =
            "Đang quay...";


        /*
         * Chọn kết quả TRƯỚC KHI animation bắt đầu.
         */

        const winnerIndex =
            Math.floor(
                random() * prizes.length
            );


        const segmentAngle =
            TWO_PI / prizes.length;


        /*
         * Tâm của ô chiến thắng.
         */

        const winnerCenter =
            winnerIndex *
                segmentAngle +
            segmentAngle / 2;


        /*
         * Pointer nằm ở vị trí 12 giờ.
         *
         * Tính góc cần quay để ô winner
         * nằm đúng dưới pointer.
         */

        const targetRotation =
            TWO_PI -
            winnerCenter;


        /*
         * Thêm 5 - 7 vòng quay.
         */

        const extraSpins =
            5 +
            Math.floor(
                random() * 3
            );


        const currentNormalized =
            rotation % TWO_PI;


        let delta =
            targetRotation -
            currentNormalized;


        if (delta < 0) {
            delta += TWO_PI;
        }


        delta +=
            extraSpins * TWO_PI;


        /*
         * Thời gian animation
         */

        const duration =
            5200;


        const startRotation =
            rotation;

        const startTime =
            performance.now();


        function animate(now) {

            const elapsed =
                now -
                startTime;


            const progress =
                Math.min(
                    elapsed / duration,
                    1
                );


            const eased =
                easeOutCubic(
                    progress
                );


            rotation =
                startRotation +
                delta * eased;


            const size =
                canvas.getBoundingClientRect().width;


            drawWheel(size);


            if (progress < 1) {

                animationFrame =
                    requestAnimationFrame(
                        animate
                    );

            } else {

                rotation =
                    startRotation +
                    delta;


                drawWheel(size);


                finishSpin(
                    winnerIndex
                );

            }

        }


        animationFrame =
            requestAnimationFrame(
                animate
            );

    }


    /* -----------------------------------------------------
       KẾT THÚC
    ----------------------------------------------------- */

    function finishSpin(
        winnerIndex
    ) {

        spinning = false;

        spinButton.disabled = false;


        resultPrize.textContent =
            "🎉 " +
            prizes[winnerIndex];


        /*
         * Hiệu ứng nút.
         */

        spinButton.animate(
            [
                {
                    transform:
                        "scale(1)"
                },
                {
                    transform:
                        "scale(1.08)"
                },
                {
                    transform:
                        "scale(1)"
                }
            ],
            {
                duration: 450,
                easing: "ease-out"
            }
        );

    }


    /* -----------------------------------------------------
       EVENTS
    ----------------------------------------------------- */

    spinButton.addEventListener(
        "click",
        spin
    );


    /* Cho phép click vào tâm để quay */

    document
        .querySelector(".wheel-center")
        .addEventListener(
            "click",
            spin
        );


    window.addEventListener(
        "resize",
        resizeCanvas
    );


    /* -----------------------------------------------------
       INIT
    ----------------------------------------------------- */

    resizeCanvas();

})();
</script>

</body>
</html>
Không có CSS code
Không có JavaScript code
Live Preview
Ctrl1 HTML Ctrl2 CSS Ctrl3 JS Ctrl↵ Reload
Chia sẻ Demo này · Gửi cho bạn bè hoặc đồng nghiệp