삼각함수 - 원형 슬라이드

by jinam yu

HTML

<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.min.css">
<div id="app">
  <div class="viewport">
    <ul class="slide" :style="getStyles()">
      <li
          v-for="i in len"
          :style="getPosition(i)"
          :class="{ active: isActive(i) }"
          >
        <button type="button">
          <img :src="'https://placeimg.com/480/640/' + i" alt="" />
        </button>
      </li>
    </ul>
    <div class="control">
      <button type="button" @click="prev"><i class="fa-solid fa-chevron-left"></i></button>
      <button type="button" @click="next"><i class="fa-solid fa-chevron-right"></i></button>
    </div>
  </div>
</div>

SCSS

html,
body,
#app {
  width: 100%;
  height: 100%;
  position: relative;
}

#app {
  background-color: #363636;
}

.viewport {
  width: 100%;
  height: 100%;
  position: relative;
  overflow: hidden;
}
.slide {
  position: absolute;
  left: 0;
  top: 0;
  list-style: none;
  padding: 0;
  margin: 0;
  width: 100%;
  height: 100%;
  transition: transform 0.75s cubic-bezier(0.165, 0.84, 0.44, 1);
  li {
    width: 20%;
    height: 0%;
    font-size: 0;
    position: absolute;
    & > button {
      width: 100%;

      position: relative;
      padding-bottom: 130%;
      background-color: #999;
      border: 0;
      border-radius: 8px;
      overflow: hidden;
      cursor: pointer;
      box-shadow: 0 4px 16px 1px rgba(#000, 0.5);

      img {
        position: absolute;
        left: 0;
        top: 0;
        width: 100%;
        height: 100%;
        object-fit: cover;
        transition: transform 0.5s cubic-bezier(0.165, 0.84, 0.44, 1);
      }

      &:hover {
        img {
          transform: scale(1.15);
        }
      }
    }
  }
  li.active {
    button {
      img {
        transform: scale(1.15);
      }
    }
  }
}

.control {
  position: absolute;
  left: 50%;
  bottom: 24px;
  transform: translate(-50%, -50%);

  button {
    text-transform: uppercase;
    line-height: 1;
    border: 0;
    background-color: transparent;
    font-size: 22px;
    font-weight: 700;
    margin-left: 8px;
    margin-right: 8px;
    cursor: pointer;
    color: white;
  }
}

JavaScript

const cnt = 10;
const len = cnt * 2;

new Vue({
	el:'#app',
  data: {
	  len,
    radius: 0,
    rotate:0,
    current:1,
  },
  methods: {
		prev() {
      this.move(this.current + 1);
    },
		next() {
      this.move(this.current - 1);
    },
  	move(i){
    	this.rotate = ((i - 1) / len) * 360;
		  this.current = i;
    },
  	getStyles(){
    	const y = window.innerWidth - window.innerWidth / 4;
		  return { top: `${y}px`, transform: `rotate(${this.rotate}deg)` };
    },
  	getPosition(i){
    	const index = i - 1;
      const step = ((2 * Math.PI) / len) * index;

      const cos = Math.cos(step - Math.PI / 2) * this.radius;
      const sin = Math.sin(step - Math.PI / 2) * this.radius;

      const x = cos;
      const y = sin;

      const r = (index / len) * 360;
      return {
        left: `calc(50% - ${x}px)`,
        top: `calc(50% + ${y}px)`,
        zIndex: y,
        transform: `translate(-50%, -50%) rotate(${-r}deg)`,
        transformOrigin: "center",
      };
    },
    isActive(i){
	    let x = this.current % len;
      if (x === 0) x = len;
      if (x < 0) x = len + x;
      return x === i;
    },
    resize() {
      this.radius = window.innerWidth;
    },
  },
  mounted(){
  	window.addEventListener("resize", this.resize);
	  this.resize();
  }
})