생각보다 이벤트 페이지에서 많이 사용되는 룰렛을 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>

 

 

 

728x90

'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

+ Recent posts