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:
}}
></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: