[javascript] Slide Animation 만들기

javascript slide animation

이번 포스트는 javascript를 이용하여 Slide Animation을 만드는 방법에 대해 알아보도록 하겠습니다. 개인적으로 Slide Animation은 DOM을 다루는 방법에 대해 공부할 수 있는 가장 좋은 방법이라고 생각합니다.

  1. 뼈대 작성
  2. 가로 슬라이드
  3. 세로 슬라이드
  4. 페이드 슬라이드

일단 간단하게 슬라이드를 구현하는 방법과, 이어서 플러그인으로 만드는 방법에 대해 다룰 것입니다.


뼈대 작성

javascript로 슬라이드를 만들기 이전에, 일단 tag와 css를 작성해야 합니다.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Slide</title>
  <style>
    *{margin:0;padding:0;}
    ul,li{list-style:none;}
    .slide{height:150px;overflow:hidden;}
    .slide ul{height:100%;}
    .slide li{height:100%;}
    .slide li:nth-child(1){background:#faa;}
    .slide li:nth-child(2){background:#afa;}
    .slide li:nth-child(3){background:#aaf;}
    .slide li:nth-child(4){background:#faf;}
  </style>
  <script>
  /* 이 부분에다 script 코드를 작성할 것입니다. */
  </script>
</head>
<body>
  <div class="slide">
    <ul>
      <li></li>
      <li></li>
      <li></li>
      <li></li>
    </ul>
  </div>
</body>
</html>

이렇게 기본적인 tag와 css만 작성하고, 나머지는 javascript로 만들도록 하겠습니다.

편의상 es6 문법을 사용하도록 하겠습니다.
es6 문법 살펴보기 : http://junil-hwang.com/blog/javascript-es6-spec/

그리고 flex layout을 사용할 것입니다.
flex layout : http://junil-hwang.com/blog/css-flex-layout%ec%97%90-%eb%8c%80%ed%95%9c-%ec%9d%b4%ed%95%b4/

CSS Slide를 먼저 이해한 다음에 오면 더욱 좋습니다.
CSS Slide Animation : http://junil-hwang.com/blog/css-slide-animation/


가로 슬라이드

일단 style 초기화부터 해보겠습니다. 가로 슬라이드를 작성하기 위해선 다음과 같이 스타일을 작성해야 합니다.

  1. ul의 너비는 li의 갯수 * 100% (ex: li가 4개 => 400%)
  2. li의 너비는 100% / li의 갯수 (ex: li가 4개 => 25%)
const all = ele => document.querySelectorAll(ele)
const one = ele => document.querySelector(ele)
const slide = _ => {
  const wrap = one('.slide') // .slide 선택
  const target = wrap.children[0] // .slide ul 선택
  const len = target.children.length // .slide li 갯수

  // .slide ul의 너비 조정
  target.style.cssText = `width:calc(100% * ${len});display:flex;transition:1s;`

  // .slide li의 너비 조정
  Array.from(target.children)
  .forEach(ele => ele.style.cssText = `width:calc(100% / ${len});`)
}

window.onload = function () { slide() }

일단 style 초기화를 시켰습니다. 사실 직접적으로 보이는 변화는 없습니다. 이제 화면 전환만 해주면 끝입니다.

const slide = _ => {
  /* ... 이전 코드들 ... */
  let pos = 0
  setInterval(() => {
    pos = (pos + 1) % len // 장면 선택
    target.style.marginLeft = `${-pos * 100}%` // 장면 전환
  }, 1500) // 1500 = 1500ms = 1.5sec. 즉, 1.5초 마다 실행
}

pos = (pos + 1) % len 에 대해 다루자면

1. %는 나머지 연산입니다.
2. len은 현재 4입니다.
[pos = 0 일 경우] 1 % 4 => 1
[pos = 1 일 경우] 2 % 4 => 2
[pos = 2 일 경우] 3 % 4 => 3
[pos = 3 일 경우] 4 % 4 => 0

위의 과정을 반복합니다.

이렇게 슬라이드 애니메이션을 만들어봤습니다. 여기서 중요한 점은

  1. 스타일 초기화
  2. 슬라이드 장면(pos) 선택
  3. pos에 맞게 장면 전환

등이 있습니다.


세로 슬라이드

세로 슬라이드는 script 부분만 조금 다르게 작성하면 됩니다.

const slide = _ => {
  const wrap = one('.slide')
  const target = wrap.children[0]
  const len = target.children.length
  const height = target.clientHeight
  target.style.cssText = `height:calc(100% * ${len});transition:1s`
  Array.from(target.children)
  .forEach(ele => ele.style.cssText = `height:calc(100% / ${len});`)
  let pos = 0
  setInterval(() => {
    pos = (pos + 1) % len
    target.style.marginTop = `${-pos * height}px`
  }, 1500)
}

달라진 부분은 다음과 같습니다.

// 1) height 추가
const height = target.clientHeight
// 2) display:flex 제거
// 3) ul에서 width => height 변경
target.style.cssText = `height:calc(100% * ${len});transition:1s`
// 4) li에서 width => height 변경
Array.from(target.children).forEach(ele => ele.style.cssText = `height:calc(100% / ${len});`)
// 5) 100% 단위의 이동이 아닌, height * px 단위의 이동
target.style.marginTop = `${-pos * height}px`

margin-top을 100%로 할 경우, height의 너비가 아닌 width의 너비 만큼 이동합니다. 따라서 100% 단위가 아니라 height * px 단위로 marginTop을 지정해야 정상적으로 작동합니다.

페이드 슬라이드

const slide = _ => {
  const wrap = one('.slide')
  const target = wrap.children[0]
  const len = target.children.length
  const liStyle = `
    position:absolute;
    left:0;right:0;top:0;bottom:0;transition:1s;opacity:0
  `
  target.style.cssText = `position:relative;`
  Array.from(target.children)
  .forEach(ele => ele.style.cssText = liStyle)
  target.children[0].style.opacity = 1
  let pos = 0
  setInterval(_ => {
    target.children[pos].style.opacity = 0
    pos = (pos + 1) % len 
    target.children[pos].style.opacity = 1
  }, 1500)
}

달라지고 추가 된 부분은 다음과 같습니다.

// 1) li의 style 변경
const liStyle = `
  position:absolute;
  left:0;right:0;top:0;bottom:0;transition:1s;opacity:0
`
Array.from(target.children).forEach(ele => ele.style.cssText = liStyle)

// 2) ul의 style 변경
target.style.cssText = `position:relative;`

// 3) 첫번째 장면 활성화
target.children[0].style.opacity = 1

// 4) 이전 장면 비활성
target.children[pos].style.opacity = 0

// 5) 다음 장면 활성
target.children[pos].style.opacity = 1

fade 같은 경우, ul이 아닌 li를 조작해야 합니다.

마치며

이렇게 javascript를 이용하여 가로,세로,페이드인아웃 슬라이드를 만들어봤습니다.

최종 소스코드는 아래와 같습니다.

가로 슬라이드

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Slide</title>
<style>
  *{margin:0;padding:0;}
  ul,li{list-style:none;}
  .slide{height:150px;overflow:hidden;}
  .slide ul{height:100%;}
  .slide li{height:100%;}
  .slide li:nth-child(1){background:#faa;}
  .slide li:nth-child(2){background:#afa;}
  .slide li:nth-child(3){background:#aaf;}
  .slide li:nth-child(4){background:#faf;}
</style>
<script>
const all = ele => document.querySelectorAll(ele)
const one = ele => document.querySelector(ele)
const slide = _ => {
  const wrap = one('.slide') // .slide 선택
  const target = wrap.children[0] // .slide ul 선택
  const len = target.children.length // .slide li 갯수

  // .slide ul의 너비 조정
  target.style.cssText = `width:calc(100% * ${len});display:flex;transition:1s`

  // .slide li의 너비 조정
  Array.from(target.children)
  .forEach(ele => ele.style.cssText = `width:calc(100% / ${len});`)

  // 화면 전환 실행
  let pos = 0
  setInterval(() => {
    pos = (pos + 1) % len // 장면 선택
    target.style.marginLeft = `${-pos * 100}%`
  }, 1500) // 1500 = 1500ms = 1.5sec. 즉, 1.5초 마다 실행
}
window.onload = function () {
  slide()
}
</script>
</head>
<body>
<div class="slide">
  <ul>
    <li></li>
    <li></li>
    <li></li>
    <li></li>
  </ul>
</div>
</body>
</html>



세로 슬라이드

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Slide</title>
<style>
  *{margin:0;padding:0;}
  ul,li{list-style:none;}
  .slide{height:150px;overflow:hidden;}
  .slide ul{height:100%;}
  .slide li{height:100%;}
  .slide li:nth-child(1){background:#faa;}
  .slide li:nth-child(2){background:#afa;}
  .slide li:nth-child(3){background:#aaf;}
  .slide li:nth-child(4){background:#faf;}
</style>
<script>
const all = ele => document.querySelectorAll(ele)
const one = ele => document.querySelector(ele)
const slide = _ => {
  const wrap = one('.slide') // .slide 선택
  const target = wrap.children[0] // .slide ul 선택
  const len = target.children.length // .slide li 갯수
  const height = target.clientHeight

  // .slide ul의 너비 조정
  target.style.cssText = `height:calc(100% * ${len});transition:1s`

  // .slide li의 너비 조정
  Array.from(target.children)
  .forEach(ele => ele.style.cssText = `height:calc(100% / ${len});`)

  // 화면 전환 실행
  let pos = 0
  setInterval(() => {
    pos = (pos + 1) % len // 장면 선택
    target.style.marginTop = `${-pos * height}px`
  }, 1500) // 1500 = 1500ms = 1.5sec. 즉, 1.5초 마다 실행
}
window.onload = function () {
  slide()
}
</script>
</head>
<body>
<div class="slide">
  <ul>
    <li></li>
    <li></li>
    <li></li>
    <li></li>
  </ul>
</div>
</body>
</html>



페이드 슬라이드

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Slide</title>
<style>
  *{margin:0;padding:0;}
  ul,li{list-style:none;}
  .slide{height:150px;overflow:hidden;}
  .slide ul{height:100%;}
  .slide li{height:100%;}
  .slide li:nth-child(1){background:#faa;}
  .slide li:nth-child(2){background:#afa;}
  .slide li:nth-child(3){background:#aaf;}
  .slide li:nth-child(4){background:#faf;}
</style>
<script>
const all = ele => document.querySelectorAll(ele)
const one = ele => document.querySelector(ele)
const slide = _ => {
  const wrap = one('.slide')
  const target = wrap.children[0]
  const len = target.children.length
  const liStyle = `
    position:absolute;
    left:0;right:0;top:0;bottom:0;transition:1s;opacity:0
  `
  target.style.cssText = `position:relative;`
  Array.from(target.children)
  .forEach(ele => ele.style.cssText = liStyle)
  target.children[0].style.opacity = 1
  let pos = 0
  setInterval(_ => {
    target.children[pos].style.opacity = 0
    pos = (pos + 1) % len 
    target.children[pos].style.opacity = 1
  }, 1500)
}
window.onload = function () {
  slide()
}
</script>
</head>
<body>
<div class="slide">
  <ul>
    <li></li>
    <li></li>
    <li></li>
    <li></li>
  </ul>
</div>
</body>
</html>

다음 포스트는 이렇게 세 종류의 slide를 plugin 형태로 제작하도록 하겠습니다.
다음 포스트 바로 가기 : [javascript] Slide Animation Plugin 만들기


참고자료

  1. CSS Slide http://junil-hwang.com/blog/css-slide-animation/
  2. flex layout http://junil-hwang.com/blog/css-flex-layout%ec%97%90-%eb%8c%80%ed%95%9c-%ec%9d%b4%ed%95%b4/
  3. ES6+ 문법 http://junil-hwang.com/blog/javascript-es6-spec/