Scratch Card Using React js
In this article, we will build a scratch card using ReactJS. A scratch card contains a reward; when the user scratches that card, the color present on the card will start erasing. And after erasing, the color content (reward) will be visible.
Preview:
Prerequisite
Steps to Create the project:
- Create a react application by using this command
npx create-react-app random-meal-generator
- After creating your project folder, i.e. random-meal-generator, use the following command to navigate to it:
cd random-meal-generator
Project Structure
Package.json
"dependencies": {
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
}
Approach
- InitializeCanvas: A gradient backdrop with randomly picked prize value is used to setup the first look of the scratch card.
- checkIfTouchDevice: Check whether the user’s device is touch capable, and use different event handlers for touch and mouse devices.
- getMouseCoordinates: Gets current mouse/touch coordinates in canvas’ space.
- canvasElement: Detects dragging via mouse or touch, updates the scratch area.
- scratch: Reveals the hidden content by erasing the top layer with destination-out global composite operation and updates the canvas based on the user’s interactions.
- The useEffect hook is used to initialize the canvas of the scratch card. This canvas is initialized with the gradient filled background and with a hidden prize value. Users can use mouse or touch events to interact with the card.
Example:
Javascript
import React, { useEffect, useState } from "react" ; import "./App.css" ; const App = () => { const [prizeValue, setPrizeValue] = useState( "$10" ); // Default value useEffect(() => { const canvasElement = document.getElementById( "scratch" ); const canvasContext = canvasElement.getContext( "2d" ); const initializeCanvas = () => { const gradient = canvasContext .createLinearGradient(0, 0, 135, 135); gradient.addColorStop(0, "#d63031" ); gradient.addColorStop(1, "#fdcb6e" ); canvasContext.fillStyle = gradient; canvasContext.fillRect(0, 0, 200, 200); // Generate a random prize value //from the available options const prizeOptions = [ "$1" , "$5" , "$10" , "$20" , "$25" , "$30" , "$35" , "$40" , "$45" , "$50" ]; const randomPrize = prizeOptions[Math.floor(Math.random() * prizeOptions.length)]; setPrizeValue(randomPrize); }; let mouseX = 0; let mouseY = 0; let isDragging = false ; const eventTypes = { mouse: { down: "mousedown" , move: "mousemove" , up: "mouseup" }, touch: { down: "touchstart" , move: "touchmove" , up: "touchend" } }; let deviceType = "" ; const checkIfTouchDevice = () => { try { document.createEvent( "TouchEvent" ); deviceType = "touch" ; return true ; } catch (e) { deviceType = "mouse" ; return false ; } }; const getMouseCoordinates = (event) => { mouseX = (!checkIfTouchDevice() ? event.pageX : event.touches[0].pageX) - canvasElement.getBoundingClientRect().left; mouseY = (!checkIfTouchDevice() ? event.pageY : event.touches[0].pageY) - canvasElement.getBoundingClientRect().top; }; checkIfTouchDevice(); canvasElement.addEventListener(eventTypes[deviceType] .down, (event) => { isDragging = true ; getMouseCoordinates(event); scratch(mouseX, mouseY); }); canvasElement.addEventListener(eventTypes[deviceType] .move, (event) => { if (!checkIfTouchDevice()) { event.preventDefault(); } if (isDragging) { getMouseCoordinates(event); scratch(mouseX, mouseY); } }); canvasElement.addEventListener(eventTypes[deviceType] .up, () => { isDragging = false ; }); canvasElement.addEventListener( "mouseleave" , () => { isDragging = false ; }); const scratch = (x, y) => { canvasContext .globalCompositeOperation = "destination-out" ; canvasContext.beginPath(); canvasContext.arc(x, y, 12, 0, 2 * Math.PI); canvasContext.fill(); }; initializeCanvas(); }, []); return ( <div className= "container" > <div className= "base" > <h4>You Won</h4> <h3>{prizeValue}</h3> </div> <canvas id= "scratch" width= "200" height= "200" style={{ cursor: 'url("https://media.w3wiki.net/wp-content/uploads/20231030101751/bx-eraser-icon.png"), auto' }} ></canvas> </div> ); }; export default App; |
CSS
* { padding : 0 ; margin : 0 ; box-sizing: border-box; } body { height : 100 vh; background : #eee ; } .container { position : absolute ; transform: translate( -50% , -50% ); top : 50% ; left : 50% ; border-radius: 0.6em ; } .base, #scratch { height : 200px ; width : 200px ; position : absolute ; transform: translate( -50% , -50% ); top : 50% ; left : 50% ; text-align : center ; cursor : grabbing; border-radius: 2em ; } .base { background-color : #ffffff ; font-family : 'Poppins' , sans-serif ; display : flex; flex- direction : column; align-items: center ; justify- content : center ; box-shadow: 0 1.2em 2.5em rgba( 16 , 2 , 96 , 0.15 ); } .base h 3 { font-weight : 600 ; font-size : 1.5em ; color : #17013b ; } .base h 4 { font-weight : 400 ; color : #746e7e ; } #scratch { -webkit-tap-highlight- color : transparent ; -webkit-touch-callout: none ; -webkit-user-select: none ; user-select: none ; } |
- Type the following command in the terminal:
npm start
- Type the following URL in the browser:
http://localhost:3000/
Output:
Contact Us