15
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【初学者向け】プラグインなし。Carousel Slider 自作してみた。

Last updated at Posted at 2021-01-16

カルーセルスライダーを自作しました。また初学者用に図解化してみたので是非参考にして下さい。

完成イメージ

82a575a43988db6d2debfeb19731f00a.gif

箱が小さくて申し訳ないのですが、こんな感じです。
ごく一般なカルーセルスライダーです。

ちなみにカルーセルとスライダーの違いですが、カルーセルは別名メリーゴーランドというのですが、簡単に言ってしまうと同じところをグルグル回ってるという意味です。

対してスライダーは上下左右にスライドしていれば、それはスライダーです。

正直ループするかしないかの違いで機能自体に大きな違いはなく、意味的なくくりもないみたいです。

下準備

準備として、以下のことをします。
※コードを書かない場合は読み飛ばして下さい。

【準備するもの】
・画像(以下にリンク貼ります)
・html/css/jsファイル(インラインで書いても良いけど分けた方がわかりやすいよ!)

階層やファイル名は以下に合わせると良いでしょう。
スクリーンショット 2021-01-17 1.50.29.png

コーディング

マークアップ

html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <!-- Google font -->
    <link rel="preconnect" href="https://fonts.gstatic.com">
    <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@100;300;400;500;700;900&display=swap" rel="stylesheet">
    <!-- FontAwesome -->
    <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.15.1/css/all.css" integrity="sha384-vp86vTRFVJgpjF9jiIGPEEqYqlDwgyBgEF109VFjmqGmIY/Y4HV4d3Gp2irVfcrp" crossorigin="anonymous">
    <!-- Original CSS -->
    <link rel="stylesheet" href="./css/style.css">
</head>
<body>
    <!-- header -->
    <header>
        <h1>Carousel Slider</h1>
        <p>center line<br></p>
    </header>
    <!-- carousel slider -->
    <div class="carousel">
        <ul>
            <li><img src="./img/img1.png"></li>
            <li><img src="./img/img2.png"></li>
            <li><img src="./img/img3.png"></li>
        </ul>
        <!-- prev next button -->
        <span class="prev"></span>
        <span class="next"></span>
    </div>    
    <!-- JQuery CDN -->
    <script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
    <!-- Original JS -->
    <script src="./js/main.js"></script>
</body>
</html>

GoogleFontFontAwesomeはCDN書いていますが、今回は使いません。
JQueryは絶対使うので最新版を入れましょう。
body内の説明ですが、headerのpは中心が、わかりやすい様にをつけています。あとあと説明に使うのでとりあえず着けときましょう。
div.carouselですが、大きく2つのブロックに分かれいます。まずulには画像を3枚並べる様にliを3つ並べています。あとはボタンとしてspanを2つ書いています。

スクリーンショット 2021-01-17 2.05.29.png

画面はこんな感じになれば良いと思います。

レイアウト変更

次にCSSを書いていきます。ここは何回かに分けて説明します。
まずResetを書き、headerは中央寄せだけ書きます。

css
@charset "utf-8";
/* Reset */
html,body,h1,p,ul,li{
    padding: 0;
    margin: 0;
}
ul{
    list-style: none;
}
img{
    border: none;
    vertical-align: bottom;
    max-width: 100%;
} 
/* header
--------------------------------------- */
header{
    text-align: center;
}

スクリーンショット 2021-01-17 2.10.18.png

こんな感じになったかと思います。
次に、carousel slider部分を整えます。
まずやるべきは中央寄せです。今回はブロック要素を中央寄せなのでmarginを使えば良いですね。

css(追加分のみ記載)
.carousel{
    width: 100px;
    margin: 0 auto;
}

スクリーンショット 2021-01-17 2.14.57.png

こんな感じです。
ここで冒頭でも触れた、headerのpのセンターライン(↓)の出番です。
画像3枚がちゃんとこの、ラインのど真ん中に来てることを確認しましょう。

次に、横並べをします。
横に並べたい要素はimgではなくliなのでulflexをかけます。
ulがflexコンテナ、liがflexアイテムです。

css(追加分のみ記載)
.carousel > ul{
    display: flex;
}

とりあえずflexかけました。画面を確認します。

スクリーンショット 2021-01-17 2.22.04.png

画像ちっさw
はい、これはflexの特性が関係してます。

flexは、flexアイテムを横並べにするのですが、親要素の幅に対して均等配置する特性もあります。

つまり今回で言う親要素とはulのことを指しています。そしてそのulの幅と言うのは、100pxになります。

ん?と思った人もいるかも知れません、ulに幅指定なんかしてないのに、なぜ100pxなのか?

初心に戻って、CSSの略称はなんだったでしょうか?Cascading Style Sheetsです。Cascad=滝、つまり上から下へ流れることを示しています。もっと言うと、親から何かを受け継ぐと考えても良いでしょう。

では今回は「どこから」「何を受け継いでいるか」と言うとulの親である.carouselから幅を受け継いでると言うことになります。つまり100pxです。

話を戻します。
クイズです。

・100px内に画像が3つ収納されています。
・画像の元々の幅は100pxです。
・元の画像サイズのまま、横に並べたいです。

どうしたら良いでしょうか・・・

答えは簡単です。flexアイテムで自動的に均等化されることは回避できないので、flexコンテナ自体を大きくします。

ではどれだけ大きくするかと言うと、画像の枚数分、倍にします。今回は3枚なので3倍にします。

CSSで3倍にする方法は、2通りあります。純粋に300%と書くかcalc(100% * 3)と書くかです。
後者は一見メンドそうに見えますが、おそらく玄人になればなるほど後者を選ぶでしょう。
理由は明示的に3倍してるとわかるからです。現場に出れば、複数人でコードを管理します。パッと他人のコードを見た時にどう言う意図でコードを書いたかが伝わるのは後者の気がします。

なので今回はcalcを使います。

css
.carousel > ul{
    width: calc(100% * 3);
    display: flex;
}

スクリーンショット 2021-01-17 2.47.00.png

画面はこんな感じです。
画像サイズも元通りで、横並べになりました。
ただ注目すべき点は、センターラインの位置ですね。一番最初の要素が中心にいて、そこから右に後続の要素が並んでいます。

なんか変な気がしますが、カルーセルなので問題ありません。最終的にはこのセンターラインにいる要素しか表示しないので後続がどこにいようが関係ないです。

では続きを書いていきます。
次にやるべきは、ボタンを作成し、適正な位置に配置します。
まずボタンの見た目だけやります。

css(追加分のみ記載)
.carousel > span{
    border-radius: 50%;
    background: orange;
    color: white;
    font-size: 30px;
}

スクリーンショット 2021-01-17 2.56.23.png

こんな感じです。難しいことはありませんね。
次に配置です。配置といえばpositionですが、今回は特定の要素を基準にして、ボタンを配置したいです。

今回の場合、特定の要素と言うのは、このカルーセル大元である.carouselです。

こう言った、特定の要素に対して配置をしたい場合、親要素にrelative、子要素にabsoluteを使います。

では親要素は.carousel、子要素はspanとうことになるのでコードを追加していきます。

css
.carousel{
    width: 100px;
    margin: 0 auto;
    position: relative;
}
.carousel > span{
    position: absolute;
    top: 50%;
    border-radius: 50%;
    background: orange;
    color: white;
    font-size: 30px;
}
.carousel > span.prev{
    left: 0;
}
.carousel > span.next{
    right: 0;
}

こんな感じですね。順番に解説します。
まず.carouselは先程も言った通り、spanの基準点にしたいのでposition: relative;をかけてます。

次に、基準点を作ったので、この基準点に対して、絶対配置をおこないます。

もちろん絶対配置の対象はspanなので.carousel > spanposition: absolute;をかけています。

ただposition: absolute;をかけるだけだと、効力が発揮されないので、プラスアルファーとしてtop: 50%;をつけています。これは親要素の50%の位置に配置することを意味しています。

そして、prevボタンは左に、nextボタンは右に配置したいので、それぞれ個別にleft: 0;,right: 0;をかけています。

ここまで出来たら画面を確認します。

スクリーンショット 2021-01-17 3.23.08.png

いろいろ問題がありますね・・・
2箇所あります。
まずは、ボタンの形が歪んでます。これはabsoluteの特性が関係しています。
absoluteをかけると、floatの様に、元居た階層から離れ(浮いてしまい)、別の階層(上の階層)に要素が移動してしまいます。と同時にblock要素になります。

結果から先に言うと、block要素になったことで、line-height: 1.5;が付与されました。

spanはインライン要素です。インライン要素にはline-height: 1.0;が初期値としてあります。

対してblock要素には、line-height: 1.5;が付与されています。

ここまでくるとなんとなく想像つくかと思いますが、absoluteをかけたことで、block要素になり、line-height: 1.0;から1.5になったと言うことになります。なので縦長なレイアウトになってしまったと言うことですね。

2つ目の問題として、ボタンが中央に配置されてないことですが、これは、positionの配置方法が、要素の中心ではなく、左上だからです。なので、要素の半分のサイズ分、上にズラす必要があります。

css(変更セレクタ全体を記載)
.carousel > span{
    position: absolute;
    top: 50%;
    border-radius: 50%;
    background: orange;
    color: white;
    font-size: 30px;
    margin-top: -15px;
    line-height: 1.0;
}

これで、良い位置に配置されたかと思います。

スクリーンショット 2021-01-17 3.42.46.png

これでレイアウト完成です!
次はJSを書いていくのですが、JSを書く上で重要なのは、テストや検証をどれだけやるかです。

なので先にテストと検証をやっていきます。

JQuery

なにをテストしていくかと言うと、今回のメイン機能である、スライドです。
まず手作業で実装したい機能を実現させます。
以下のコード2行を追加します。

css
.carousel > ul{
    width: calc(100% * 3);
    display: flex;

    margin-left: -100%;
    transition: .3s;
}

スクリーンショット 2021-01-17 3.52.06.png

画面はこうです。今回は検証を使うので画面全体を撮ってます。
では検証の部分を見ていきます。以下の通りにしましょう。

スクリーンショット 2021-01-17 3.54.45.png

Elementsの中からulを選び、Stylesの中から指定のポイントをクリックします。
以下の動画を見ながらやっってみて下さい。

8595d80412cecc95b0144963eb0996ec.gif

margin-left: -100%;についてる、チェックボックスをつけたり消したりすることで、擬似的にスライダーを実装することが出来ます。

これは検証ツール上でCSSのオンオフが出来ることを利用した手法です。
この様に、目的を明確にする為に検証ツールは使うべきです。

さて、話を戻しますが、ここまでやって、margin-left: -100%;をつけたり消したりすることで、スライダー実装することが出来ることがわかりました。なのでこれをJSに落とし込みます。

では機能の概要はなんとなくわかりました。「●●をしたらxxをする」と言うのがJSイベントの基本なのですが、●●とxxなにが当てはまるでしょうか?

スライダーなので「ボタンを押したらスライドする」がただしそうですが、いまいち具体性にかけます。とはいえ考えても進まないので、今言った「ボタンを押したらスライドする」をコードに落とし込みます。

main.js
$(function(){
  $('.carousel > span').on('click', function(){
    console.log($(this));
  });
});

こんな感じです。これも鉄則ですが、必ず1手順づつconsoleで確認しましょう。

ここでconsoleすることで、このクリックイベントは動くことを証明出来るので後々エラーになった場合に、エラー原因の候補から外れます。
ではボタンクリックでconsoleログを確認します。

スクリーンショット 2021-01-17 4.19.51.png

この様に、prevを押したらspan.prevがnextを押したらspan.nextconsoleログに表示されましたか?

$(this)と言うのは、自身の要素と言う意味があり、今回の場合イベントの発火元を$('.carousel > span')と曖昧なセレクタしています。

$('.carousel > span')nextprevの2つ存在することは理解できるかと思います。
ただしどちらがクリックされるかはわからないので、あえて$('.carousel > span')とすることで、どちらがクリックされてもイベントが発火する様な設計になっています。

ただprevnextで別々の動きをするので$(this)を使用することで、クリックされた要素のみを取得できる様にしてます。

では次に、処理を分岐させましょう。
当たり前ですが、まずconsoleログで確認します。
コードとしてはif文を使います。prevが押されたらnextが押されたらと表示します。

main.js
$(function(){
  $('.carousel > span').on('click', function(){
    if($(this).hasClass('prev')){
      console.log('');
    } else{
      console.log('');
    }
  });
});

スクリーンショット 2021-01-17 4.35.18.png

こんな感じで、ボタンを押した分consoleログに表示されます。
ここで注意したいのが、右と左がちゃんとボタンの向きとマッチしてるか確認します。左右を逆転して書いてしまうことはありがちです。ここで不穏因子は徹底的に排除しましょう。

ここから本番です。先程検証ツールを使って手動でスライド実装した時のコードが残ってます、まずこれを消しましょう。

css
.carousel > ul{
    width: calc(100% * 3);
    display: flex;

    /* margin-left: -100%;
    transition: .3s; */
}

これが出来たら、消した2行と同じ意味を持つ、JSのコードを書いていきましょう。

main.js
$(function(){
  $('.carousel > span').on('click', function(){
    if($(this).hasClass('prev')){
      console.log('');
      $('.carousel > ul').animate({'margin-left':'-100%'}, 1000);
    } else{
      console.log('');
    }
  });
});

e1533bd37a21ddcabd044585ccd20c91.gif

この様に動けば良いかと。
ただし現状問題があります。これは左ボタンを押したら、左に要素1つ分スライドすると言うコードなのですが、逆に言うと元いた位置から、1つ分以上動けないと言うことになります。

実際にやってみると、何度クリックしても最初の1回以上は動かないはずです。
ではどうするか、一旦の打開策としてmargin-left: 0;を続けてかけてみましょう。

main.js
$(function(){
  $('.carousel > span').on('click', function(){
    if($(this).hasClass('prev')){
      console.log('');
      $('.carousel > ul').animate({'margin-left':'-100%'}, 1000, function(){
        $('.carousel > ul').css('margin-left', '0');
      });
    } else{
      console.log('');
    }
  });
});

ここでポイントがあります。animate関数に続けて、処理を書く場合は、上記の様に関数をネスト化する必要があります。
かなり複雑な書き方ですが、正直慣れなので今は我慢しましょう。

では動きを確認します。

709b316078a35ca6d525e6f7da829919.gif

無限にクリックできる様になったのですが、毎回初期の位置に戻ってしまいます。
次はこれを解決する方法を考えましょう。
ちょっと難しいのですが、カルーセル自体がループするものなので、移動した要素、末尾に配置すれば解決します。

main.js
$(function(){
  $('.carousel > span').on('click', function(){
    if($(this).hasClass('prev')){
      console.log('');
      $('.carousel > ul').animate({'margin-left':'-100%'}, 1000, function(){
        $('.carousel > ul').css('margin-left', '0');
        $('.carousel > ul').append($('.carousel > ul > li:first-child'));
      });
    } else{
      console.log('');
    }
  });
});

こんな感じです。順に解説します。
まずセレクタ末尾に追加するメソッドとして、.append()があります。これは既存の要素、今回であればliですが、既存要素があれば動作的には追加ではなくカット&ペーストになります。

つまり.animate()で左にズレたと同時に、先頭のliをカット、末尾にペーストしてます。

そして重要なのが、先頭がカットされ、末尾に追加されたと言うことは、元いた位置から押し出しで2つ分移動したと言うことになります。

そして、margin-left: 0;を追加した時の挙動を思い出して欲しいのですが、左にスライドしたと思ったら瞬間的に右に移動しましたよね?

これを合わせることで、2つ左に移動して、右に1つ移動した結果、左に1つスライドした、となります。

ここら辺はややこしいので、図解化したので合わせてみて下さい。

カルーセル1.jpg

カルーセル2.jpg

カルーセル3.jpg

では動作を確認します。

2c6ec8246a0e0251a6d648de42fd8f79.gif

こんな感じでループが組めたら左側は完成です。
右側は左とおんなじ様なロジックで出来てるので自身で解読してみて下さい。以下に完成形コード載せときます。
console.logは邪魔なので消しました。

main.js
$(function(){
  $('.carousel > span').on('click', function(){
    if($(this).hasClass('prev')){
      $('.carousel > ul').animate({'margin-left':'-100%'}, 1000, function(){
        $('.carousel > ul').css('margin-left', '0');
        $('.carousel > ul').append($('.carousel > ul > li:first-child'));
      });
    } else{
      $('.carousel > ul').prepend($('.carousel > ul > li:last-child'));
      $('.carousel > ul').css('margin-left', '-100%');
      $('.carousel > ul').animate({'margin-left':'0'}, 1000);
    }
  });
});

仕上げ

ではJSが完成したところで仕上げと行きます。
現状全てのli要素が剥き出しになっているので、センターラインのエリアのみ表示できる様にしましょう。

css
.carousel{
    width: 100px;
    margin: 0 auto;
    position: relative;
    overflow: hidden;
}

大元の親要素.carouseloverflow: hidden;をかけるだけです。.carousel > ulwidth: calc(100% * 3);をかけていて、親要素からはみ出る様に設計したので、はみ出た分を隠すCSSを適用させます。

これで目的通り、Carousel Sliderの完成です!

まとめ

長時間ありがとうございました。作成に8時間もかかってしまいました😅
ただ機能と言うよりは、開発までの工程を学んで欲しくて、ひたすらに書き続けました。8時間あっという間でした。

将来的にマネジメント・教育する立場になりたいと思っております。もし何かご意見、修正等あれば遠慮なくどうぞ!

2021/1/17追記

imgサイズどのサイズにも対応できる様に、cssを追記。

css
@charset "utf-8";
/* Reset */
html,body,h1,p,ul,li{
    padding: 0;
    margin: 0;
}
ul{
    list-style: none;
}
img{
    border: none;
    vertical-align: bottom;
    max-width: 100%;
} 
/* header
--------------------------------------- */
header{
    text-align: center;
}
/* carousel slider
--------------------------------------- */
.carousel{
    /* ここの幅、高さを変える事でどのサイズにも対応させることが可能。imgのサイズ指定は不要。 */
    width: 100%;
    height: 50vh;
    margin: 0 auto;
    position: relative;
    overflow: hidden;
}
.carousel > ul{
    width: calc(100% * 3);
    display: flex;
}
.carousel > ul > li{
    width: calc(100% / 3);
}
.carousel img{
    width: 100%;
    height: 100%;
    object-fit: cover;
}
/* prev next button */
.carousel > span{
    position: absolute;
    top: 50%;
    border-radius: 50%;
    background: orange;
    color: white;
    font-size: 30px;
    margin-top: -15px;
    line-height: 1.0;
}
.carousel > span.prev{
    left: 0;
}
.carousel > span.next{
    right: 0;
}

・冗長なセレクタを変数化。
・自動実行コードを追加。

main.js
$(function(){
  let carUl = $('.carousel > ul');
  $('.carousel > span').on('click', function(){
    if($(this).hasClass('prev')){
      carUl.animate({'margin-left':'-100%'}, 1000, function(){
        carUl.css('margin-left', '0');
        carUl.append($('.carousel > ul > li:first-child'));
      });
    } else{
      carUl.prepend($('.carousel > ul > li:last-child'));
      carUl.css('margin-left', '-100%');
      carUl.animate({'margin-left':'0'}, 1000);
    }
  });
  // 自動実行
  setInterval(function(){
		$('.carousel > span.next').click();
	},3000);
});
15
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?