/** * Lucklott.js * * 행운 복권(스크래치) 기능을 구현한 모듈 * 사용자가 화면을 스크래치하여 당첨 여부를 확인하는 인터랙티브 기능 제공 * * 주요 기능: * 1. 스크래치 캔버스 구현 및 관리 * 2. 당첨 결과 확인 및 표시 * 3. 사용자 인터랙션 처리 * 4. 모달 팝업을 통한 결과 안내 */ $(document).ready(function() { // 기본 변수 초기화 let canvas = $('.scrachMask')[0]; // 스크래치 마스크 캔버스 요소 let ctx = canvas.getContext('2d'); // 캔버스 컨텍스트 let isDrawing = false; // 그리기 상태 플래그 let totalPixels; // 전체 픽셀 수 let clearedPixels = 0; // 지워진 픽셀 수 let isPopupShown = false; // 팝업 표시 상태 let isPrizeRequested = false; // 당첨 결과 요청 여부 플래그 추가 let prize; // 당첨 결과 저장 변수 let prizeResult = null; // 미리 받아온 당첨 결과 let bnfInnb = $('#bnfInnb').val(); // 혜택 고유 번호 // bnfInnb 값 확인 및 유효성 검사 console.log('bnfInnb value:', bnfInnb); if (!bnfInnb || bnfInnb === '' || bnfInnb === 'null' || bnfInnb === 'undefined') { console.error('bnfInnb 값이 올바르지 않습니다:', bnfInnb); // 에러 모달 표시 $('#modal-1 .microModal__title').html('오류'); $('#modal-1 .modal-icon-present').hide(); $('#modal-1 .sub_tit').html('혜택 정보를 불러올 수 없습니다.'); $('#modal-1 p:last-child').html('페이지를 새로고침 해주세요.'); MicroModal.show('modal-1'); return; } // 화면 로딩 시 재고 상태 체크 (추가) checkStockStatus(); /** * 화면 로딩시 재고 상태 체크 */ function checkStockStatus() { $.ajax({ url: '/sopmal/bnft/checkRemainingGoodsQuantity', type: 'POST', contentType: 'application/json', data: JSON.stringify({ bnfInnb: bnfInnb }), success: function(response) { console.log('checkStockStatus response:', response); // 응답 결과 파싱 let result = JSON.parse(response.resBody.result); // 재고가 없는 경우 처리 if (!result.hasAnyStock && result.totalRewards > 0) { // 품절 모달 표시 MicroModal.show('modal-sold-out'); return; } // 재고가 있는 경우 정상 진행 console.log('재고 상태:', result); }, error: function() { console.error('재고 상태 확인 실패'); // 에러 발생 시에도 품절 모달 표시 MicroModal.show('modal-sold-out'); } }); } /** * 캔버스 크기 조정 및 초기화 함수 * 화면 크기에 맞게 캔버스를 조정하고 그라데이션 배경 생성 */ function resizeCanvas() { // 캔버스 크기를 컨테이너에 맞게 설정 canvas.width = $('.luckyScrachBox').width(); canvas.height = $('.luckyScrachBox').height(); // 그라데이션 배경 생성 let gradient = ctx.createLinearGradient(0, 0, canvas.width, canvas.height); gradient.addColorStop(0, '#d3d3d3'); // 시작 색상 gradient.addColorStop(0.25, '#e9e9e9'); // 25% 지점 색상 gradient.addColorStop(0.5, '#ddd'); // 50% 지점 색상 gradient.addColorStop(0.75, '#eee'); // 75% 지점 색상 gradient.addColorStop(1, '#ddd'); // 끝 색상 // 그라데이션으로 캔버스 채우기 ctx.fillStyle = gradient; ctx.fillRect(0, 0, canvas.width, canvas.height); // 전체 픽셀 수 계산 totalPixels = canvas.width * canvas.height; } /** * 마우스/터치 위치 계산 함수 * 이벤트 객체에서 캔버스 내 상대 좌표를 계산 * @param {Event} e - 마우스 또는 터치 이벤트 객체 * @return {Object} - x, y 좌표를 포함한 객체 */ function getOffset(e) { let offset = $(canvas).offset(); // 마우스 또는 터치 이벤트의 좌표 추출 let x = (e.pageX || e.originalEvent.touches[0].pageX) - offset.left; let y = (e.pageY || e.originalEvent.touches[0].pageY) - offset.top; return { x: x, y: y }; } /** * 스크래치 진행률 확인 함수 * 캔버스에서 지워진 픽셀의 비율을 계산하고 진행 상태에 따라 처리 */ function checkClearedPercentage() { let imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); let pixels = imageData.data; clearedPixels = 0; for (let i = 3; i < pixels.length; i += 4) { if (pixels[i] === 0) { clearedPixels++; } } let clearedPercentage = (clearedPixels / totalPixels) * 100; // 80% 이상 지워졌고 팝업이 아직 표시되지 않았다면 실제 참여 처리 if (clearedPercentage > 80 && !isPopupShown && prizeResult) { isPopupShown = true; // 실제 참여 처리 confirmParticipation(); } if (clearedPercentage > 80) { isDrawing = false; } } /** * 당첨 결과 확인 함수 * 서버에 API 요청을 보내 당첨 결과를 받아옴 * @return {Promise} - 당첨 결과를 담은 Promise 객체 */ function determinePrize() { return new Promise((resolve, reject) => { $.ajax({ url: '/sopmal/bnft/participateLuckyLottery', type: 'POST', data: JSON.stringify({ bnfInnb: bnfInnb }), contentType: 'application/json', success: function(response) { let result = JSON.parse(response.resBody.result); if (!result.success) { reject(new Error(response.message)); return; } resolve(result); }, error: function(error) { reject(error); } }); }); } /** * 화면 로딩 시 당첨 결과를 미리 받아와서 이미지 설정 (실제 참여 안함) */ function preloadPrizeAndSetImages() { if (isPrizeRequested) return; isPrizeRequested = true; // bnfInnb를 숫자로 변환하여 전송 let bnfInnbNumber = parseInt(bnfInnb); if (isNaN(bnfInnbNumber)) { console.error('bnfInnb를 숫자로 변환할 수 없습니다:', bnfInnb); $('#modal-1 .microModal__title').html('오류'); $('#modal-1 .modal-icon-present').hide(); $('#modal-1 .sub_tit').html('혜택 정보가 올바르지 않습니다.'); $('#modal-1 p:last-child').html('페이지를 새로고침 해주세요.'); MicroModal.show('modal-1'); return; } // 미리보기 API 호출 (실제 참여 안함) $.ajax({ url: '/sopmal/bnft/previewLuckyLotteryResult', type: 'POST', data: JSON.stringify({ bnfInnb: bnfInnbNumber // 숫자로 전송 }), contentType: 'application/json', success: function(response) { let result = JSON.parse(response.resBody.result); if (!result.success) { // 에러 처리 $('#modal-1 .microModal__title').html('안내'); $('#modal-1 .modal-icon-present').hide(); $('#modal-1 .sub_tit').html(result.message || '참여할 수 없습니다.'); $('#modal-1 p:last-child').html('내일 다시 도전해주세요.'); MicroModal.show('modal-1'); return; } // 미리보기 결과 저장 prizeResult = result; // 당첨 등수에 따른 이미지 설정 let rank = result.rank; if (rank === 0) { // 행운상일 때는 기존 이미지 유지 (서로 다른 이미지) $('.scrachGift').html(`
  • 1등
  • 2등
  • 3등
  • `); } else { // 일반 당첨일 때는 같은 이미지 3개 let imagePath = `/mobile/images/bnft/lucky_award${rank}.svg`; let altText = rank + '등'; $('.scrachGift').html(`
  • ${altText}
  • ${altText}
  • ${altText}
  • `); } }, error: function(error) { console.error('Error in preview:', error); isPrizeRequested = false; } }); } /** * 당첨 결과 표시 함수 * 당첨 결과에 따라 적절한 모달 팝업을 표시 * @param {string} prize - JSON 형식의 당첨 결과 문자열 */ function showPrize(prize) { let result = JSON.parse(prize); if (result.success) { let modalTitle, iconText, subTitle, bottomText; // 행운상 (rank: 0)인 경우 - 응답 데이터에 따라 구분 처리 if (result.rank === 0) { modalTitle = '축하합니다!
    행운상 당첨!'; // 쿠폰 정보가 있는 경우 if (result.couponName && result.couponName.trim() !== '') { iconText = '쿠폰'; subTitle = result.couponName; bottomText = 'MY혜택 > 쿠폰에서 확인하세요.'; } // 마일리지가 있는 경우 else if (result.mileageAmount && result.mileageAmount > 0) { iconText = '포인트'; subTitle = '마일리지 ' + result.mileageAmount + '점 적립'; bottomText = 'MY혜택 > 포인트에서 확인하세요.'; } // bnfRwdCateDvCd로 판단 else { switch (result.bnfRwdCateDvCd) { case '03010': // 쿠폰 iconText = '쿠폰'; subTitle = result.couponName || '할인 쿠폰'; bottomText = 'MY혜택 > 쿠폰에서 확인하세요.'; break; case '03020': // 마일리지 iconText = '포인트'; subTitle = result.mileageAmount > 0 ? '마일리지 ' + result.mileageAmount + '점 적립' : '마일리지 10점 적립'; bottomText = 'MY혜택 > 포인트에서 확인하세요.'; break; case '03030': // 상품 iconText = '선물'; subTitle = result.couponName || '경품'; bottomText = 'MY혜택에서 확인하세요.'; break; case '03040': // 쿠폰 (응답의 bnfRwdCateDvCd가 03040인 경우) iconText = '쿠폰'; subTitle = result.couponName || '할인 쿠폰'; bottomText = 'MY혜택 > 쿠폰에서 확인하세요.'; break; default: // 기본값으로 마일리지 처리 iconText = '포인트'; subTitle = '마일리지 10점 적립'; bottomText = 'MY혜택 > 포인트에서 확인하세요.'; break; } } } // 실제 데이터를 보고 판단 (쿠폰 이름이 있으면 쿠폰으로 처리) else if (result.couponName && result.couponName.trim() !== '') { iconText = '쿠폰'; modalTitle = '축하합니다!
    ' + result.rank + '등 당첨!'; subTitle = result.couponName; bottomText = 'MY혜택 > 쿠폰에서 확인하세요.'; } // 마일리지가 있으면 마일리지로 처리 else if (result.mileageAmount && result.mileageAmount > 0) { iconText = '포인트'; modalTitle = '축하합니다!
    ' + result.rank + '등 당첨!'; subTitle = '마일리지 ' + result.mileageAmount + '점 적립'; bottomText = 'MY혜택 > 포인트에서 확인하세요.'; } // bnfRwdCateDvCd 코드를 보고 판단 else { switch (result.bnfRwdCateDvCd) { case '03020': // 마일리지 iconText = '포인트'; modalTitle = '축하합니다!
    ' + result.rank + '등 당첨!'; subTitle = result.mileageAmount > 0 ? '마일리지 ' + result.mileageAmount + '점 적립' : '마일리지가 적립되었습니다!'; bottomText = 'MY혜택 > 포인트에서 확인하세요.'; break; case '03010': // 쿠폰 iconText = '쿠폰'; modalTitle = '축하합니다!
    ' + result.rank + '등 당첨!'; subTitle = result.couponName || '할인 쿠폰'; bottomText = 'MY혜택 > 쿠폰에서 확인하세요.'; break; case '03030': // 상품 iconText = '선물'; modalTitle = '축하합니다!
    ' + result.rank + '등 당첨!'; subTitle = result.couponName || '경품'; bottomText = 'MY혜택에서 확인하세요.'; break; default: // 기본값으로 마일리지 처리 iconText = '포인트'; modalTitle = '축하합니다!
    ' + result.rank + '등 당첨!'; subTitle = '마일리지가 적립되었습니다!'; bottomText = 'MY혜택 > 포인트에서 확인하세요.'; break; } } // 모달 내용 설정 및 표시 $('#modal-1 .microModal__title').html(modalTitle); $('#modal-1 .modal-icon-present').html(iconText); $('#modal-1 .sub_tit').html(subTitle); $('#modal-1 p:last-child').html(bottomText); MicroModal.show('modal-1'); } else { // 실패 시 기본 모달 표시 MicroModal.show('modal-2'); } } /** * 실제 게임 참여 확정 처리 */ function confirmParticipation() { let bnfInnbNumber = parseInt(bnfInnb); if (isNaN(bnfInnbNumber)) { console.error('bnfInnb를 숫자로 변환할 수 없습니다:', bnfInnb); return; } $.ajax({ url: '/sopmal/bnft/confirmLuckyLotteryParticipation', type: 'POST', data: JSON.stringify({ bnfInnb: bnfInnbNumber, // 숫자로 전송 previewResult: prizeResult }), contentType: 'application/json', success: function(response) { let result = JSON.parse(response.resBody.result); if (result.success) { // 실제 참여 완료 후 결과 표시 showPrize(JSON.stringify(result)); } else { // 실패 처리 $('#modal-1 .microModal__title').html('안내'); $('#modal-1 .modal-icon-present').hide(); $('#modal-1 .sub_tit').html(result.message || '참여 처리 중 오류가 발생했습니다.'); $('#modal-1 p:last-child').html('다시 시도해주세요.'); MicroModal.show('modal-1'); } }, error: function(error) { console.error('Error confirming participation:', error); // 에러 처리 $('#modal-1 .microModal__title').html('오류'); $('#modal-1 .modal-icon-present').hide(); $('#modal-1 .sub_tit').html('참여 처리 중 오류가 발생했습니다.'); $('#modal-1 p:last-child').html('다시 시도해주세요.'); MicroModal.show('modal-1'); } }); } // 윈도우 크기 변경 시 캔버스 크기 조정 $(window).resize(resizeCanvas); // 초기 캔버스 설정 resizeCanvas(); // 화면 로딩 시 당첨 결과 미리 받아오기 preloadPrizeAndSetImages(); // 품절 모달의 메인 이동 버튼 이벤트 설정 $('#btn-go-main').on('click', function() { window.location.href = '/sopmal/bnft/bnftMain'; }); /** * 모달 닫기 버튼 클릭 이벤트 핸들러 * 모달을 닫고 캔버스를 초기화 */ $('.microModal__btnContainer').click(function() { MicroModal.close(); // 모달 닫기 isPopupShown = false; // 팝업 상태 초기화 resizeCanvas(); // 캔버스 초기화 isDrawing = false; // 그리기 상태 초기화 }); /** * 마우스/터치 시작 이벤트 핸들러 * 스크래치 시작 시 그리기 상태 설정 */ $(canvas).on('mousedown touchstart', function(e) { // 이미 그리는 중이거나 80% 이상 지워진 경우 무시 if (isDrawing || clearedPixels / totalPixels * 100 > 80) return; isDrawing = true; }); /** * 마우스/터치 이동 이벤트 핸들러 * 스크래치 효과를 구현하고 진행률 확인 */ $(canvas).on('mousemove touchmove', function(e) { if (!isDrawing) return; e.preventDefault(); // 현재 마우스/터치 위치 계산 let pos = getOffset(e); // 지우기 모드로 설정 (스크래치 효과) ctx.globalCompositeOperation = 'destination-out'; ctx.beginPath(); // 원형 브러시로 지우기 ctx.arc(pos.x, pos.y, 20, 0, Math.PI * 2, false); ctx.fill(); // 스크래치 진행률 확인 checkClearedPercentage(); }); /** * 마우스/터치 종료 이벤트 핸들러 * 그리기 상태 플래그 리셋 */ $(canvas).on('mouseup touchend', function() { isDrawing = false; }); /** * 모달 닫기 버튼 클릭 이벤트 핸들러 * 모달을 닫고 캔버스를 초기화 (품절 상황이 아닐 때만) */ // 기존의 전역 이벤트 핸들러는 제거하고, 필요시에만 등록하도록 변경 function setupNormalModalHandler() { $('.microModal__btnContainer').off('click').on('click', function() { MicroModal.close(); // 모달 닫기 isPopupShown = false; // 팝업 상태 초기화 resizeCanvas(); // 캔버스 초기화 isDrawing = false; // 그리기 상태 초기화 }); } });