いまさらながら画像スライダーを作った

今までは画像スライダーを実装するのに、**Swiper.js**や**slick**といった便利なプラグインを使っていたのですが、**「プラグインを使わずに自分で作ってみよう」**という急なきまぐれで以下のような画像スライダーを自分で実装してみました。

![img-slider.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/601599/f9b86fe5-62e9-2c02-bea4-d373f11f30a7.gif)

# ソースコード
```html:index.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="./style.css">
</head>
<body>
    <div class="main">
        <div class="container">
            <div class="btn btn-prev js-prev-btn"></div>
            <div class="btn btn-next js-next-btn"></div>
            <div class="slider-container">
                <ul class="slider-list js-slider-container">
                    <li class="slider-item js-slider-item"><img class="slider-img" src="./image/img1.jpg" alt=""></li>
                    <li class="slider-item js-slider-item"><img class="slider-img" src="./image/img2.jpg" alt=""></li>
                    <li class="slider-item js-slider-item"><img class="slider-img" src="./image/img3.jpg" alt=""></li>
                    <li class="slider-item js-slider-item"><img class="slider-img" src="./image/img4.jpg" alt=""></li>
                </ul>
            </div>
        </div>
    </div>
</body>
<script
  src="https://code.jquery.com/jquery-3.5.1.min.js"
  integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0="
  crossorigin="anonymous"></script>
<script src="./app.js"></script>
</html>
```

```css:style.css
.main {
    width: 500px;
    height: 230px;
    position: absolute;
    top: 0;
    left: 0;
    bottom: 0;
    right: 0;
    margin: auto;
}
.container {
    width: 100%;
    height: 100%;
    position: relative;
    /* overflow: hidden; */
}
.btn {
    position: absolute;
    top: 0;
    bottom: 0;
    width: 50px;
    height: 50px;
    margin: auto;
    background: rgb(193, 193, 193);
    border-radius: 100%;
    cursor: pointer;
}
.btn:before {
    content: "";
    width: 10px;
    height: 10px;
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%) rotate(45deg);
    -webkit-transform: translate(-50%, -50%) rotate(45deg);
    -ms-transform: translate(-50%, -50%) rotate(45deg);
}
.btn-prev {
    left: 0;
}
.btn-prev::before {
    border-left: 2px solid #ffffff;
    border-bottom: 2px solid #ffffff;
}
.btn-next {
    right: 0;
}
.btn-next::before {
    border-right: 2px solid #ffffff;
    border-top: 2px solid #ffffff;
}
.slider-container {
    width: 70%;
    height: 100%;
    margin: 0 auto;
    overflow: hidden;
    position: relative;
}
.slider-list {
    display: flex;
    width: 100%;
    height: 100%;
    padding: 0;
    margin: 0;
    position: absolute;
    top:0;
    left: 0;
}
.slider-item {
    list-style: none;
    min-width: 100%;
}
.slider-img {
    width: 100%;
}
```

HTML/CSSはデザインによって適宜変更してください。
ポイントは、ulタグの親要素に`overflow: hidden;`を指定すること、ulタグに`position: absolute;`を指定し、自由に動かせるようにすること、liタグを**横並び**にすることです。

```javascript:app.js
$(function() {
    // DOMの取得
    let $jsPrevBtn = $('.js-prev-btn');
    let $jsNextBtn = $('.js-next-btn');
    let $jsSliderContainer = $('.js-slider-container');
    let $jsSliderItemWidth = $('.js-slider-item').width();

    // 定数定義
    const autoTime = 4000; // 自動でスライドするときの時間間隔
    const speed = 600; // スライドするときの速さ
    const easing = "linear" // アニメーションの挙動

    // 変数定義
    let isSlide = false; // スライド中かどうかを判定する変数
    let timer;

    // 自動スライダー
    const autoPlay = () => {
        // 600ミリ秒ごとに自動でスライドする
        timer = setInterval(() => {
            slideNext();
        },  autoTime);
    }
    autoPlay();

    // Nextボタンが押されたら
    $jsNextBtn.on('click', function() {
        slideNext(); // 次の画像にスライド
    });
    // Prevボタンが押されたら
    $jsPrevBtn.on('click', function() {
        slidePrev(); // 前の画像にスライド
    });

    // 関数定義
    function slideNext() {
        if(!isSlide) { // スライド中でなければ
            isSlide = true; // スライド中に変更
            clearInterval(timer); // 一度タイマーをクリア

            // ulタグを画像1枚分左に移動
            $jsSliderContainer.animate({
                left: -$jsSliderItemWidth
            }, speed, easing, function() { // アニメーションが実行されたあとの処理

                // 左に送った画像をリストの最後の子要素に変更
                $jsSliderContainer.append($('.js-slider-item').eq(0));

                // ulタグを元の位置に戻す
                $jsSliderContainer.css({
                    left: 0
                });
                
                // 自動スライダー再開
                isSlide = false;
                autoPlay();
            });
        }
    }

    function slidePrev() {
        if(!isSlide) { // スライド中でなければ
            isSlide = true; // スライド中に変更
            clearInterval(timer); // 一度タイマーをクリア

            // リスト内の最後の子要素を先頭に移動
            $jsSliderContainer.prepend($('.js-slider-item').eq(-1));
            
            $jsSliderContainer.css({
                left: -$jsSliderItemWidth // ulタグを画像1枚分左に移動（これをしないと先頭にきた画像がすぐに表示されてしまう）
            }).animate({
                left: 0 // // ulタグを元の位置に戻す
            }, speed, easing, function(){
      
                // 自動スライダー再開
                isSlide = false;
                autoPlay();
            });
        }
    }
});
```

# 解説
今回作った画像スライダーは、リストアイテム（liタグ）を横並びにし、その親要素のulタグを移動させることで画像をスライドさせます。
以下に、動作の様子を図にまとめてみました。

![画像スライダー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/601599/be378027-5177-b733-60bf-a7ff12288862.png)

まず、ulタグの親要素には`overflow: hidden;`が指定されているため、画像は1枚分しか表示されません。（ulタグは`position: absoliute;`で浮いたような状態になっているため）
画像を左へスライドさせる場合は、その状態でulタグを左へ移動します。すると、リスト全体が左へずれるため、表示される画像は次のものへと変更されます。
最後に、スライドしたことで消えてしまった画像は、リストの末尾に移動させることで、無限に画像をスライドさせることができます。

画像を右へスライドさせる場合はこの逆のことをしているだけです。

この処理を一定間隔で発火 or ボタンをクリックしたときに発火するようにすることで、**自動** or **手動**の画像スライダーを実装することができます。

# まとめ
今回は、プラグインを使わない画像スライダーを実装してみました。
もっと良い方法、良いコードがあると思いますので、コメントで教えていただけると幸いです。
プラグインは便利で時間もかからない優れものですが、あえてプラグインを使わずに自分で1から実装してみると、勉強になりますよ〜。