UNPKG

react-free-scratchcard

Version:

A customizable scratch card component for React with random rewards support

170 lines (165 loc) 6.41 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _react = _interopRequireWildcard(require("react")); var _propTypes = _interopRequireDefault(require("prop-types")); require("./scratchcard.css"); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); } const ScratchCard = _ref => { let { scratchImage, prizeImage, rewardOptions, onRewardSelected, onScratchComplete, scratchedPoints, revealButtonText = "Scratch Now", revealingText = "Revealing...", scratchRadius = 20, autoScratchSpeed = 1, rewardExpiryMinutes = 5, storeReward = true } = _ref; const canvasRef = (0, _react.useRef)(null); const prizeCanvasRef = (0, _react.useRef)(null); const animationRef = (0, _react.useRef)(null); const [isRevealed, setIsRevealed] = (0, _react.useState)(false); const [isAutoScratching, setIsAutoScratching] = (0, _react.useState)(false); const [selectedReward, setSelectedReward] = (0, _react.useState)(null); // Initialize reward selection (0, _react.useEffect)(() => { if (rewardOptions && rewardOptions.length > 0) { const randomIndex = Math.floor(Math.random() * rewardOptions.length); const reward = rewardOptions[randomIndex]; setSelectedReward(reward); if (onRewardSelected) { onRewardSelected(reward); } } }, [rewardOptions, onRewardSelected]); // Setup canvas (0, _react.useEffect)(() => { const canvas = canvasRef.current; if (!canvas) return; const ctx = canvas.getContext("2d"); const width = canvas.width = canvas.offsetWidth; const height = canvas.height = canvas.offsetHeight; // Draw scratch surface if (scratchImage) { const img = new Image(); img.src = scratchImage; img.onload = () => ctx.drawImage(img, 0, 0, width, height); } else { ctx.fillStyle = "#ddd"; ctx.fillRect(0, 0, width, height); } // Draw prize const prizeCanvas = prizeCanvasRef.current; if (!prizeCanvas) return; const prizeCtx = prizeCanvas.getContext("2d"); prizeCanvas.width = width; prizeCanvas.height = height; const imageToUse = selectedReward?.image || prizeImage; if (!imageToUse) return; const prizeImg = new Image(); prizeImg.src = imageToUse; prizeImg.onload = () => prizeCtx.drawImage(prizeImg, 0, 0, width, height); return () => cancelAnimationFrame(animationRef.current); }, [scratchImage, prizeImage, selectedReward]); const autoScratch = () => { if (isAutoScratching || isRevealed) return; setIsAutoScratching(true); const canvas = canvasRef.current; if (!canvas) return; const ctx = canvas.getContext("2d"); ctx.globalCompositeOperation = "destination-out"; const width = canvas.width; const height = canvas.height; const centerX = width / 2; const centerY = height / 2; let angle = 0; let radius = 10; const maxRadius = Math.min(width, height) / 2; const radiusStep = autoScratchSpeed; const angleStep = 0.2; const animate = () => { const x = centerX + radius * Math.cos(angle) + Math.random() * 5; const y = centerY + radius * Math.sin(angle) + Math.random() * 5; ctx.beginPath(); ctx.arc(x, y, scratchRadius, 0, Math.PI * 2); ctx.fill(); angle += angleStep; radius += radiusStep; if (radius < maxRadius) { animationRef.current = requestAnimationFrame(animate); } else { cancelAnimationFrame(animationRef.current); setIsAutoScratching(false); setIsRevealed(true); const result = { reward: selectedReward, points: selectedReward?.points || scratchedPoints || 0 }; if (onScratchComplete) { onScratchComplete(result); } if (storeReward && result.points > 0) { const rewardData = { points: result.points, expiry: Date.now() + rewardExpiryMinutes * 60 * 1000 }; localStorage.setItem("scratchReward", JSON.stringify(rewardData)); } } }; animate(); }; return /*#__PURE__*/_react.default.createElement("div", { className: "scratch-container" }, /*#__PURE__*/_react.default.createElement("button", { className: `scratch-btn ${isRevealed ? "hidden" : ""}`, onClick: autoScratch, disabled: isAutoScratching }, isAutoScratching ? revealingText : revealButtonText), /*#__PURE__*/_react.default.createElement("div", { className: "card-wrapper", onClick: autoScratch }, /*#__PURE__*/_react.default.createElement("canvas", { ref: canvasRef, className: "scratch-canvas mt-2" }), /*#__PURE__*/_react.default.createElement("canvas", { ref: prizeCanvasRef, className: `prize-canvas ${isRevealed ? "visible" : ""}` }))); }; ScratchCard.propTypes = { // Basic usage scratchImage: _propTypes.default.string.isRequired, prizeImage: _propTypes.default.string, // Random rewards rewardOptions: _propTypes.default.arrayOf(_propTypes.default.shape({ image: _propTypes.default.string.isRequired, points: _propTypes.default.number })), onRewardSelected: _propTypes.default.func, // Callbacks onScratchComplete: _propTypes.default.func, // Points scratchedPoints: _propTypes.default.number, // UI revealButtonText: _propTypes.default.string, revealingText: _propTypes.default.string, // Behavior scratchRadius: _propTypes.default.number, autoScratchSpeed: _propTypes.default.number, // Storage rewardExpiryMinutes: _propTypes.default.number, storeReward: _propTypes.default.bool }; ScratchCard.defaultProps = { rewardOptions: [], storeReward: true }; var _default = exports.default = ScratchCard;