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:
CSS
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
body {
height: 100vh;
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 h3 {
font-weight: 600;
font-size: 1.5em;
color: #17013b;
}
.base h4 {
font-weight: 400;
color: #746e7e;
}
#scratch {
-webkit-tap-highlight-color: transparent;
-webkit-touch-callout: none;
-webkit-user-select: none;
user-select: none;
}
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");
let isDragging = false;
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);
};
const scratch = (x, y) => {
canvasContext.globalCompositeOperation = "destination-out";
canvasContext.beginPath();
canvasContext.arc(x, y, 12, 0, 2 * Math.PI);
canvasContext.fill();
};
const getMouseCoordinates = (event) => {
const rect = canvasElement.getBoundingClientRect();
const x = (event.pageX || event.touches[0].pageX) - rect.left;
const y = (event.pageY || event.touches[0].pageY) - rect.top;
return { x, y };
};
const handleMouseDown = (event) => {
isDragging = true;
const { x, y } = getMouseCoordinates(event);
scratch(x, y);
};
const handleMouseMove = (event) => {
if (isDragging) {
event.preventDefault();
const { x, y } = getMouseCoordinates(event);
scratch(x, y);
}
};
const handleMouseUp = () => {
isDragging = false;
};
const handleMouseLeave = () => {
isDragging = false;
};
const isTouchDevice = 'ontouchstart' in window;
canvasElement.addEventListener(isTouchDevice ? "touchstart" : "mousedown", handleMouseDown);
canvasElement.addEventListener(isTouchDevice ? "touchmove" : "mousemove", handleMouseMove);
canvasElement.addEventListener(isTouchDevice ? "touchend" : "mouseup", handleMouseUp);
canvasElement.addEventListener("mouseleave", handleMouseLeave);
initializeCanvas();
// Cleanup event listeners on unmount
return () => {
canvasElement.removeEventListener(isTouchDevice ? "touchstart" : "mousedown", handleMouseDown);
canvasElement.removeEventListener(isTouchDevice ? "touchmove" : "mousemove", handleMouseMove);
canvasElement.removeEventListener(isTouchDevice ? "touchend" : "mouseup", handleMouseUp);
canvasElement.removeEventListener("mouseleave", handleMouseLeave);
};
}, []);
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.geeksforgeeks.org/wp-content/uploads/20231030101751/bx-eraser-icon.png"), auto'
}}
></canvas>
</div>
);
};
export default App;
- Type the following command in the terminal:
npm start - Type the following URL in the browser:
http://localhost:3000/ Output:
|