<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>