UNPKG

scratchcard-reactjs

Version:

**Interactive scratch card functionality made simple for your app! ✨**

175 lines (166 loc) 9.5 kB
import React, { useState, useEffect } from '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 = ".base,\r\n#scratch {\r\n height: 200px;\r\n width: 200px;\r\n position: absolute;\r\n text-align: center;\r\n cursor: grabbing;\r\n border-radius: 1rem;\r\n box-shadow: rgba(0, 0, 0, 0.19) 0px 10px 20px, rgba(0, 0, 0, 0.23) 0px 6px 6px;\r\n}\r\n\r\n.base {\r\n background-color: #ffffff;\r\n font-family: 'Poppins', sans-serif;\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n justify-content: center;\r\n box-shadow: rgba(0, 0, 0, 0.1) 0px 0px 5px 0px,\r\n rgba(0, 0, 0, 0.1) 0px 0px 1px 0px;\r\n}\r\n\r\n.base:hover {\r\n box-shadow: rgba(60, 64, 67, 0.3) 0px 1px 2px 0px,\r\n rgba(60, 64, 67, 0.15) 0px 2px 6px 2px;\r\n}\r\n\r\n.base h4 {\r\n font-size: 19px;\r\n font-weight: 500;\r\n color: #141414;\r\n padding: 1rem;\r\n}\r\n\r\n#scratch {\r\n -webkit-tap-highlight-color: transparent;\r\n -webkit-touch-callout: none;\r\n -webkit-user-select: none;\r\n user-select: none;\r\n}\r\n\r\n/*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImNhcmQuY3NzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztFQUVFLGFBQWE7RUFDYixZQUFZO0VBQ1osa0JBQWtCO0VBQ2xCLGtCQUFrQjtFQUNsQixnQkFBZ0I7RUFDaEIsbUJBQW1CO0VBQ25CLDhFQUE4RTtBQUNoRjs7QUFFQTtFQUNFLHlCQUF5QjtFQUN6QixrQ0FBa0M7RUFDbEMsYUFBYTtFQUNiLHNCQUFzQjtFQUN0QixtQkFBbUI7RUFDbkIsdUJBQXVCO0VBQ3ZCO3NDQUNvQztBQUN0Qzs7QUFFQTtFQUNFOzBDQUN3QztBQUMxQzs7QUFFQTtFQUNFLGVBQWU7RUFDZixnQkFBZ0I7RUFDaEIsY0FBYztFQUNkLGFBQWE7QUFDZjs7QUFFQTtFQUNFLHdDQUF3QztFQUN4QywyQkFBMkI7RUFDM0IseUJBQXlCO0VBQ3pCLGlCQUFpQjtBQUNuQiIsImZpbGUiOiJjYXJkLmNzcyIsInNvdXJjZXNDb250ZW50IjpbIi5iYXNlLFxyXG4jc2NyYXRjaCB7XHJcbiAgaGVpZ2h0OiAyMDBweDtcclxuICB3aWR0aDogMjAwcHg7XHJcbiAgcG9zaXRpb246IGFic29sdXRlO1xyXG4gIHRleHQtYWxpZ246IGNlbnRlcjtcclxuICBjdXJzb3I6IGdyYWJiaW5nO1xyXG4gIGJvcmRlci1yYWRpdXM6IDFyZW07XHJcbiAgYm94LXNoYWRvdzogcmdiYSgwLCAwLCAwLCAwLjE5KSAwcHggMTBweCAyMHB4LCByZ2JhKDAsIDAsIDAsIDAuMjMpIDBweCA2cHggNnB4O1xyXG59XHJcblxyXG4uYmFzZSB7XHJcbiAgYmFja2dyb3VuZC1jb2xvcjogI2ZmZmZmZjtcclxuICBmb250LWZhbWlseTogJ1BvcHBpbnMnLCBzYW5zLXNlcmlmO1xyXG4gIGRpc3BsYXk6IGZsZXg7XHJcbiAgZmxleC1kaXJlY3Rpb246IGNvbHVtbjtcclxuICBhbGlnbi1pdGVtczogY2VudGVyO1xyXG4gIGp1c3RpZnktY29udGVudDogY2VudGVyO1xyXG4gIGJveC1zaGFkb3c6IHJnYmEoMCwgMCwgMCwgMC4xKSAwcHggMHB4IDVweCAwcHgsXHJcbiAgICByZ2JhKDAsIDAsIDAsIDAuMSkgMHB4IDBweCAxcHggMHB4O1xyXG59XHJcblxyXG4uYmFzZTpob3ZlciB7XHJcbiAgYm94LXNoYWRvdzogcmdiYSg2MCwgNjQsIDY3LCAwLjMpIDBweCAxcHggMnB4IDBweCxcclxuICAgIHJnYmEoNjAsIDY0LCA2NywgMC4xNSkgMHB4IDJweCA2cHggMnB4O1xyXG59XHJcblxyXG4uYmFzZSBoNCB7XHJcbiAgZm9udC1zaXplOiAxOXB4O1xyXG4gIGZvbnQtd2VpZ2h0OiA1MDA7XHJcbiAgY29sb3I6ICMxNDE0MTQ7XHJcbiAgcGFkZGluZzogMXJlbTtcclxufVxyXG5cclxuI3NjcmF0Y2gge1xyXG4gIC13ZWJraXQtdGFwLWhpZ2hsaWdodC1jb2xvcjogdHJhbnNwYXJlbnQ7XHJcbiAgLXdlYmtpdC10b3VjaC1jYWxsb3V0OiBub25lO1xyXG4gIC13ZWJraXQtdXNlci1zZWxlY3Q6IG5vbmU7XHJcbiAgdXNlci1zZWxlY3Q6IG5vbmU7XHJcbn1cclxuIl19 */"; styleInject(css_248z); const ScratchCard = ({ data, variant, handleCoverScratched }) => { const [coverRemoved, setCoverRemoved] = useState(false); useEffect(() => { let coverScratched = false; const canvasElement = document.getElementById("scratch"); const canvasContext = canvasElement.getContext("2d"); const coverArea = canvasElement.width * canvasElement.height; const eventTypes = { mouse: { down: "mousedown", move: "mousemove", up: "mouseup", }, touch: { down: "touchstart", move: "touchmove", up: "touchend", }, }; const initializeCanvas = () => { const gradient = canvasContext?.createLinearGradient(0, 0, 135, 135); let color1 = "", color2 = ""; if (variant === "blue") { color1 = "#2c67f2"; color2 = "#62cff4"; } else if (variant === "green") { color1 = "#53db97"; color2 = "#0695b6"; } else { color1 = "#d63031"; color2 = "#fdcb6e"; } gradient?.addColorStop(0, color1); gradient?.addColorStop(1, color2); if (canvasContext && gradient) { canvasContext.fillStyle = gradient; canvasContext.fillRect(0, 0, canvasElement?.width, canvasElement?.height); } }; let mouseX = 0; let mouseY = 0; let isDragging = false; let deviceType = ""; const checkIfTouchDevice = () => { try { document.createEvent("TouchEvent"); deviceType = "touch"; return true; } catch (e) { deviceType = "mouse"; return false; } }; const getMouseCoordinates = (event) => { const touch = "touches" in event ? event.touches[0] : event; mouseX = touch.pageX - canvasElement.getBoundingClientRect().left; mouseY = touch.pageY - canvasElement.getBoundingClientRect().top; }; const handleDown = (event) => { isDragging = true; getMouseCoordinates(event); scratch(mouseX, mouseY); }; const handleMove = (event) => { if (deviceType === "mouse") event.preventDefault(); if (isDragging) { getMouseCoordinates(event); scratch(mouseX, mouseY); } }; const handleUp = () => { isDragging = false; checkScratchedPercentage(); }; const checkScratchedPercentage = () => { if (canvasContext) { const imageData = canvasContext.getImageData(0, 0, canvasElement?.width, canvasElement?.height).data; let scratchedPixels = 0; for (let i = 0; i < imageData.length; i += 4) { // Check if the pixel is scratched (transparent) if (imageData[i + 3] === 0) { scratchedPixels++; } } const currentScratchedPercentage = (scratchedPixels / coverArea) * 100; if (currentScratchedPercentage >= 60) { // Remove the cover setCoverRemoved(true); if (!coverScratched && handleCoverScratched) { handleCoverScratched(); coverScratched = true; } } } }; checkIfTouchDevice(); canvasElement?.addEventListener(eventTypes[deviceType].down, handleDown); canvasElement?.addEventListener(eventTypes[deviceType].move, handleMove); document.addEventListener(eventTypes[deviceType].up, handleUp); const scratch = (x, y) => { if (canvasContext) { canvasContext.globalCompositeOperation = "destination-out"; canvasContext.beginPath(); canvasContext.arc(x, y, 22, 0, 2 * Math.PI); canvasContext.fill(); // Fill in gaps between points for smoother scratching const lastX = canvasElement?.dataset.lastX ? parseInt(canvasElement?.dataset.lastX) : x; const lastY = canvasElement?.dataset.lastY ? parseInt(canvasElement?.dataset.lastY) : y; const dx = x - lastX; const dy = y - lastY; const distance = Math.sqrt(dx * dx + dy * dy); if (distance > 1) { for (let i = 0; i < distance; i++) { const newX = lastX + (dx * i) / distance; const newY = lastY + (dy * i) / distance; canvasContext.beginPath(); canvasContext.arc(newX, newY, 22, 0, 2 * Math.PI); canvasContext.fill(); } } canvasElement.dataset.lastX = x.toString(); canvasElement.dataset.lastY = y.toString(); } }; initializeCanvas(); return () => { canvasElement?.removeEventListener(eventTypes[deviceType].down, handleDown); canvasElement?.removeEventListener(eventTypes[deviceType].move, handleMove); document.removeEventListener(eventTypes[deviceType].up, handleUp); }; }, []); return (React.createElement("div", { className: "container" }, React.createElement("div", { className: "base" }, React.createElement("h4", null, data)), !coverRemoved && React.createElement("canvas", { id: "scratch", width: "200", height: "200" }))); }; export { ScratchCard };