カルーセルスライダーを自作しました。また初学者用に図解化してみたので是非参考にして下さい。
完成イメージ
箱が小さくて申し訳ないのですが、こんな感じです。
ごく一般なカルーセルスライダーです。
ちなみにカルーセルとスライダーの違いですが、カルーセルは別名メリーゴーランドというのですが、簡単に言ってしまうと同じところをグルグル回ってるという意味です。
対してスライダーは上下左右にスライドしていれば、それはスライダーです。
正直ループするかしないかの違いで機能自体に大きな違いはなく、意味的なくくりもないみたいです。
下準備
準備として、以下のことをします。
※コードを書かない場合は読み飛ばして下さい。
【準備するもの】
・画像(以下にリンク貼ります)
・html/css/jsファイル(インラインで書いても良いけど分けた方がわかりやすいよ!)
コーディング
マークアップ
<!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>
・GoogleFont
とFontAwesome
はCDN書いていますが、今回は使いません。
・JQuery
は絶対使うので最新版を入れましょう。
・body
内の説明ですが、headerのp
は中心が、わかりやすい様に↓
をつけています。あとあと説明に使うのでとりあえず着けときましょう。
・div.carousel
ですが、大きく2つのブロックに分かれいます。まずul
には画像を3枚並べる様にli
を3つ並べています。あとはボタンとしてspan
を2つ書いています。
画面はこんな感じになれば良いと思います。
レイアウト変更
次にCSSを書いていきます。ここは何回かに分けて説明します。
まずReset
を書き、header
は中央寄せだけ書きます。
@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
部分を整えます。
まずやるべきは中央寄せです。今回はブロック要素を中央寄せなのでmargin
を使えば良いですね。
.carousel{
width: 100px;
margin: 0 auto;
}
こんな感じです。
ここで冒頭でも触れた、headerのp
のセンターライン(↓)の出番です。
画像3枚がちゃんとこの、ラインのど真ん中に来てることを確認しましょう。
次に、横並べをします。
横に並べたい要素はimg
ではなくli
なのでul
にflex
をかけます。
※ul
がflexコンテナ、li
がflexアイテムです。
.carousel > ul{
display: flex;
}
とりあえずflexかけました。画面を確認します。
画像ちっさ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
を使います。
.carousel > ul{
width: calc(100% * 3);
display: flex;
}
画面はこんな感じです。
画像サイズも元通りで、横並べになりました。
ただ注目すべき点は、センターラインの位置ですね。一番最初の要素が中心にいて、そこから右に後続の要素が並んでいます。
なんか変な気がしますが、カルーセルなので問題ありません。最終的にはこのセンターラインにいる要素しか表示しないので後続がどこにいようが関係ないです。
では続きを書いていきます。
次にやるべきは、ボタンを作成し、適正な位置に配置します。
まずボタンの見た目だけやります。
.carousel > span{
border-radius: 50%;
background: orange;
color: white;
font-size: 30px;
}
こんな感じです。難しいことはありませんね。
次に配置です。配置といえばposition
ですが、今回は特定の要素を基準にして、ボタンを配置したいです。
今回の場合、特定の要素と言うのは、このカルーセル大元である.carousel
です。
こう言った、特定の要素に対して配置をしたい場合、親要素にrelative
、子要素にabsolute
を使います。
では親要素は.carousel
、子要素はspan
とうことになるのでコードを追加していきます。
.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 > span
にposition: absolute;
をかけています。
ただposition: absolute;
をかけるだけだと、効力が発揮されないので、プラスアルファーとしてtop: 50%;
をつけています。これは親要素の50%の位置に配置することを意味しています。
そして、prevボタンは左に、nextボタンは右に配置したいので、それぞれ個別にleft: 0;
,right: 0;
をかけています。
ここまで出来たら画面を確認します。
いろいろ問題がありますね・・・
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
の配置方法が、要素の中心ではなく、左上だからです。なので、要素の半分のサイズ分、上にズラす必要があります。
.carousel > span{
position: absolute;
top: 50%;
border-radius: 50%;
background: orange;
color: white;
font-size: 30px;
margin-top: -15px;
line-height: 1.0;
}
これで、良い位置に配置されたかと思います。
これでレイアウト完成です!
次はJSを書いていくのですが、JSを書く上で重要なのは、テストや検証をどれだけやるかです。
なので先にテストと検証をやっていきます。
JQuery
なにをテストしていくかと言うと、今回のメイン機能である、スライドです。
まず手作業で実装したい機能を実現させます。
以下のコード2行を追加します。
.carousel > ul{
width: calc(100% * 3);
display: flex;
margin-left: -100%;
transition: .3s;
}
画面はこうです。今回は検証を使うので画面全体を撮ってます。
では検証の部分を見ていきます。以下の通りにしましょう。
Elementsの中からul
を選び、Stylesの中から指定のポイントをクリックします。
以下の動画を見ながらやっってみて下さい。
margin-left: -100%;
についてる、チェックボックスをつけたり消したりすることで、擬似的にスライダーを実装することが出来ます。
これは検証ツール上でCSSのオンオフが出来ることを利用した手法です。
この様に、目的を明確にする為に検証ツールは使うべきです。
さて、話を戻しますが、ここまでやって、margin-left: -100%;
をつけたり消したりすることで、スライダー実装することが出来ることがわかりました。なのでこれをJSに落とし込みます。
では機能の概要はなんとなくわかりました。「●●をしたらxxをする」と言うのがJSイベントの基本なのですが、●●とxxなにが当てはまるでしょうか?
スライダーなので「ボタンを押したらスライドする」がただしそうですが、いまいち具体性にかけます。とはいえ考えても進まないので、今言った「ボタンを押したらスライドする」をコードに落とし込みます。
$(function(){
$('.carousel > span').on('click', function(){
console.log($(this));
});
});
こんな感じです。これも鉄則ですが、必ず1手順づつconsole
で確認しましょう。
ここでconsole
することで、このクリックイベントは動くことを証明出来るので後々エラーになった場合に、エラー原因の候補から外れます。
ではボタンクリックでconsole
ログを確認します。
この様に、prevを押したらspan.prev
がnextを押したらspan.next
がconsole
ログに表示されましたか?
$(this)
と言うのは、自身の要素と言う意味があり、今回の場合イベントの発火元を$('.carousel > span')
と曖昧なセレクタしています。
$('.carousel > span')
はnext
とprev
の2つ存在することは理解できるかと思います。
ただしどちらがクリックされるかはわからないので、あえて$('.carousel > span')
とすることで、どちらがクリックされてもイベントが発火する様な設計になっています。
ただprev
とnext
で別々の動きをするので$(this)
を使用することで、クリックされた要素のみを取得できる様にしてます。
では次に、処理を分岐させましょう。
当たり前ですが、まずconsole
ログで確認します。
コードとしてはif文を使います。prev
が押されたら左
、next
が押されたら右
と表示します。
$(function(){
$('.carousel > span').on('click', function(){
if($(this).hasClass('prev')){
console.log('左');
} else{
console.log('右');
}
});
});
こんな感じで、ボタンを押した分console
ログに表示されます。
ここで注意したいのが、右と左がちゃんとボタンの向きとマッチしてるか確認します。左右を逆転して書いてしまうことはありがちです。ここで不穏因子は徹底的に排除しましょう。
ここから本番です。先程検証ツールを使って手動でスライド実装した時のコードが残ってます、まずこれを消しましょう。
.carousel > ul{
width: calc(100% * 3);
display: flex;
/* margin-left: -100%;
transition: .3s; */
}
これが出来たら、消した2行と同じ意味を持つ、JSのコードを書いていきましょう。
$(function(){
$('.carousel > span').on('click', function(){
if($(this).hasClass('prev')){
console.log('左');
$('.carousel > ul').animate({'margin-left':'-100%'}, 1000);
} else{
console.log('右');
}
});
});
この様に動けば良いかと。
ただし現状問題があります。これは左ボタンを押したら、左に要素1つ分スライドすると言うコードなのですが、逆に言うと元いた位置から、1つ分以上動けないと言うことになります。
実際にやってみると、何度クリックしても最初の1回以上は動かないはずです。
ではどうするか、一旦の打開策としてmargin-left: 0;
を続けてかけてみましょう。
$(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関数
に続けて、処理を書く場合は、上記の様に関数をネスト化する必要があります。
かなり複雑な書き方ですが、正直慣れなので今は我慢しましょう。
では動きを確認します。
無限にクリックできる様になったのですが、毎回初期の位置に戻ってしまいます。
次はこれを解決する方法を考えましょう。
ちょっと難しいのですが、カルーセル自体がループするものなので、移動した要素、末尾に配置すれば解決します。
$(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つスライドした、となります。
ここら辺はややこしいので、図解化したので合わせてみて下さい。
では動作を確認します。
こんな感じでループが組めたら左側は完成です。
右側は左とおんなじ様なロジックで出来てるので自身で解読してみて下さい。以下に完成形コード載せときます。
※console.log
は邪魔なので消しました。
$(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
要素が剥き出しになっているので、センターラインのエリアのみ表示できる様にしましょう。
.carousel{
width: 100px;
margin: 0 auto;
position: relative;
overflow: hidden;
}
大元の親要素.carousel
にoverflow: hidden;
をかけるだけです。.carousel > ul
にwidth: calc(100% * 3);
をかけていて、親要素からはみ出る様に設計したので、はみ出た分を隠すCSSを適用させます。
これで目的通り、Carousel Sliderの完成です!
まとめ
長時間ありがとうございました。作成に8時間もかかってしまいました😅
ただ機能と言うよりは、開発までの工程を学んで欲しくて、ひたすらに書き続けました。8時間あっという間でした。
将来的にマネジメント・教育する立場になりたいと思っております。もし何かご意見、修正等あれば遠慮なくどうぞ!
2021/1/17追記
imgサイズどのサイズにも対応できる様に、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;
}
・冗長なセレクタを変数化。
・自動実行コードを追加。
$(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);
});