JAVASCRIPT

슬라이드 이펙트 만들기 7 - 버튼, 썸네일

hyejeong3283 2023. 4. 14. 16:44
728x90
반응형

버튼과 썸네일을 누르면 해당 이미지로 바뀌는 이미지 슬라이드를 만들었습니다.

 

HTML

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>07. 슬라이드 이펙트</title>

    <link rel="stylesheet" href="css/reset.css">
    <link rel="stylesheet" href="css/slider.css">
</head>
<body class="img07 bg07 font07">
    <header id="header">
        <h1>Javascript Slider Effect07</h1>
        <p>슬라이드 이펙트 : 썸네일 슬라이드(버튼, 썸네일)</p>
        <ul>
            <li><a href="sliderEffect01.html">1</a></li>
            <li><a href="sliderEffect02.html">2</a></li>
            <li><a href="sliderEffect03.html">3</a></li>
            <li><a href="sliderEffect04.html">4</a></li>
            <li><a href="sliderEffect05.html">5</a></li>
            <li><a href="sliderEffect06.html">6</a></li>
            <li class="active"><a href="sliderEffect07.html">7</a></li>
        </ul>
    </header>
    <!-- //header-->

    <main id="main">
        <div class="slider__wrap">
            <div class="slider__img"></div>
            <div class="slider__thumb"></div>
            <div class="slider__btn">
                <a href="#" class="prev" title="이전이미지">prev</a>
                <a href="#" class="next" title="다음이미지">next</a>
            </div>
        </div>
    </main>
    <!-- //main-->

    <footer id="footer">
        <a href="mailto:hyejeong3283@gmail.com">hyejeong3283@gmail.com</a>
    </footer>
    <!-- //footer-->
</body>
</html>

CSS

<style>
/* slider__wrap */
.slider__wrap {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    width: 800px;
    height: 450px;
    box-shadow: 0 50px 100px rgba(0, 0, 0, 0.4);
}
.slider__img {
    position: relative;
    width: 100%;
    height: 100%;
    overflow: hidden;
}
.slider__img img {
    position: absolute;
    width: 100%;
    height: 100%;
    background-size: cover;
    object-fit: cover;
    opacity: 0;
    transform: scale(1.1);
    transition: all 500ms ease-in-out;
}
.slider__img img.active {
    opacity: 1;
    transform: scale(1);
}
.slider__thumb {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, 140px);
    width: 100px;
    display: flex;
    justify-content: center;
    gap: 10px;
}
.slider__thumb img {
    cursor: pointer;
    border: 2px solid transparent;
}
.slider__thumb img.active {
    border: 2px solid #fff;
}
.slider__btn a {
    position: absolute;
    top: 0;
    width: 40px;
    height: 100%;
    display: flex;
    justify-content: center;
    align-items: center;
    font-size: 12px;
    color: #fff;
    background-color: rgba(0,0,0,0.2);
    transition: all 300ms ease-in-out;
}
.slider__btn a.next {
    right: 0;
}
.slider__btn a:hover {
    background-color: rgba(0,0,0,0.5);
}
</style>

SCRIPT

let images = [
    "./img/sliderEffect02-min.jpg",
    "./img/sliderEffect03-min.jpg",
    "./img/sliderEffect06-min.jpg",
    "./img/sliderEffect09-min.jpg",
    "./img/sliderEffect10-min.jpg"
];

images라는 변수에 각 이미지 파일 경로를 문자열로 표현하여 배열 형태로 저장했습니다.

 

<script>  
    function imageSlider(parent, images){
        let currentIndex = 0;  

        // 선택자
        let slider = {
            parent: parent,
            images: parent.querySelector(".slider__img"),
            thumbnails: parent.querySelector(".slider__thumb"),
            prevBtn: parent.querySelector(".slider__btn .prev"),
            nextBtn: parent.querySelector(".slider__btn .next")
        }

        // 이미지 출력하기
        slider.images.innerHTML = images.map((image, index) => {
            return `<img src="${image}" alt="이미지${index}">`;
        }).join("");

        // 이미지 활성화(active)하기
        let imageNodes = slider.images.querySelectorAll("img");
        imageNodes[currentIndex].classList.add("active");

        // 썸네일 이미지 출력하기
        slider.thumbnails.innerHTML = slider.images.innerHTML;

        // 썸네일 활성화(active)하기
        let thumbnailNodes = slider.thumbnails.querySelectorAll("img");
        thumbnailNodes[currentIndex].classList.add("active");

        // 썸네일 이미지 클릭하기
        // for(let i=0; i<thumbnailNodes.length; i++){
        //     thumbnailNodes[i].addEventListener("click", function(){
        //         slider.thumbnails.querySelector("img.active").classList.remove("active");
        //         thumbnailNodes[i].classList.add("active");

        //         imageNodes[currentIndex].classList.remove("active");
        //         currentIndex = i;
        //         imageNodes[currentIndex].classList.add("active");
        //     });
        // }

        thumbnailNodes.forEach((btn, index) => {
            btn.addEventListener("click", function(){
                slider.thumbnails.querySelector("img.active").classList.remove("active");
                btn.classList.add("active");

                imageNodes[currentIndex].classList.remove("active");
                currentIndex = index;
                imageNodes[currentIndex].classList.add("active");
            });
        });

        // 왼쪽 버튼 클릭하기
        slider.prevBtn.addEventListener("click", function(){
            imageNodes[currentIndex].classList.remove("active");
            currentIndex--;

            // 0 4 3 2 1 0 4 3 2 . . .
            if(currentIndex < 0) currentIndex = images.length - 1;
            imageNodes[currentIndex].classList.add("active");

            // 썸네일
            slider.thumbnails.querySelector("img.active").classList.remove("active");
            thumbnailNodes[currentIndex].classList.add("active");
        });

        // 오른쪽 버튼 클릭하기
        slider.nextBtn.addEventListener("click", function(){
            imageNodes[currentIndex].classList.remove("active");

            // 1 2 3 4 0 1 2 3 4 . . .
            currentIndex = (currentIndex + 1) % images.length;
            imageNodes[currentIndex].classList.add("active");

            // 썸네일
            slider.thumbnails.querySelector("img.active").classList.remove("active");
            thumbnailNodes[currentIndex].classList.add("active");
        });
    }

    imageSlider(document.querySelector(".slider__wrap"), images);
</script>

함수 imageSlider는 두 개의 매개변수 parent와 images를 받습니다. 

parent는 이미지 슬라이더를 삽입할 부모 요소를 가리키는 객체이고, images는 슬라이더에 사용할 이미지들의 경로가 담긴 배열입니다.

 

let currentIndex = 0;

현재 인덱스를 0으로 초기화하고, parent 객체 내부의 요소들을 선택합니다.

 

선택자

  • parent: 이 객체가 속한 부모 요소를 참조합니다.
  • images: 이 객체의 이미지 슬라이더 영역을 참조합니다. querySelector() 메소드를 사용하여 해당 요소를 선택합니다.
  • thumbnails: 이 객체의 썸네일 이미지 영역을 참조합니다. querySelector() 메소드를 사용하여 해당 요소를 선택합니다.
  • prevBtn: 이 객체의 이전 버튼을 참조합니다. querySelector() 메소드를 사용하여 해당 요소를 선택합니다.
  • nextBtn: 이 객체의 다음 버튼을 참조합니다. querySelector() 메소드를 사용하여 해당 요소를 선택합니다.

이렇게 생성된 slider 객체는 나중에 이미지 슬라이더를 다루는 함수에서 참조됩니다.

 

이미지 출력하기

slider.images 프로퍼티는 이미지 슬라이더의 이미지 영역을 참조합니다. innerHTML 속성을 사용하여 해당 영역의 내용을 변경합니다.

map() 메소드를 사용하여 images 배열을 반복하면서, 배열 요소 image와 인덱스 index를 받아서, img 요소를 생성합니다. img 요소의 src 속성에는 image 요소의 값을 지정하고, alt 속성에는 "이미지"라는 문자열과 인덱스 값을 조합하여 지정합니다. map() 메소드가 반환하는 배열을 join() 메소드로 문자열로 변환하여 innerHTML 속성에 할당합니다.

 

이미지 활성화하기

이미지 슬라이더의 첫 번째 이미지를 활성화합니다. 

slider.images.querySelectorAll() 메소드를 사용하여 해당 영역의 모든 img 요소를 선택하고, 선택한 img 요소들의 목록을 imageNodes 변수에 할당합니다.

currentIndex 변수는 현재 활성화된 이미지의 인덱스를 나타냅니다.

이미지 슬라이더를 처음 로드하면, currentIndex는 0이 되고, 이 코드는 imageNodes 목록에서 첫 번째 img 요소를 선택하여 활성화합니다.

classList.add("active") 메소드를 사용하여 선택한 img 요소에 해당 요소를 활성화하여 슬라이더의 첫 번째 이미지가 화면에 표시됩니다.

 

썸네일 이미지 출력하기

slider.images.innerHTML은 이미지 영역의 내부 HTML 코드를 문자열로 반환합니다. 

slider.thumbnails.innerHTML에 이 값을 할당하면, 썸네일 영역의 내부 HTML 코드가 이미지 영역과 같아지므로, 

이미지 슬라이더의 이미지 영역에 있는 img 요소들이 썸네일 영역에도 동일하게 출력됩니다.

 

썸네일 이미지 클릭하기

forEach문을 사용하여 이미지 수만큼 썸네일에 클릭 이벤트를 등록하고

썸네일 중에 active가 붙은 이미지에 active를 제거하고 클릭한 이미지에 active를 붙여 썸네일 테두리에 선택 효과를 줍니다.

메인 이미지중에 active가 붙어있는 건 active를 제거하고 현재 인덱스 값을 선택한 인덱스 값으로 변경하여 해당 이미지에 active 클래스를 명을 부여합니다.

썸네일 클릭시 메인 이미지도 같은 이미지를 출력합니다.

 

왼쪽, 오른쪽 버튼 클릭하기

prevBtn 버튼에 클릭 이벤트를 등록하여 클릭시 현재 이미지의 active를 제거하고 인덱스 값을 1씩 줄입니다.

그리고 현재 인덱스 값이 0보다 작으면 현재 인덱스 값에 이미지 갯수 -1를 저장하고, 그 인덱스 값의 이미지에 active를 부여합니다.

썸네일도 같은 방식으로 active를 제거하고, 추가합니다.

nextBtn 버튼도 위와 같은 방식으로 이벤트를 등록합니다.

 

즉, prevBtn과 nextBtn 버튼을 클릭하면 메인 이미지와 썸네일 이미지의 인덱스를 늘리고 줄여 변경시켜줍니다.