Javascript - 그리드 레이아웃 이미지 교체 효과

핀터레스트에서 영감을 받아 3x3 그리드 레이아웃에서 이미지를 순차적으로 교체하는 웹 효과를 자바스크립트로 구현한 예시입니다. 더미 데이터는 photo.json에 저장되어 있습니다.자바스크립트에서는 이미지를 순차적으로 교체하는 스크립트를 구현했습니다.

Javascript - 그리드 레이아웃 이미지 교체 효과
Photo by Patrick Hendry / Unsplash

Reference

핀터레스트에서 재미있는 효과를 봤습니다.

3X3의 그리드 레이아웃에서 이미지가 순차적으로 교체되는 효과인데, 단순하면서도 인상적입니다. 영상에서 많이 쓰이는 효과지만, 자바스크립트를 이용하면 웹에서도 비교적 수월하게 구현할 수 있습니다.

Preview

아래 영상은 실제로 구현했을 때 모습입니다. 순차적으로 이미지가 교체되는데, 약간의 딜레이를 주어서 다음 그리드 부분에서는 한 박자씩 늦게 교차되도록 구현합니다.

0:00
/

HTML

html은 아주 단순합니다. app이라는 id를 갖고 있는 main 태그에 내용이 삽입되도록 javascript에서 모든 부분을 제어합니다.

<!doctype html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <link rel="stylesheet" href="style.css">
    <title>Photo Changer</title>
</head>
<body>

<main id="app"></main>

<script src="main.js" defer async type="module" ></script>
</body>
</html>

photo.json

간단한 더미 데이터를 마련해서 json 파일에 담아두었습니다. 이미지의 출처는 모두 pexels.com 에서 가져왔습니다.

{
  "data": [
    {
      "id": 1,
      "url": "https://images.pexels.com/photos/58997/pexels-photo-58997.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1"
    },
    {
      "id": 2,
      "url": "https://images.pexels.com/photos/4043573/pexels-photo-4043573.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1"
    },
    {
      "id": 3,
      "url": "https://images.pexels.com/photos/9335893/pexels-photo-9335893.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1"
    },
    {
      "id": 4,
      "url": "https://images.pexels.com/photos/9291125/pexels-photo-9291125.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1"
    }
  ]
}

SCSS

스타일 시트 부분도 그다지 복잡하지 않은 구성입니다. 기본적인 배치는 grid 를 이용해서 4x3 그리드를 만들고 있습니다.

@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@100;200;300;400;500;600;700;800;900&display=swap');
*{
margin:0;
  padding:0;
  box-sizing: border-box;
}

html,body{
  width: 100%;
  height: 100%;
  font-family: 'Poppins', sans-serif;
}
body{
  display: flex;
  justify-content: center;
  align-items: center;
}
img{
  width: 100%;
  max-width: 100%;
  height: 100%;
  object-fit: cover;
  border:0;
}

#app{
  --col : 4;
  --row: 3;
  --size: 800px;
  display: grid;
  grid-template-columns: repeat(var(--col), 1fr);
  grid-template-rows: repeat(var(--row),1fr);
  width:var(--size);
  height:var(--size);
  border-radius: 1vw;
  overflow: hidden;
  position: relative;
  &::before,&::after{
    width:100%;
    height: 100%;
    position: absolute;
    top:0;
    left:0;
  }
  &::before{
    content: '';
    background-color: hsla(220,50%,20%,0.5);
  }
  &::after{
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 5rem;
    color: white;
    text-transform: uppercase;
    content: 'Happy Dogs'
  }
  figure{
    height: 100%;
    overflow:hidden;
  }
}

Javascript

import photos from "./photo.json" assert {type: 'json'}
const {data} = photos
const delay = 500

// 주요 기능: 이미지와 이미지 변환기를 설정합니다.
const main = () => {
    // app element를 가져옵니다.
    const app = document.getElementById('app')

    // app element 내부의 이미지 개수를 9개로 설정합니다.
    setImages(app, 12)

    // app element 내부의 모든 이미지를 가져와서 이미지 변환기에 설정합니다.
    setChanger(Array.from(app.querySelectorAll('img')))
}
// 이미지들을 설정하는 함수
const setImages = (app, count) => {
    // 이미지 개수만큼 반복
    for (let i = 0; i < count; i++) {
        // figure와 image 엘리먼트 생성
        const figure = document.createElement('figure')
        const image = document.createElement('img')
        // 이미지를 figure에 추가
        figure.appendChild(image)
        // app에 figure 추가
        app.appendChild(figure)
        image.src= data[0].url; // 초기 이미지 설정
    }
}
// 이미지 배열을 받아 타이머를 이용해 이미지를 변경하는 함수
const setChanger = (imageArray) => {
    let timer; // 타이머 변수
    imageArray.forEach((item, index) => {
        timer = setTimeout(() => imageChanger(item,data,delay), delay * index); // 지연 시간에 따라 이미지 변경
    });
};
/**
 * 이미지를 주어진 데이터 배열의 url로 변경하는 함수
 * @param {HTMLImageElement} image - 변경할 이미지 요소
 * @param {Array} data - 이미지 URL을 포함하는 데이터 배열
 * @param {number} delay - 이미지 변경 간격 (밀리초)
 */
const imageChanger = (image, data, delay) => {
    let index = 0; // 데이터 배열의 인덱스
    const timer = setInterval(() => {
        if (index < data.length) {
            image.src = data[index].url; // 이미지 소스 변경
            index++; // 다음 데이터로 이동
        } else {
            clearInterval(timer); // 타이머 중지
        }
    }, delay);
}

window.addEventListener('load', main)

대략적인 작업의 흐름은 아래와 같습니다.

photo.json 에서 이미지 링크들을 가져와서 반복문을 이용해 배치하고, 순차적으로 교체하는 일을 하는 스크립트입니다.

delay 상수에서 이미지가 교체되는 빠르기지연시간 을 모두 같이 쓰고 있습니다. 필요하다면 두 부분으로 나누는 것도 나쁘지 않습니다.

main 함수 안에서는 setImagessetChanger 함수를 호출하고 있는데, 각각 이미지를 배치하고, 그 배치된 이미지마다 순차적으로 교체되는 일을 하도록 작성했습니다.

setChanger 함수에서는 배치된 이미지들의 배열을 넘겨받아서, 그 배열 안에서 img 태그를 하나하나 꺼내 다시 imageChanger 함수로 각각의 이미지를 넘겨주는데, 그 안에서 setInterval 비동기 함수를 이용해 img 태그의 이미지를 순차적으로 교체하는 일을 합니다.

교체할 이미지는 photo.json에서 가져옵니다.

그리고 마지막으로 전체적인 작업을 실행할 수 있는 main 함수를 호출하는 것으로 스크립트를 마무리합니다.