Крутите колесо и выиграйте приз
Просто нажмите кнопку ниже, чтобы вызвать окно с колесом призов
Крутите колесо
заполните форму и нажмите кнопку "крутить"
Ура, вы выиграли!
Приз
промокод
Промокод действует при оформлении любого заказа.
Нажмите на кнопку "Получить приз" и оформите заказ, используя промокод
По всем вопросам розыгрыша пишите на mail@mail.ru
<script src="https://cdn.jsdelivr.net/npm/canvas-confetti@1.6.0/dist/confetti.browser.min.js"></script>

<script>
(function() {
    'use strict';
    
    const version = '#1'; //версия
    const testMode = true;  //режим тестирования
    const timeRepeat = 48; //время хранения выигрыша в часах
    const maxRevolution = 25; //обороты колеса до остановки
    const spinTime = 5; // время вращения в секундах
    const multyPage = false; // false -  работа по всему сайту, true - работа в пределах одной страницы
    const showConfetti = true; // true = включено, false = выключено
    
    
    function getStorageKey(baseKey) {
        if (!multyPage) return baseKey;
        let pagePath = window.location.pathname;
        if (pagePath.endsWith('/') && pagePath.length > 1) {
            pagePath = pagePath.slice(0, -1);
        }
        const pathKey = pagePath || 'home';
        return `${baseKey}_${pathKey}`;
    }
    
    const STORAGE_KEY_WIN_BASE = 'wheel_prize_win';
    const STORAGE_KEY_TIME_BASE = 'wheel_prize_timestamp';
    const STORAGE_KEY_VER_BASE = 'wheel_prize_version';
    
    const STORAGE_KEY_WIN = getStorageKey(STORAGE_KEY_WIN_BASE);
    const STORAGE_KEY_TIME = getStorageKey(STORAGE_KEY_TIME_BASE);
    const STORAGE_KEY_VER = getStorageKey(STORAGE_KEY_VER_BASE);
    
    let wheelOption = [];
    let pendingPrize = null;
    let wheelWrapper = null;
    let isSpinning = false;
    let wheelRendered = false;
    

    function triggerFireworks() {
        if (!showConfetti) return;
        if (typeof confetti !== 'function') return;
        
        confetti({
            particleCount: 100,
            spread: 70,
            origin: { y: 0.6 },
            colors: ['#ff0000', '#00ff00', '#0000ff', '#ffff00', '#ff00ff', '#00ffff']
        });
        
        setTimeout(() => {
            confetti({
                particleCount: 50,
                angle: 60,
                spread: 80,
                origin: { x: 0, y: 0.6 },
                colors: ['#ffd700', '#ffa500']
            });
            confetti({
                particleCount: 50,
                angle: 120,
                spread: 80,
                origin: { x: 1, y: 0.6 },
                colors: ['#ffd700', '#ffa500']
            });
        }, 250);

        setTimeout(() => {
            confetti({
                particleCount: 150,
                spread: 100,
                decay: 0.9,
                gravity: 0.6,
                origin: { y: 0.7 },
                shapes: ['star', 'circle'],
                colors: ['#ff0000', '#00ff00', '#0000ff', '#ffff00', '#ff00ff', '#00ffff', '#ffd700']
            });
        }, 500);
    }
    

    function getWeightedRandom(options) {
        if (!options || options.length === 0) return null;
        const allEmpty = options.every(opt => !opt.probability || opt.probability.trim() === "");
        if (allEmpty) return options[Math.floor(Math.random() * options.length)];
        let totalWeight = 0;
        const weightedOptions = options.map(opt => {
            const weight = Math.max(0, parseFloat(opt.probability) || 0);
            totalWeight += weight;
            return { ...opt, weight };
        });
        if (totalWeight === 0) return options[Math.floor(Math.random() * options.length)];
        let random = Math.random() * totalWeight;
        for (const opt of weightedOptions) {
            if (random < opt.weight) return opt;
            random -= opt.weight;
        }
        return weightedOptions[weightedOptions.length - 1];
    }
    
    function parseWheelData() {
        const wheelDataBlock = document.querySelector('.uc-wheel-data');
        if (!wheelDataBlock) { console.warn('Блок .uc-wheel-data не найден'); return []; }
        const tbody = wheelDataBlock.querySelector('tbody');
        if (!tbody) { console.warn('tbody не найден'); return []; }
        const result = [];
        tbody.querySelectorAll('tr').forEach(function(row) {
            const cells = row.querySelectorAll('td');
            if (cells.length >= 7) {
                result.push({
                    prizeName: cells[0]?.innerHTML?.trim() || "",
                    finalMessage: cells[4]?.innerHTML?.trim() || "",
                    sectorColor: cells[1]?.textContent?.trim() || "",
                    textColor: cells[2]?.textContent?.trim() || "",
                    probability: cells[3]?.textContent?.trim() || "",
                    promocode: cells[5]?.textContent?.trim() || "",
                    link: cells[6]?.textContent?.trim() || ""
                });
            }
        });
        try { wheelDataBlock.remove(); } catch(e) {}
        return result;
    }
    
    function saveWinToStorage(prize) {
        try {
            localStorage.setItem(STORAGE_KEY_WIN, JSON.stringify(prize));
            localStorage.setItem(STORAGE_KEY_TIME, Date.now().toString());
            localStorage.setItem(STORAGE_KEY_VER, version);
        } catch(e) { console.warn('localStorage error:', e); }
    }
    
    function getSavedWin() {
        if (testMode) return null;
        try {
            const savedPrize = localStorage.getItem(STORAGE_KEY_WIN);
            const savedTime = localStorage.getItem(STORAGE_KEY_TIME);
            const savedVersion = localStorage.getItem(STORAGE_KEY_VER);
            if (!savedPrize || !savedTime) return null;
            if (savedVersion !== version) { clearWinStorage(); return null; }
            const prize = JSON.parse(savedPrize);
            const elapsed = Date.now() - parseInt(savedTime, 10);
            const cooldownMs = timeRepeat * 60 * 60 * 1000;
            if (elapsed >= cooldownMs) { clearWinStorage(); return null; }
            return prize;
        } catch(e) { clearWinStorage(); return null; }
    }
    
    function clearWinStorage() {
        localStorage.removeItem(STORAGE_KEY_WIN);
        localStorage.removeItem(STORAGE_KEY_TIME);
        localStorage.removeItem(STORAGE_KEY_VER);
    }

    function updateArtboardHeight(prizeSection) {
        const artboard = prizeSection.closest('.t396__artboard');
        if (artboard && artboard.getAttribute('data-artboard-heightmode') === 'hug') {
            if (typeof t396__updateAutoHeight === 'function') {
                t396__updateAutoHeight(artboard);
            }
        }
    }
    
    function copyPromocode(btn) {
        const block = btn.closest('.promocode-block');
        if (!block) return;
        const textEl = block.querySelector('.promocode-text .tn-atom');
        if (!textEl) return;
        const text = (textEl.textContent || textEl.innerText || "").trim();
        if (!text) return;
        if (navigator.clipboard?.writeText) {
            navigator.clipboard.writeText(text).then(() => showCopyFeedback(btn));
        } else {
            fallbackCopy(text, btn);
        }
    }
    
    function fallbackCopy(text, btn) {
        const ta = document.createElement('textarea');
        ta.value = text;
        ta.style.cssText = 'position:fixed;left:-9999px;top:0';
        document.body.appendChild(ta);
        ta.select();
        try { document.execCommand('copy'); showCopyFeedback(btn); } catch(e) {}
        document.body.removeChild(ta);
    }
    
    function showCopyFeedback(btn) {
        const mol = btn.closest('.tn-molecule');
        if (mol) {
            mol.classList.add('copy-mode');
            setTimeout(() => mol.classList.remove('copy-mode'), 300);
        }
    }
    
    function initPromocodeCopyHandler(wheelBlock) {
        const btn = wheelBlock.querySelector('.promocode-copy');
        if (!btn) return;
        btn.replaceWith(btn.cloneNode(true));
        wheelBlock.querySelector('.promocode-copy')?.addEventListener('click', (e) => {
            e.preventDefault(); copyPromocode(e.currentTarget);
        });
    }
    
    function renderWheel(prizes, container) {
        if (!prizes?.length) { console.warn('Нет призов для колеса'); return; }
        const wheelFortune = container.querySelector('.wheelfortune .tn-atom');
        if (!wheelFortune) { console.warn('Не найден .wheelfortune .tn-atom'); return; }
        wheelWrapper = container.querySelector('.wheel_wrp.t-text');
        if (!wheelWrapper) {
            wheelWrapper = document.createElement('div');
            wheelWrapper.className = 'wheel_wrp t-text';
            wheelFortune.appendChild(wheelWrapper);
        }
        wheelWrapper.innerHTML = '';
        const wheelFortuneEl = container.querySelector('.wheelfortune');
        const width = wheelFortuneEl?.style.width || '400px';
        const height = wheelFortuneEl?.style.height || '400px';
        const widthNum = parseInt(width, 10) || 400;
        const heightNum = parseInt(height, 10) || 400;
        const diameter = Math.min(widthNum, heightNum);
        const sectorCount = prizes.length;
        const angleRad = (Math.PI * 2) / sectorCount;
        const sectorHalfWidth = (diameter / 2) * Math.tan(angleRad / 2);
        prizes.forEach((prize, i) => {
            const sector = document.createElement('div');
            sector.className = 'wheel_sector';
            sector.style.cssText = `
                border-width: ${diameter/2}px ${sectorHalfWidth}px 0;
                border-color: ${prize.sectorColor || '#ccc'} transparent transparent transparent;
                transform-origin: ${sectorHalfWidth}px ${diameter/2}px;
                left: ${(diameter/2) - sectorHalfWidth}px;
                transform: rotate(${(360 * i / sectorCount) + 90}deg);
            `;
            wheelWrapper.appendChild(sector);
            const line = document.createElement('div');
            line.className = 'wheel_line';
            line.style.cssText = `
                width: ${diameter/2}px;
                transform-origin: ${diameter/2}px 1px;
                top: calc(50% - 1px);
                transform: rotate(${((360 * i / sectorCount) + (180 * (sectorCount - 1)) / sectorCount)}deg);
            `;
            wheelWrapper.appendChild(line);
            const textCont = document.createElement('div');
            textCont.className = 'wheel_textcont';
            const textEl = document.createElement('div');
            textEl.innerHTML = prize.prizeName || '';
            textEl.style.cssText = `
                color: ${prize.textColor || '#271306'};
                text-align: center;
                transform: rotate(-90deg);
            `;
            textCont.appendChild(textEl);
            textCont.style.cssText = `
                width: ${diameter/2}px;
                height: ${diameter/2}px;
                transform-origin: ${(diameter/4)}px ${diameter/2}px;
                left: ${(diameter/2) - (diameter/4)}px;
                transform: rotate(${(360 * i / sectorCount) + 90}deg);
            `;
            wheelWrapper.appendChild(textCont);
        });
        const bgUrl = wheelFortune.getAttribute('data-original');
        if (bgUrl && bgUrl !== 'undefined') {
            Array.from(wheelWrapper.children).forEach(el => {
                el.style.display = 'none';
            });
            wheelWrapper.style.backgroundImage = `url(${bgUrl})`;
            wheelWrapper.style.backgroundPosition = 'center center';
            wheelWrapper.style.backgroundSize = 'cover';
            wheelWrapper.style.backgroundRepeat = 'no-repeat';
            wheelWrapper.classList.add('custom-background')
        }
        wheelRendered = true;
    }
    
    function calculateStopAngle(winningIndex, sectorCount) {
        const anglePerSector = 360 / sectorCount;
        const sectorNumber = winningIndex + 1;
        const sectorsToPass = sectorCount - (sectorNumber - 1);
        const targetOffset = sectorsToPass * anglePerSector;
        const fullRotations = maxRevolution * 360;
        const finalAngle = fullRotations + targetOffset;
        return finalAngle;
    }
    
    function spinWheel(targetAngle, onComplete) {
        if (!wheelWrapper || !wheelRendered) {
            if (onComplete) onComplete();
            return;
        }
        if (isSpinning) {
            return;
        }
        isSpinning = true;
        wheelWrapper.classList.add('stop-animation');
        wheelWrapper.style.transition = 'none';
        wheelWrapper.style.transform = wheelWrapper.style.transform || 'rotate(0deg)';
        void wheelWrapper.offsetWidth;
        wheelWrapper.style.transition = `transform ${spinTime}s cubic-bezier(0.15, 0.85, 0.35, 1)`;
        wheelWrapper.style.transform = `rotate(${targetAngle}deg)`;
        const handler = function(e) {
            if (e.propertyName !== 'transform') return;
            wheelWrapper.removeEventListener('transitionend', handler);
            setTimeout(() => {
                isSpinning = false;
                if (typeof onComplete === 'function') onComplete();
            }, 50);
        };
        wheelWrapper.addEventListener('transitionend', handler);
        setTimeout(() => {
            if (isSpinning) {
                isSpinning = false;
                wheelWrapper.removeEventListener('transitionend', handler);
                if (typeof onComplete === 'function') onComplete();
            }
        }, spinTime * 1000 + 500);
    }
    
    function setWheelToPrize(winningIndex, sectorCount) {
        if (!wheelWrapper || !wheelRendered) return;
        const angle = calculateStopAngle(winningIndex, sectorCount);
        wheelWrapper.classList.add('stop-animation');
        wheelWrapper.style.transition = 'none';
        wheelWrapper.style.transform = `rotate(${angle}deg)`;
        setTimeout(() => {
            wheelWrapper.style.transition = `transform ${spinTime}s cubic-bezier(0.15, 0.85, 0.35, 1)`;
        }, 30);
    }
    
    function displayPrizeResults(selectedPrize, wheelBlock) {
        if (!selectedPrize) return;
        
        triggerFireworks();
        
        const prizeSection = wheelBlock.querySelector('.prize-section');
        const formSection = wheelBlock.querySelector('.form-section');
        if (!prizeSection) return;
        if (formSection) {
            formSection.style.display = 'none';
        }
        prizeSection.style.display = 'block';
        const nameEl = prizeSection.querySelector('.prize_name .tn-atom');
        if (nameEl) nameEl.innerHTML = selectedPrize.finalMessage || "";
        if (formSection) {
            const input = formSection.querySelector('input[name="sector_prize"]');
            if (input && selectedPrize.prizeName) {
                const tmp = document.createElement('div');
                tmp.innerHTML = selectedPrize.prizeName;
                input.value = tmp.textContent || tmp.innerText || "";
            }
        }
        const promoBlock = prizeSection.querySelector('.promocode-block');
        if (promoBlock) {
            if (selectedPrize.promocode?.trim()) {
                promoBlock.style.display = 'block';
                const promoText = promoBlock.querySelector('.promocode-text .tn-atom');
                if (promoText) promoText.innerHTML = selectedPrize.promocode;
                initPromocodeCopyHandler(wheelBlock);
            } else {
                promoBlock.style.display = 'none';
            }
        }
        const btnLink = prizeSection.querySelector('.btn-get-prize');
        if (btnLink) {
            if (selectedPrize.link?.trim()) {
                btnLink.style.display = 'block';
                const linkEl = btnLink.querySelector('.tn-atom');
                if (linkEl) linkEl.href = selectedPrize.link;
            } else {
                btnLink.style.display = 'none';
            }
        }
        updateArtboardHeight(prizeSection);
        if (formSection) {
            setTimeout(() => {
                try {
                    formSection.remove();
                    updateArtboardHeight(prizeSection);
                } catch(e) {}
            }, 100);
        }
    }
    
    function findPrizeIndex(prize, options) {
        return options.findIndex(p => 
            p.prizeName === prize.prizeName && p.promocode === prize.promocode
        );
    }
    
    window.t396_onSuccess = function(form) {
        if (!form) return;
        if (form instanceof jQuery) form = form.get(0);
        const formSection = form.closest('.form-section');
        const wheelBlock = form.closest('.uc-wheel-block');
        if (!formSection || !wheelBlock) return;
        const inputsBox = formSection.querySelector('.t-form__inputsbox');
        if (inputsBox) {
            inputsBox.remove();
        }
        if (pendingPrize && wheelRendered) {
            const idx = findPrizeIndex(pendingPrize, wheelOption);
            if (idx >= 0) {
                const angle = calculateStopAngle(idx, wheelOption.length);
                spinWheel(angle, () => {
                    saveWinToStorage(pendingPrize);
                    displayPrizeResults(pendingPrize, wheelBlock);
                    pendingPrize = null;
                });
            } else {
                saveWinToStorage(pendingPrize);
                displayPrizeResults(pendingPrize, wheelBlock);
                pendingPrize = null;
            }
        }
    };
    
    function initWheelHandler(wheelBlock) {
        const formSection = wheelBlock.querySelector('.form-section');
        const submitBtn = formSection?.querySelector('.t-submit');
        if (!submitBtn) return;
        const form = submitBtn.closest('form');
        if (form && form.getAttribute('data-success-popup') === 'y') {
            form.removeAttribute('data-success-popup');
        }
        if (testMode) submitBtn.setAttribute('type', 'button');
        submitBtn.addEventListener('click', function() {
            if (!wheelOption?.length) return;
            const prize = getWeightedRandom(wheelOption);
            const input = formSection.querySelector('input[name="sector_prize"]');
            if (input && prize.prizeName) {
                const tmp = document.createElement('div');
                tmp.innerHTML = prize.prizeName;
                input.value = tmp.textContent || tmp.innerText || "";
            }
            if (testMode) {
                if (wheelRendered) {
                    const idx = findPrizeIndex(prize, wheelOption);
                    if (idx >= 0) {
                        const angle = calculateStopAngle(idx, wheelOption.length);
                        spinWheel(angle, () => displayPrizeResults(prize, wheelBlock));
                    } else {
                        displayPrizeResults(prize, wheelBlock);
                    }
                } else {
                    displayPrizeResults(prize, wheelBlock);
                }
            } else {
                pendingPrize = prize;
            }
        });
    }
    
    function checkAndDisplaySavedWin(wheelBlock) {
        if (testMode) return false;
        const saved = getSavedWin();
        if (!saved) return false;
        if (wheelRendered) {
            const idx = findPrizeIndex(saved, wheelOption);
            if (idx >= 0) setWheelToPrize(idx, wheelOption.length);
        }
        displayPrizeResults(saved, wheelBlock);
        return true;
    }
    
    function init() {
        const wheelBlock = document.querySelector('.uc-wheel-block');
        if (!wheelBlock) return;
        wheelOption = parseWheelData();
        window.wheelOption = wheelOption;
        if (wheelOption.length > 0) {
            renderWheel(wheelOption, wheelBlock);
        }
        const hasWin = checkAndDisplaySavedWin(wheelBlock);
        if (!hasWin) {
            initWheelHandler(wheelBlock);
        } else if (!testMode) {
            setTimeout(() => {
                wheelBlock.querySelector('.form-section')?.remove();
            }, 100);
        }
    }
    
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', () => setTimeout(init, 1500));
    } else {
        setTimeout(init, 1500);
    }
})();
</script>

<style>
/*Центральная точка колеса*/
.wheel_wrp:after {
    width: 25px;
    height: 25px;
    background-color: #fff;
    background-image: radial-gradient(circle at center, #ffffff 0%, #ffffff 40%, #4f4f4f 100%);
}

/*Параметры линий секторов*/
.wheel_line {
    background-color: #fff;
    height: 4px;
}

/*Параметры текста в секторах*/
.wheel_textcont>div {
    font-size: 16px;
    font-weight: 400;
    margin-top: 100px;
    line-height: 1.4;
    text-align: center;
    width: 180px;
}

/*Цвет фона поля промокода*/
.copy-mode {
    background-color: #f3efeb !important;
}

/*Цвет фона успешного сообщения отправки формы*/
.uc-wheel-block .t-form__successbox {
    background: #819576;
    border-radius: 5px;
    padding: 10px;
}

.wheel_wrp {
    width: inherit;
    height: inherit;
    box-sizing: border-box;
    position: relative;
    overflow: hidden;
    border-radius: 50%;
    background-position: center center;
    background-size: cover;
    background-repeat: no-repeat;
    animation: idle-rotation linear 60s infinite;
}

/*Центральная точка колеса*/
.wheel_wrp:after {
    content: "";
    border-radius: 50%;
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    opacity: 1;
    z-index: 199;
}

.wheelfortune  .tn-atom {
    width: inherit;
    height: inherit;
}

.wheel_wrp.stop-animation {
    animation: none;
}

.wheel_wrp.custom-background:after {
    display: none;
}

.wheel_sector {
    width: 0px;
    height: 0px;
    border-style: solid;
    position: absolute;
    top: 0;
    display: flex;
    justify-content: center;
    align-items: center;
}

/*Параметры линий секторов*/
.wheel_line {
    transform: rotate(0deg);
    position: absolute;
    z-index: 99;
}

/*Параметры текста*/
.wheel_textcont {
    position: absolute;
    top: 0;
    z-index: 99999;
    display: flex;
    flex-direction: column-reverse;
    justify-content: flex-end;
    align-items: center;
}

.wheel_textcont div {
    transform: rotate(-90deg);
}

.spin_wheel,
.open-wheel,
.close-wheel,
.wheel-start-btn {
    cursor: pointer;
}

.spin_wheel {
    transition: all 0.2s linear;
}

.spin_wheel:hover {
    filter: sepia(1);
}

.wheel_form,
.form_noactive {
    color: #fff;
}

.uc-wheel-popup .t-popup__container.t-popup__container-static {
    margin: 0;
}

.wheel-start {
    pointer-events: none !important;
    filter: grayscale(1);
}

.wheel-start.wheel-start-active {
    pointer-events: auto !important;
    filter: none;
    cursor: pointer;
}

.promocode-copy {
    cursor: pointer;
}

.promocode-text {
    cursor: text;
}

#allrecords .t-rec .prize-section,
.uc-wheel-data {
    display: none;
}

/*Анимайция вращения*/
@keyframes idle-rotation {
    0% { transform: rotate(0); }
    100% { transform: rotate(360deg); }
}

/*Анимация появления приза */
.prize-section {
    animation: prize-pop 0.6s ease-out;
}

@keyframes prize-pop {
    0% { transform: scale(0.9); opacity: 0; }
    50% { transform: scale(1.05); }
    100% { transform: scale(1); opacity: 1; }
}


@keyframes glow-pulse {
    0%, 100% { opacity: 0; }
    50% { opacity: 1; }
}

canvas[width][height] {
    z-index: 9999999 !important;
    pointer-events: none !important;
    position: fixed !important;
    top: 0 !important;
    left: 0 !important;
}

#allrecords .t-popup:has(.uc-wheel-block) {
    z-index: 99999 !important;
}

</style>
Made on
Tilda