생각보다 이벤트 페이지에서 많이 사용되는 룰렛을 jQuery가 아닌 javascript로만 구현해 보고 싶어졌다.
jQuery를 사용한다면 코딩 몇줄로 구현되어 웬만한 브라우저에 지원되겠지만....
이벤트 페이지는 그래픽/디자인이 많이 들어가기 때문에 대체로 이미지를 활용하게 된다.
[작업 플로우]
1. 최소 돌림판, 지시화살표, 버튼 의 엘리먼트가 필요
2. 지시화살표는 상단에 고정
3. 버튼을 클릭시 -> 몇바퀴 360도 회전하다가 멈춤
4. 멈추는 대상은 랜덤이여야함
5. 돌림판은 이미지, 지시화살표는 css로 처리
1. start / stop
클래스 이름을 잘못지었으나, 수정하기 귀찮으니 그대로 진행.
부모객체 div.rouletter의 크기를 400 x 400 으로 정하고, 돌림판의 크기를 350 x 350으로 하여 중간에 위치하게 했다.
버튼을 클릭해서 start로 만든다음 돌림판을 stop누를때까지 회전을 시킬꺼라 keyframe을 사용해주자.
<div class="rouletter">
<div class="rouletter-bg"><div class="rouletter-wacu"></div></div>
<div class="rouletter-arrow"></div>
<button class="rouletter-btn">start</button>
</div>
.rouletter{
position: relative;
width:400px;
height:400px;
}
.rouletter-bg{
position: absolute;
top:50%;
left:50%;
transform:translate(-50%,-50%);
width:350px;
height:350px;
border-radius:350px;
overflow:hidden;
}
.rouletter-wacu{
width:100%;height:100%;
background:#f5f5f2 url('https://m.lifeplanet.co.kr:444/commons/slink/administrator/openInnovation/img/MO)%20360%ED%94%8C%EB%9E%98%EB%8B%9B_%EB%A3%B0%EB%A0%9B%ED%8C%90_476x476_201026.png') no-repeat;
background-size:100%;
transform-origin: center;
}
.rouletter-arrow{
position: absolute;
top:0;
left:50%;
transform:translateX(-50%);
width:1px;
height:1px;
border-right:10px solid transparent;
border-left:10px solid transparent;
border-top:40px solid red;
border-bottom:0px solid transparent;
}
.rouletter-btn{
position: absolute;
top:50%;
left:50%;
transform:translate(-50%,-50%);
width:80px;
height:80px;
border-radius:80px;
background:#fff;
border-image: linear-gradient(to right, #fbfcb9be, #ffcdf3aa, #65d3ffaa);
border: 2px solid;
}
// 돌림판 회전 애니메이션
.on{
animation-name: ani;
animation-duration: 0.1s;
animation-fill-mode: forwards;
animation-iteration-count: infinite;
}
@keyframes ani{
0% {
transform: rotate(0deg);
transition-timing-function: ease-out;
}
100%{
transform: rotate(360deg);
}
}
애니메이션은 클래스명 on에 담아놓고, javascript로 명령해주자.
var rouletter = {
// 부여할 숫자 랜덤으로 하기
random:function(){
min = Math.ceil(0);
max = Math.floor(5);
return Math.floor(Math.random() * (max - min)) + min;
},
// start 버튼
start:function(){
var btn = document.querySelector('.rouletter-btn');
var panel = document.querySelector('.rouletter-wacu');
panel.classList.add('on');
btn.innerText = 'stop';
},
// stop 버튼
stop:function(){
var btn = document.querySelector('.rouletter-btn');
var panel = document.querySelector('.rouletter-wacu');
// 돌림판 형태가 6개로 분할되어있어 필요한 각도를 array로 만들었다.
// 후에 length로 for문으로 돌려서 array처리로 변경하면 보다 동적으로 대처할수 있겠다.
var deg = [60,120,180,240,300,360];
panel.style.transform = 'rotate('+ deg[rouletter.random()] +'deg)';
panel.classList.remove('on');
btn.innerText = 'start';
}
}
document.addEventListener('click',function(e){
var target = e.target
if( target.tagName === 'BUTTON'){
target.innerText === 'start'
? rouletter.start() : rouletter.stop();
}
})
< preview >
2. start
start/stop 상태에서 버튼한번 클릭으로 결과 도출하는 상태도 만들어보자.
그리고 돌림판을 몇바퀴 회전시키다가 멈춤처리를 할것이라 css에서 keyframe을 사용하지 않고 js에서 처리 할것이다.
var rolLength = 6;
var rolette = {
rRandom :function(){
var min = Math.ceil(0);
var max = Math.floor(rolLength-1);
return Math.floor(Math.random() * (max - min)) + min;
},
rRotate:function(){
var panel = document.querySelector(".rouletter-wacu");
var deg = [];
for (var i = 1, len = rolLength; i <= len; i++) {
deg.push((360 / len) * i);
}
var baseDeg = 7200; //20바퀴회전
var stopDeg = baseDeg + deg[rolette.rRandom()]; // + 부여된 랜덤숫자에 해당하는각도
panel.animate(
//keyframe
[
{ transform: "rotate(0deg)" },
{ transform: "rotate(" + stopDeg + "deg)" }
],
// animation option
{
fill: "forwards",
duration: 7000,
easing: "ease-out"
}
);
}
}
document.addEventListener("click", function (e) {
var target = e.target;
if (target.tagName === "BUTTON") {
rolette.rRotate();
}
});
<preview>
위 코드는 최신브라우저에서만 가능하다.
ie 자체는 아예 무시된 경우인데, 아무래도 cross 처리 되는 룰렛도 만들어야겠다.
3. 보안된 룰렛
IE 꺼저라
하.. 그나마 두루두루 쓸수 있는 코드로 변경하면서 좀 보안해보자.
1. 위에 코드들은 회전하는 동안 클릭을 할 수 있기때문에 동작중 클릭막기.
2. css에서는 이미지에 transition옵션을 추가해주고 element.animate()의 대체요소로 setInterval() 활용,
> transition-timing-function: ease-in-out;
> transition: 2s;
var rolLength = 6;
const rRandom = () => {
var min = Math.ceil(0);
var max = Math.floor(rolLength - 1);
return Math.floor(Math.random() * (max - min)) + min;
};
const rRotate = () => {
var panel = document.querySelector(".rouletter-wacu");
var btn = document.querySelector(".rouletter-btn");
var deg = [];
for (var i = 1, len = rolLength; i <= len; i++) {
deg.push((360 / len) * i);
}
var num = 0; //회전을 시킬 횟수
var ani = setInterval(() => {
num++; //인터벌 수치만큼 증가
panel.style.transform = "rotate(" + 360 * num + "deg)";
// 버튼 이벤트 막기
btn.disabled = true; //button,input
btn.style.pointerEvents = "none"; //a 태그
// 횟수 50에 다달았을때,
if (num === 50) {
clearInterval(ani); //회전 삭제
panel.style.transform = "rotate(" + deg[rRandom()] + "deg)"; //랜덤숫자로 멈춤
}
}, 50);
};
document.addEventListener("click", function (e) {
var target = e.target;
if (target.tagName === "BUTTON") {
rRotate();
// transition-timing-function 를 ease-in-out로 적용 햇기때문에 잔여초 이후 버튼활성화
setTimeout(() => {
target.disabled = false;
target.style.pointerEvents = "auto";
console.log("이벤트가 끝남");
}, 5500);
}
});
<preview>
4. 최종
결과 팝업까지 띄어야 프론트에서 할수있는 부분을 다 한거 같다.
랜덤숫자 부분은 사은품 갯수가 한정되어 있고 디비에 데이터도 저장해야 하기 때문에 관련해서는 백엔드에서 예외, 중복 처리 및 데이터 처리를 해야 할것이다.
* html
<!DOCTYPE html>
<html>
<head>
<title>룰렛</title>
<meta charset="UTF-8" />
</head>
<body>
<div id="app"></div>
<script src="src/roulette.js"></script>
</body>
</html>
*css
.rouletter {
position: relative;
width: 400px;
height: 400px;
}
.rouletter-bg {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 350px;
height: 350px;
border-radius: 350px;
overflow: hidden;
}
.rouletter-wacu {
width: 100%;
height: 100%;
background: #f5f5f2
url("https://m.lifeplanet.co.kr:444/commons/slink/administrator/openInnovation/img/MO)%20360%ED%94%8C%EB%9E%98%EB%8B%9B_%EB%A3%B0%EB%A0%9B%ED%8C%90_476x476_201026.png")
no-repeat;
background-size: 100%;
transform-origin: center;
transition-timing-function: ease-in-out;
transition: 2s;
}
.rouletter-arrow {
position: absolute;
top: 0;
left: 50%;
transform: translateX(-50%);
width: 1px;
height: 1px;
border-right: 10px solid transparent;
border-left: 10px solid transparent;
border-top: 40px solid red;
border-bottom: 0px solid transparent;
}
.rouletter-btn {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 80px;
height: 80px;
border-radius: 80px;
background: #fff;
border-image: linear-gradient(to right, #fbfcb9be, #ffcdf3aa, #65d3ffaa);
border: 2px solid;
}
.hidden-input {
display: none;
}
*javascript
var rolLength = 6; // 해당 룰렛 콘텐츠 갯수
var setNum; // 랜덤숫자 담을 변수
var hiddenInput = document.createElement("input");
hiddenInput.className = "hidden-input";
//랜덤숫자부여
const rRandom = () => {
var min = Math.ceil(0);
var max = Math.floor(rolLength - 1);
return Math.floor(Math.random() * (max - min)) + min;
};
const rRotate = () => {
var panel = document.querySelector(".rouletter-wacu");
var btn = document.querySelector(".rouletter-btn");
var deg = [];
// 룰렛 각도 설정(rolLength = 6)
for (var i = 1, len = rolLength; i <= len; i++) {
deg.push((360 / len) * i);
}
// 랜덤 생성된 숫자를 히든 인풋에 넣기
var num = 0;
document.body.append(hiddenInput);
setNum = hiddenInput.value = rRandom();
// 애니설정
var ani = setInterval(() => {
num++;
panel.style.transform = "rotate(" + 360 * num + "deg)";
btn.disabled = true; //button,input
btn.style.pointerEvents = "none"; //a 태그
// 총 50에 다달했을때, 즉 마지막 바퀴를 돌고나서
if (num === 50) {
clearInterval(ani);
panel.style.transform = `rotate(${deg[setNum]}deg)`;
}
}, 50);
};
// 정해진 alert띄우기, custom modal등
const rLayerPopup = (num) => {
switch (num) {
case 1:
alert("당첨!! 스타벅스 아메리카노");
break;
case 3:
alert("당첨!! 햄버거 세트 교환권");
break;
case 5:
alert("당첨!! CU 3,000원 상품권");
break;
default:
alert("꽝! 다음기회에");
}
};
// reset
const rReset = (ele) => {
setTimeout(() => {
ele.disabled = false;
ele.style.pointerEvents = "auto";
rLayerPopup(setNum);
hiddenInput.remove();
}, 5500);
};
// 룰렛 이벤트 클릭 버튼
document.addEventListener("click", function (e) {
var target = e.target;
if (target.tagName === "BUTTON") {
rRotate();
rReset(target);
}
});
// roulette default
document.getElementById("app").innerHTML = `
<div class="rouletter">
<div class="rouletter-bg">
<div class="rouletter-wacu"></div>
</div>
<div class="rouletter-arrow"></div>
<button class="rouletter-btn">start</button>
</div>
`;
<preview>
'css' 카테고리의 다른 글
[scss] scss 컴파일 해보기 (1) | 2022.08.07 |
---|---|
[css] 도넛 차트 그리기 (0) | 2021.06.03 |
[css] 레이아웃 그리기 (0) | 2021.03.30 |
[css] reset파일 만들기 귀찮을 때 (0) | 2021.03.17 |
[css] 크로스브라우징 (0) | 2021.03.17 |