.page
  .spinner
    .item.item_1
      .item__inner
    .item.item_2
      .item__inner
    .item.item_3
      .item__inner
    .item.item_4
      .item__inner
    .item.item_5
      .item__inner
  .overlay
View Compiled
@use postcss-nested;
html {
  height: 100%;
}
body {
  background: #20242D;
  min-height: 100%;
  * {
    box-sizing: border-box;
  }
}

.page {
  background: #333A4A;
  user-select: none;
  overflow: hidden;
  width: 280px;
  height: 480px;
  border-radius: 3px;
  position: absolute;
  top: 50%;
  left: 50%;
  margin: -240px 0 0 -140px;
  box-shadow: 1px 2px 4px rgba(0, 0, 0, .35),
              -1px 0 0 rgba(255, 255, 255, .2);
  
  &_active {
    .item__inner {
      transition: opacity .15s ease-in-out,
                  transform .15s ease-in-out .1s;
      opacity: 0;
    }
    .item__inner_active {
      opacity: 1;
      transform: scale(1);
    }
    .overlay {
      top: 100%;
      left: 100%;
    }
  }
  &_moving {
    .overlay {
      top: 0;
      left: 0;
    }
  }
  &_touch {
  }
}

.overlay {
  position: absolute;
  top: 0;
  left: 70%;
  width: 100%;
  height: 100%;
  z-index: 100;
}

.spinner {
  transition: transform .1s linear;
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  transform: rotate(0);
  transform-origin: 50% 100%;
  user-select: none;
}

.item {
  width: 100%;
  height: 100%;
  user-select: none;
  position: absolute;
  top: 0;
  left: 0;
  transform-origin: 50% 100%;
  &_1 {
    .item__inner {
      background: #9966FF;
    }
  }
  &_2 {;
    .item__inner {
      background: #FF6699;
    }
  }
  &_3 {
    .item__inner {
      background: #00FFCC;
    }
  }
  &_4 {
    .item__inner {
      background: #FEED30;
    }
  }
  &_5 {
    .item__inner {
      background: #6666FF;
    }
  }
  &__inner {
    transition: opacity .15s ease-in-out .1s,
                transform .15s ease-in-out;
    width: 100%;
    height: 100%;
    position: absolute;
    top: 0;
    left: 0;
    border-radius: 3px;
    background: #00FFCC;
    transform: scale(.33);
    transform-origin: 50%;
    &:after {
      content: '';
      position: absolute;
      width: 50%;
      left: 25%;
      top: 100%;
      margin-top: 40px;
      height: 10px;
      border-radius: 10px;
      background: inherit;
    }
  }
}
View Compiled
startPosX = 0
lastPos = 0
angle = 0
itemsOffset = 45
angleCoef = 3.5
maxCount = 2
innerPagesTemplate = [-2, -1, 0, 1, 2]
isClicked = false
page = document.querySelector('.page')
overlay = document.querySelector('.overlay')
innerPages = document.querySelectorAll('.item__inner')
spinner = document.querySelector('.spinner')
oldX = 0
oldY = 0

isTouchDevice = (e) ->
  window.ontouchstart || navigator.maxTouchPoints
if isTouchDevice()
  eventMove = 'touchmove'
  eventStart = 'touchstart'
  eventEnd = 'touchend'
else
  eventMove = 'mousemove'
  eventStart = 'mousedown'
  eventEnd = 'mouseup'

startHanding = (e) ->
  oldX = e.clientX
  oldY = e.clientY
  if isTouchDevice()
    page.classList.add('page_touch')
    startPosX = e.touches[0].clientX
  else
    startPosX = e.clientX 
  page.addEventListener(eventMove, moveHanding, true)

moveHanding = (e)->
  if !isTouchDevice() && oldX == e.clientX && oldY == e.clientY
    return
  oldX = e.clientX
  oldY = e.clientY
  
  if isTouchDevice()
    angle = lastPos + e.touches[0].clientX - startPosX
  else
    angle = lastPos + e.clientX - startPosX
  page.classList.add('page_moving')
  setAngle(angle)
  
finishHanding = (e)->
  page.removeEventListener(eventMove, moveHanding, true)
  
  page.classList.remove('page_moving')
  page.classList.remove('page_touch')
  if angle%(itemsOffset *angleCoef) <= (itemsOffset * angleCoef) / 2
    rotateAngle = angle - angle%(itemsOffset * angleCoef)
  else
    rotateAngle = angle + ((itemsOffset * angleCoef) - angle%(itemsOffset * angleCoef))
    
  if rotateAngle / angleCoef > itemsOffset * maxCount
    lastPos = angleCoef * itemsOffset * maxCount
    rotateAngle = itemsOffset * maxCount
  else if rotateAngle / angleCoef < -itemsOffset * maxCount
    lastPos = -angleCoef * itemsOffset * maxCount
    rotateAngle = -itemsOffset * maxCount
  else
    lastPos = rotateAngle
    rotateAngle = rotateAngle / angleCoef
    
  spinner.style.webkitTransform = "rotate(#{rotateAngle}deg)"
  spinner.style.transform = "rotate(#{rotateAngle}deg)"

innerHanding = (e)->
  e.stopPropagation()
  for innerPage in innerPages
    innerPage.classList.remove('item__inner_active')
  page.classList.toggle('page_active')
  if page.classList.contains('page_active')
    page.removeEventListener(eventStart, startHanding, true)
    e.target.classList.add('item__inner_active')
  else
    page.addEventListener(eventStart, startHanding, true)

setAngle = (angleVal)->
  offset = angleVal / angleCoef
  spinner.style.webkitTransform = "rotate(#{offset}deg)"
  spinner.style.transform = "rotate(#{offset}deg)"
  
page = document.querySelector('.page')
page.addEventListener(eventStart, startHanding, true)

document.addEventListener(eventEnd, finishHanding, true)
  
for innerPage, i in innerPages
  innerPage.parentNode.style.webkitTransform = "rotate(#{itemsOffset * innerPagesTemplate[i]}deg)"
  innerPage.parentNode.style.transform = "rotate(#{itemsOffset * innerPagesTemplate[i]}deg)"
  innerPage.addEventListener('click', innerHanding, true)
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.