lucky-wheel-component
Version:
A lucky wheel component for Vue 3 and React
92 lines (82 loc) • 6.4 kB
JavaScript
'use strict';
var jsxRuntime = require('react/jsx-runtime');
var react = require('react');
function styleInject(css, ref) {
if ( ref === void 0 ) ref = {};
var insertAt = ref.insertAt;
if (typeof document === 'undefined') { return; }
var head = document.head || document.getElementsByTagName('head')[0];
var style = document.createElement('style');
style.type = 'text/css';
if (insertAt === 'top') {
if (head.firstChild) {
head.insertBefore(style, head.firstChild);
} else {
head.appendChild(style);
}
} else {
head.appendChild(style);
}
if (style.styleSheet) {
style.styleSheet.cssText = css;
} else {
style.appendChild(document.createTextNode(css));
}
}
var css_248z = ".lucky-wheel {\r\n width: 100%;\r\n max-width: 600px;\r\n margin: 0 auto;\r\n}\r\n\r\n.wheel-box {\r\n width: 100%;\r\n padding-bottom: 100%;\r\n position: relative;\r\n}\r\n\r\n#wheel {\r\n position: absolute;\r\n width: 100%;\r\n height: 100%;\r\n border-radius: 50%;\r\n overflow: hidden;\r\n background: url('./assets/wheel-bg.png') center center no-repeat;\r\n background-size: 100% 100%;\r\n transform-origin: center center;\r\n transition: transform 5s cubic-bezier(0.46, 0.03, 0, 0.96);\r\n}\r\n\r\n/* 转盘内部样式 */\r\n.wheel-inner {\r\n position: absolute;\r\n width: 83%;\r\n height: 83%;\r\n top: 50%;\r\n left: 50%;\r\n transform: translate(-50%, -50%);\r\n border-radius: 50%;\r\n overflow: hidden;\r\n}\r\n\r\n.prize-part {\r\n height: 100%;\r\n width: 50%;\r\n position: absolute;\r\n top: 0;\r\n left: 50%;\r\n transform-origin: left center;\r\n box-sizing: border-box;\r\n}\r\n\r\n.prize-bg {\r\n width: 100%;\r\n height: 100%;\r\n position: absolute;\r\n top: 0;\r\n left: 0;\r\n}\r\n\r\n.prize-text {\r\n transform: translate(0, -50%) rotate(90deg);\r\n width: 100%;\r\n text-align: center;\r\n position: absolute;\r\n top: 50%;\r\n left: 20%;\r\n font-size: 1rem;\r\n padding-top: 0.3rem;\r\n box-sizing: border-box;\r\n white-space: nowrap;\r\n z-index: 1;\r\n}\r\n\r\n.pointer {\r\n position: absolute;\r\n left: 50%;\r\n top: 50%;\r\n transform: translate(-50%, -50%);\r\n width: 120px;\r\n height: 120px;\r\n background: url('./assets/game-arrow.png') center center no-repeat;\r\n background-size: contain;\r\n cursor: pointer;\r\n z-index: 2;\r\n}\r\n\r\n.pointer.disabled {\r\n cursor: not-allowed;\r\n opacity: 0.6;\r\n}\r\n\r\n/* 弹窗样式 */\r\n.modal {\r\n position: fixed;\r\n top: 0;\r\n left: 0;\r\n width: 100%;\r\n height: 100%;\r\n background: rgba(0, 0, 0, 0.5);\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n z-index: 1000;\r\n}\r\n\r\n.modal-content {\r\n background: white;\r\n padding: 20px;\r\n border-radius: 8px;\r\n position: relative;\r\n min-width: 300px;\r\n text-align: center;\r\n}\r\n\r\n.close {\r\n position: absolute;\r\n right: 10px;\r\n top: 5px;\r\n font-size: 24px;\r\n cursor: pointer;\r\n color: #666;\r\n}\r\n\r\n#modalMessage {\r\n margin: 20px 0;\r\n font-size: 16px;\r\n line-height: 1.5;\r\n white-space: pre-line;\r\n} ";
styleInject(css_248z);
const LuckyWheel = ({ prizes, initialDrawCount = 3, colors = ["#f31f49", "#fff7d7", "#a71d77"], textColors = ["#f3f1f1", "#a8213c", "#f3f1f1"], additionalTurns = 10, messages = {
noChance: '您没有抽奖机会了'
}, onStart, onComplete }) => {
const [currentRotation, setCurrentRotation] = react.useState(0);
const [isRotating, setIsRotating] = react.useState(false);
const [drawCount, setDrawCount] = react.useState(initialDrawCount);
const lastClickTime = react.useRef(0);
const [lastRotation, setLastRotation] = react.useState(0);
const wheelRef = react.useRef(null);
const getPrizeStyle = react.useCallback((index) => {
const perAngle = 360 / prizes.length;
const p = perAngle / 2;
const d = Math.tan(p * Math.PI / 180) * 100;
const x = (100 - d) / 2;
return {
transform: `rotateZ(${perAngle / 2 - 90 + perAngle * index}deg)`,
clipPath: `polygon(0% 50%, 100% ${x}%, 100% ${100 - x}%)`
};
}, [prizes.length]);
const startRotation = react.useCallback(() => {
const now = Date.now();
if (now - lastClickTime.current < 1000 || isRotating || drawCount <= 0) {
return;
}
lastClickTime.current = now;
if (drawCount <= 0) {
return;
}
setIsRotating(true);
// 随机选择奖品
const prizeIndex = Math.floor(Math.random() * prizes.length);
const perAngle = 360 / prizes.length;
const targetAngle = prizeIndex * perAngle + perAngle / 2;
// 计算旋转角度
const additionalRotation = 360 * additionalTurns; // 额外旋转10圈
const totalRotation = currentRotation - additionalRotation - targetAngle + lastRotation;
// 应用旋转动画
if (wheelRef.current) {
wheelRef.current.style.transform = `rotate(${totalRotation}deg)`;
}
// 触发开始事件
onStart?.({ prizeIndex, drawCount: drawCount - 1 });
// 动画结束后处理
setTimeout(() => {
setDrawCount(prev => prev - 1);
setIsRotating(false);
setCurrentRotation(totalRotation);
setLastRotation(targetAngle);
const prize = prizes[prizeIndex];
onComplete?.({ index: prizeIndex, prize });
}, 5000);
}, [currentRotation, drawCount, isRotating, prizes, onStart, onComplete, messages]);
return (jsxRuntime.jsx("div", { className: "lucky-wheel", children: jsxRuntime.jsxs("div", { className: "wheel-box", children: [jsxRuntime.jsx("div", { id: "wheel", ref: wheelRef, children: jsxRuntime.jsx("div", { className: "wheel-inner", children: prizes.map((prize, index) => (jsxRuntime.jsxs("div", { className: "prize-part", style: getPrizeStyle(index), children: [jsxRuntime.jsx("div", { className: "prize-bg", style: { background: colors[index % colors.length] } }), jsxRuntime.jsx("div", { className: "prize-text", style: { color: textColors[index % textColors.length] }, children: prize.prize })] }, index))) }) }), jsxRuntime.jsx("div", { className: `pointer ${drawCount === 0 ? 'disabled' : ''}`, onClick: startRotation })] }) }));
};
module.exports = LuckyWheel;