Canvas 합성(GlobalCompositeOperation) 기반 자바스크립트 스크래치 복권 효과

안녕하세요!

벌써 18번째 프로젝트까지 달려왔네요. 오늘은 웹 페이지에 생동감을 불어넣고, 방문자들에게 소소한 재미를 선사할 수 있는 ‘자바스크립트 스크래치 복권(Scratch-off Card)’ 기능을 만들어 보려고 합니다.

우리는 어릴 적 문구점 앞이나 이벤트 페이지에서 은색 덮개를 동전으로 긁어 당첨을 확인하던 설렘을 기억합니다. 이 시각적이고 물리적인 경험을 웹 브라우저에서 그대로 재현하는 것은 사용자에게 매우 큰 즐거움을 줍니다. 단순히 클릭 한 번으로 결과를 확인하는 것보다, 직접 마우스를 움직여 숨겨진 요소를 찾아내는 과정은 사용자 인터랙션 측면에서 매우 훌륭한 장치가 됩니다. 이번 포스팅을 통해 캔버스(Canvas)의 심화 기능을 함께 마스터해 봅시다.

캔버스의 기초는 [자바스크립트 그림판 제작(링크)] 포스팅을 먼저 참고하시면 큰 도움이 됩니다.

자바스크립트 스크래치 복권 메인 화면

1. 핵심 기술의 원리

이번 프로젝트의 가장 핵심적인 기술은 HTML5의 Canvas API입니다.

이전에 작성했던 [6번째 프로젝트: 자바스크립트 그림판 만들기]에서도 Canvas API를 다루었었는데요. 이번에는 조금 더 추가해서 다루어보도록 하겠습니다.

평소 우리가 캔버스를 사용할 때는 선을 긋거나 색을 채우는 ‘그리기’에 집중하지만, 스크래치 효과는 반대로 ‘이미 그려진 것을 투명하게 지워내는 것’이 핵심입니다. 이를 가능하게 하는 마법 같은 속성이 바로 globalCompositeOperation입니다. 기본적으로 캔버스에 새로운 도형을 그리면 기존 그림 위에 덮어씌워지지만, 이 속성을 destination-out으로 설정하면 새로 그리는 부분이 기존 그림을 투명하게 깎아내게 됩니다. 즉, 마우스가 지나가는 자리가 지우개처럼 작동하여 그 아래 숨겨져 있던 이미지나 텍스트가 드러나게 되는 원리입니다.

Canvas의 합성 연산 중 destination-out 모드는 새로운 도형이 그려지는 영역의 기존 픽셀을 투명(RGBAA=0)(RGBA의 A=0)으로 치환하여, 마치 물리적인 층을 깎아내는 듯한 효과를 줍니다.


2. 자바스크립트 스크래치 복권 영상

자바스크립트 스크래치 복권 실행 영상

스크래치 효과의 본질을 담고 있는 코드 조각입니다. 이 부분을 이해한다면 캔버스를 활용한 다양한 효과를 응용할 수 있습니다.

const canvas = document.getElementById('scratchCanvas');
const ctx = canvas.getContext('2d');

// 1. 초기 덮개 생성
// 캔버스 전체를 은색(또는 회색)으로 가득 채워 복권의 덮개를 만듭니다.
ctx.fillStyle = '#C0C0C0'; 
ctx.fillRect(0, 0, canvas.width, canvas.height);

// 2. 핵심 설정: 지우개 모드 활성화
// 'destination-out'은 새로 그리는 도형이 기존 콘텐츠와 겹치는 부분을 투명하게 만듭니다.
ctx.globalCompositeOperation = 'destination-out';

// 3. 마우스/터치 드로잉 이벤트
// 사용자의 움직임을 감지하여 원(arc)을 그려 덮개를 지워나갑니다.
function scratch(e) {
    const x = e.offsetX || e.touches[0].clientX - canvas.offsetLeft;
    const y = e.offsetY || e.touches[0].clientY - canvas.offsetTop;

    ctx.beginPath();
    // 긁어내는 영역의 크기를 반지름 20px 정도로 설정합니다.
    ctx.arc(x, y, 20, 0, Math.PI * 2); 
    ctx.fill();
}

3. 프로젝트 구현 중 마주한 고민과 해결책

자바스크립트 스크래치 복권 만들기를 진행하면서 가장 고민했던 부분은 “사용자가 충분히 긁었는지 어떻게 판단할까?” 하는 문제였습니다. 사용자가 아주 조금만 긁었는데 당첨 결과가 바로 노출되거나, 반대로 거의 다 긁었는데도 별다른 피드백이 없다면 사용자 경험이 떨어질 수밖에 없습니다.

이를 해결하기 위해 캔버스의 픽셀 데이터(Pixel Data)를 분석하는 방법을 활용하기로 하였습니다. ctx.getImageData() 함수를 사용하면 캔버스의 모든 픽셀 정보를 가져올 수 있는데, 여기서 알파 채널(투명도) 값이 0인 픽셀의 개수를 카운트했습니다. 전체 픽셀 중 투명해진 픽셀이 50~60% 이상이 되면, 자바스크립트를 통해 캔버스 요소에 페이드 아웃 효과를 주어 덮개가 자연스럽게 완전히 사라지도록 구현했습니다.

전체 픽셀 수(Ptotal)(P_{total}) 중 투명도가 0인 픽셀(Ptransparent)(P_{transparent})의 비율을 계산하여 일정 수치 이상일 때 당첨 결과를 노출합니다.

Scratch Ratio(%)=PtransparentPtotal×100Scratch\ Ratio(\%) = \frac{P_{transparent}}{P_{total}} \times 100

이 과정을 통해 단순히 시각적인 효과를 넘어 데이터 기반의 인터랙션을 설계하는 법을 배울 수 있었습니다.


4. 실무 응용 및 프로젝트의 가치

이 자바스크립트 스크래치 복권 프로젝트는 학습용을 넘어 실무에서도 매우 다양하게 활용될 수 있습니다.

  1. 마케팅 프로모션: 신규 가입자나 이벤트 참여자에게 랜덤 쿠폰을 제공할 때, 긁는 재미를 더해 브랜드 각인 효과를 높일 수 있습니다.
  2. 학습형 퀴즈: 질문의 정답을 스크래치 영역 아래에 숨겨두어 학습자가 직접 확인하게 함으로써 몰입도를 높이는 교육용 콘텐츠로 확장 가능합니다.
  3. 모바일 웹: 스마트폰의 터치 이벤트와 결합하면 실제 동전으로 복권을 긁는 듯한 아날로그적인 감성을 디지털에서 완벽하게 재현할 수 있습니다.

5. 앞으로의 발전 방향

오늘 만든 기초적인 스크래치 기능을 바탕으로, 우리는 더 많은 발전을 이룰 수 있습니다.

  • 멀티 레이어 시스템: 여러 겹의 레이어를 쌓아 단계별로 비밀을 풀어가는 스토리텔링형 콘텐츠를 제작할 수 있습니다.
  • 입자(Particle) 효과: 캔버스를 긁을 때 실제 은색 가루가 사방으로 튀는 물리 효과를 추가한다면 시각적 만족도가 극대화될 것입니다.
  • 사운드 인터랙션: 마우스의 속도에 따라 ‘슥슥’거리는 소리의 크기를 조절하는 오디오 API 연동도 흥미로운 도전 과제가 될 것입니다.

6. 자바스크립트 스크래치 복권

축하합니다!

1등 당첨 💰

마우스나 손가락으로 복권을 긁어보세요!

사실 처음 캔버스를 접했을 때만 해도 화면에 점 하나 찍는 것이 참 낯설고 어렵게 느껴졌던 기억이 납니다. 하지만 이렇게 하나씩 기능을 구현해 보며, 단순히 ‘그리는’ 것을 넘어 ‘지워내는’ 역발상을 통해 새로운 인터랙션을 만들어내는 과정은 코딩이 가진 진정한 매력을 다시금 깨닫게 해주었습니다.

마우스 궤적에 따라 이미지를 투명하게 지우는 로직이 가장 어려웠습니다. destination-out 설정을 통해 실제 복권을 긁는 듯한 시각적 효과를 구현하며 캔버스의 심화 기능을 마스터할 수 있었습니다.

프로젝트가 하나둘 쌓여갈수록 느끼는 점은, 완벽한 코드보다 중요한 것은 ‘사용자가 이 기능을 통해 어떤 즐거움을 느낄까?’를 고민하는 마음이라는 것입니다. 이번 자바스크립트 스크래치 복권 역시 기술적으로는 globalCompositeOperation이라는 속성 하나가 핵심이지만, 그 안에 담긴 설렘과 재미는 우리 블로그가 지향하는 ‘함께 성장하는 즐거움’과 맞닿아 있다고 생각합니다.

혹시 코드를 작성하다가 막히는 부분이 있거나, 여러분만의 멋진 아이디어를 더해 더 화려한 복권을 만드셨다면 언제든지 댓글로 공유해 주세요. 서로의 코드를 보고 영감을 주고받는 과정이야말로 우리가 가장 빠르게 성장하는 길이니까요.

이번 프로젝트를 통해 캔버스의 심화 로직을 이해하셨다면, 지난번에 다뤘던 [17번째 프로젝트: 자바스크립트 텍스트 분석기] 링크를 다시 한번 확인해 보시는 것도 추천드립니다. 시각적인 인터랙션과는 또 다른 ‘데이터 처리’의 묘미를 느끼실 수 있을 거예요.

댓글 남기기