Kali ini saya akan membagikan salah satu pengalaman project website saya, yaitu membuat sebuah website doorprize dengan animasi gosok kartu atau scratch cards. Sebelum saya mulai, mari kita pahami terlebih dahulu apa itu animasi gosok kartu atau scratch cards. Animasi gosok kartu adalah efek visual yang mirip dengan menggosok kartu voucher atau kartu hadiah untuk mengungkapkan konten tersembunyi di bawahnya. Dalam konteks website, animasi ini digunakan untuk memberikan pengalaman interaktif kepada pengguna, di mana mereka dapat “menggosok” kartu virtual untuk mengungkapkan konten atau hadiah yang tersembunyi di bawahnya.
Pada project ini saya hanya menggunakan HTML, CSS dan Javascript dan menggunakan 2 plugin javascript untuk mempermudah dan mempercepat proses developmentnya yaitu menggunakan jquery dan js-confetti.
Daftar Isi
Penggunaan HTML untuk Struktur Dasar
Pertama-tama, saya akan menggunakan HTML untuk membuat struktur dasarnya terlebih dahulu. Berikut adalah contoh kode HTML sederhana yang akan saya gunakan:
<div id="container">
<div class="container" id="js-container">
<canvas class="canvas" id="js-canvas" width="355" height="225"></canvas>
<div class="notif">
<h2>Selamat</h2>
<h1>Kamu Menang!!</h1>
<h1>Mobil</h1>
<h3>Pin kamu: 232323</h3>
</div>
</div>
</div>
</div>
Dalam struktur ini, saya membuat div dengan id container yang mengelilingi elemen utamanya. Di dalamnya, ada elemen <div> dengan kelas canvas, yang berisi gambar latar belakang kanvas untuk menggosok, dan teks hadiah. Dan <div> dengan class notif adalah informasi yang akan ditampilkan ke use setelah menggosok kartu.
Menyiapkan CSS untuk Penampilan Visual
Selanjutnya, saya akan memberikan style untuk tampilan visual dari class div yang kita miliki menggunakan CSS. Berikut adalah contoh kode CSS yang saya gunakan:
body {
margin:0;
padding:0;
font-family:'Arial';
width:100%;
height:100vh;
overflow-x: hidden;
background: linear-gradient(90deg, rgba(238,58,70,1) 0%, rgba(162,33,113,1) 100%) fixed;
display: flex;
align-items: center;
justify-content: center;
}
.container {
border: 3px solid yellow;
background-color:white;
border-radius:20px;
width: 355px;
height: 225px;
margin: auto;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
-o-user-select: none;
user-select: none;
}
.canvas {
position: absolute;
border-radius:20px;
cursor: url('coin.png'), pointer;
}
.notif {
padding: 0px;
text-align:center;
}
Dalam CSS ini, saya mendefinisikan tata letak dan gaya visual untuk container animasi gosok kartu, elemen gosok kartu itu sendiri, gambar latar belakang, kanvas gosok, dan teks hadiah.
Menambahkan Interaksi dengan JavaScript
Sekarang, mari kita berikan animasi ke dalam id div yang telah kita buat menggunakan JavaScript. Berikut adalah contoh kode JavaScript yang akan menambahkan interaksi ke animasi tersebut:
(function() {
'use strict';
var base_url = window.location.origin;
var isDrawing, lastPoint;
var container = document.getElementById('js-container'),
canvas = document.getElementById('js-canvas'),
canvasWidth = canvas.width,
canvasHeight = canvas.height,
ctx = canvas.getContext('2d'),
image = new Image(),
brush = new Image();
// base64 Workaround because Same-Origin-Policy
image.src = 'card.png';
image.onload = function() {
ctx.drawImage(image, 0, 0);
// Show the form when Image is loaded.
document.querySelectorAll('.notif')[0].style.visibility = 'visible';
};
brush.src = 'brush.png';
canvas.addEventListener('mousedown', handleMouseDown, false);
canvas.addEventListener('touchstart', handleMouseDown, false);
canvas.addEventListener('mousemove', handleMouseMove, false);
canvas.addEventListener('touchmove', handleMouseMove, false);
canvas.addEventListener('mouseup', handleMouseUp, false);
canvas.addEventListener('touchend', handleMouseUp, false);
function distanceBetween(point1, point2) {
return Math.sqrt(Math.pow(point2.x - point1.x, 2) + Math.pow(point2.y - point1.y, 2));
}
function angleBetween(point1, point2) {
return Math.atan2( point2.x - point1.x, point2.y - point1.y );
}
// Only test every `stride` pixel. `stride`x faster,
// but might lead to inaccuracy
function getFilledInPixels(stride) {
if (!stride || stride < 1) { stride = 1; }
var pixels = ctx.getImageData(0, 0, canvasWidth, canvasHeight),
pdata = pixels.data,
l = pdata.length,
total = (l / stride),
count = 0;
// Iterate over all pixels
for(var i = count = 0; i < l; i += stride) {
if (parseInt(pdata[i]) === 0) {
count++;
}
}
return Math.round((count / total) * 100);
}
function getMouse(e, canvas) {
var offsetX = 0, offsetY = 0, mx, my;
if (canvas.offsetParent !== undefined) {
do {
offsetX += canvas.offsetLeft;
offsetY += canvas.offsetTop;
} while ((canvas = canvas.offsetParent));
}
mx = (e.pageX || e.touches[0].clientX) - offsetX;
my = (e.pageY || e.touches[0].clientY) - offsetY;
return {x: mx, y: my};
}
function handlePercentage(filledInPixels) {
filledInPixels = filledInPixels || 0;
if (filledInPixels > 60) {
canvas.parentNode.removeChild(canvas);
// confetti.start();
const canvasa = document.getElementById('js-canvas')
const jsConfetti = new JSConfetti({ canvasa })
setTimeout(() => {
jsConfetti.addConfetti()
}, 0)
setTimeout(() => {
jsConfetti.addConfetti()
}, 2000)
setTimeout(() => {
jsConfetti.addConfetti()
}, 4000)
// alert("success");
}
}
function handleMouseDown(e) {
isDrawing = true;
lastPoint = getMouse(e, canvas);
}
function handleMouseMove(e) {
if (!isDrawing) { return; }
e.preventDefault();
var currentPoint = getMouse(e, canvas),
dist = distanceBetween(lastPoint, currentPoint),
angle = angleBetween(lastPoint, currentPoint),
x, y;
for (var i = 0; i < dist; i++) {
x = lastPoint.x + (Math.sin(angle) * i) - 25;
y = lastPoint.y + (Math.cos(angle) * i) - 25;
ctx.globalCompositeOperation = 'destination-out';
ctx.drawImage(brush, x, y);
}
lastPoint = currentPoint;
handlePercentage(getFilledInPixels(10));
}
function handleMouseUp(e) {
isDrawing = false;
}
})();
Dalam JavaScript ini, kita menggunakan jQuery untuk memudahkan manajemen elemen HTML. Kode ini bertanggung jawab atas penggambaran gambar latar belakang pada kanvas, mengaktifkan gosokan saat pengguna menekan atau menggeser mouse atau layar sentuh, dan memeriksa apakah gosokan sudah selesai dan menampilkan hadiah jika iya.
Implementasi Lengkap HTML, CSS, Javascript
Berikut adalah implementasi lengkap animasi yang telah kita buat menggunakan HTML, CSS, dan JavaScript:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Scratch</title>
<style type="text/css">
body {
margin:0;
padding:0;
font-family:'Arial';
width:100%;
height:100vh;
overflow-x: hidden;
background: linear-gradient(90deg, rgba(238,58,70,1) 0%, rgba(162,33,113,1) 100%) fixed;
display: flex;
align-items: center;
justify-content: center;
}
.container {
border: 3px solid yellow;
background-color:white;
border-radius:20px;
width: 355px;
height: 225px;
margin: auto;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
-o-user-select: none;
user-select: none;
}
.canvas {
position: absolute;
border-radius:20px;
cursor: url('coin.png'), pointer;
}
.notif {
padding: 0px;
text-align:center;
}
</style>
</head>
<body>
<script
src="https://code.jquery.com/jquery-3.5.1.min.js"
integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0="
crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/js-confetti@0.9.0/dist/js-confetti.browser.js"></script>
<div id="container">
<div class="container" id="js-container">
<canvas class="canvas" id="js-canvas" width="355" height="225"></canvas>
<div class="notif">
<h2>Selamat</h2>
<h1>Kamu Menang!!</h1>
<h1>Mobil</h1>
<h3>Pin kamu: 232323</h3>
</div>
</div>
</div>
</div>
<script>
(function() {
'use strict';
var base_url = window.location.origin;
var isDrawing, lastPoint;
var container = document.getElementById('js-container'),
canvas = document.getElementById('js-canvas'),
canvasWidth = canvas.width,
canvasHeight = canvas.height,
ctx = canvas.getContext('2d'),
image = new Image(),
brush = new Image();
// base64 Workaround because Same-Origin-Policy
image.src = 'card.png';
image.onload = function() {
ctx.drawImage(image, 0, 0);
// Show the form when Image is loaded.
document.querySelectorAll('.notif')[0].style.visibility = 'visible';
};
brush.src = 'brush.png';
canvas.addEventListener('mousedown', handleMouseDown, false);
canvas.addEventListener('touchstart', handleMouseDown, false);
canvas.addEventListener('mousemove', handleMouseMove, false);
canvas.addEventListener('touchmove', handleMouseMove, false);
canvas.addEventListener('mouseup', handleMouseUp, false);
canvas.addEventListener('touchend', handleMouseUp, false);
function distanceBetween(point1, point2) {
return Math.sqrt(Math.pow(point2.x - point1.x, 2) + Math.pow(point2.y - point1.y, 2));
}
function angleBetween(point1, point2) {
return Math.atan2( point2.x - point1.x, point2.y - point1.y );
}
// Only test every `stride` pixel. `stride`x faster,
// but might lead to inaccuracy
function getFilledInPixels(stride) {
if (!stride || stride < 1) { stride = 1; }
var pixels = ctx.getImageData(0, 0, canvasWidth, canvasHeight),
pdata = pixels.data,
l = pdata.length,
total = (l / stride),
count = 0;
// Iterate over all pixels
for(var i = count = 0; i < l; i += stride) {
if (parseInt(pdata[i]) === 0) {
count++;
}
}
return Math.round((count / total) * 100);
}
function getMouse(e, canvas) {
var offsetX = 0, offsetY = 0, mx, my;
if (canvas.offsetParent !== undefined) {
do {
offsetX += canvas.offsetLeft;
offsetY += canvas.offsetTop;
} while ((canvas = canvas.offsetParent));
}
mx = (e.pageX || e.touches[0].clientX) - offsetX;
my = (e.pageY || e.touches[0].clientY) - offsetY;
return {x: mx, y: my};
}
function handlePercentage(filledInPixels) {
filledInPixels = filledInPixels || 0;
if (filledInPixels > 60) {
canvas.parentNode.removeChild(canvas);
// confetti.start();
const canvasa = document.getElementById('js-canvas')
const jsConfetti = new JSConfetti({ canvasa })
setTimeout(() => {
jsConfetti.addConfetti()
}, 0)
setTimeout(() => {
jsConfetti.addConfetti()
}, 2000)
setTimeout(() => {
jsConfetti.addConfetti()
}, 4000)
// alert("success");
}
}
function handleMouseDown(e) {
isDrawing = true;
lastPoint = getMouse(e, canvas);
}
function handleMouseMove(e) {
if (!isDrawing) { return; }
e.preventDefault();
var currentPoint = getMouse(e, canvas),
dist = distanceBetween(lastPoint, currentPoint),
angle = angleBetween(lastPoint, currentPoint),
x, y;
for (var i = 0; i < dist; i++) {
x = lastPoint.x + (Math.sin(angle) * i) - 25;
y = lastPoint.y + (Math.cos(angle) * i) - 25;
ctx.globalCompositeOperation = 'destination-out';
ctx.drawImage(brush, x, y);
}
lastPoint = currentPoint;
handlePercentage(getFilledInPixels(10));
}
function handleMouseUp(e) {
isDrawing = false;
}
})();
</script>
</body>
</html>
Baca juga: Javascript Format Input NPWP
Kesimpulan
Dengan menggabungkan HTML, CSS, dan JavaScript, kita dapat membuat animasi gosok kartu yang menarik dan interaktif. Animasi ini dapat memberikan pengalaman yang menyenangkan bagi pengguna website, terutama dalam konteks kontes atau promosi. Dengan mengikuti langkah-langkah di atas, kamu dapat membuat animasi gosok kartu sendiri dan mengintegrasikannya ke dalam proyek website kamu. Selamat mencoba!