HTMLとCSSだけでカルーセル・スライダーを実装するテクニックのまとめ
Post on:2020年1月15日
カルーセルやスライダーをHTMLとCSSだけで実装するテクニックを紹介します。
Flexboxでの横一列配置をはじめ、scroll-snap-typeやscroll-behaviorでスライドのスクロールの挙動を調整でき、自動再生機能もCSSのみで実装できます。
下記は各ポイントを意訳したものです。
※当ブログでの翻訳記事は、元サイト様にライセンスを得て翻訳しています。
カルーセルをCSSのみで実装するテクニックのまとめ
カルーセルやスライドを実装する時、HTMLとCSSだけでどれだけのものが実装できるか知るとあなたは驚くかもしれません。以前に紹介したHTMLとCSSで実装するスライダーを見てましょう。
実装のポイントは、下記の通りです。
- スライダーの各パネルは、Flexboxで横一列に設定します。
- パネルを1つだけ表示するにはオーバーフローを使用し、-webkit-overflow-scrollingでスワイプできるようにします(iOSのみ)。
- scroll-snap-typeを使用すると、スライドをうまく整列させることができます。
参考: scroll-snapプロパティの基礎知識と便利な使い方 - ナビゲーションのリンクは#jump-linksを使うだけで、スムーズなスクロールはscroll-behaviorで実装します。
参考: CSSでページをアニメーションでスクロールさせる「Scroll-behavior」
実際の動作は、下記のデモでご覧ください。
See the Pen
Real Simple Slider by Chris Coyier (@chriscoyier)
on CodePen.
コードは、下記の通りです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
<div class="slider"> <a href="#slide-1">1</a> <a href="#slide-2">2</a> <a href="#slide-3">3</a> <a href="#slide-4">4</a> <a href="#slide-5">5</a> <div class="slides"> <div id="slide-1"> 1 </div> <div id="slide-2"> 2 </div> <div id="slide-3"> 3 </div> <div id="slide-4"> 4 </div> <div id="slide-5"> 5 </div> </div> </div> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
* { box-sizing: border-box; } .slider { width: 300px; text-align: center; overflow: hidden; } .slides { display: flex; overflow-x: auto; scroll-snap-type: x mandatory; scroll-behavior: smooth; -webkit-overflow-scrolling: touch; /* scroll-snap-points-x: repeat(300px); scroll-snap-type: mandatory; */ } .slides::-webkit-scrollbar { width: 10px; height: 10px; } .slides::-webkit-scrollbar-thumb { background: black; border-radius: 10px; } .slides::-webkit-scrollbar-track { background: transparent; } .slides > div { scroll-snap-align: start; flex-shrink: 0; width: 300px; height: 300px; margin-right: 50px; border-radius: 10px; background: #eee; transform-origin: center center; transform: scale(1); transition: transform 0.5s; position: relative; display: flex; justify-content: center; align-items: center; font-size: 100px; } .slides > div:target { /* transform: scale(0.8); */ } .author-info { background: rgba(0, 0, 0, 0.75); color: white; padding: 0.75rem; text-align: center; position: absolute; bottom: 0; left: 0; width: 100%; margin: 0; } .author-info a { color: white; } img { object-fit: cover; position: absolute; top: 0; left: 0; width: 100%; height: 100%; } .slider > a { display: inline-flex; width: 1.5rem; height: 1.5rem; background: white; text-decoration: none; align-items: center; justify-content: center; border-radius: 50%; margin: 0 0 0.5rem 0; position: relative; } .slider > a:active { top: 1px; } .slider > a:focus { background: #000; } /* Don't need button navigation */ @supports (scroll-snap-type) { .slider > a { display: none; } } html, body { height: 100%; overflow: hidden; } body { display: flex; align-items: center; justify-content: center; background: linear-gradient(to bottom, #74ABE2, #5563DE); font-family: 'Ropa Sans', sans-serif; } |
Christian Schaeferのカルーセルは次へボタンと前へボタンに加えて、自動再生機能も備えています。もちろん、HTMLとCSSのみで実装されています。
See the Pen
A CSS-only Carousel Slider by Christian Schaefer (@Schepp)
on CodePen.
CSSで実装する自動再生機能に注目してみましょう。
- まず、スクロールのスナップポイントをゆっくりと右にオフセットします。スナップされているため、スクロールエリアが追従します。
- スライド全体の幅をスクロールした後、スナップを無効にします。これでスクロールエリアはスナップポイントから解放されます。
- 次に、スナップポイントを元の位置にジャンプさせ、スクロールエリアをスナップドラッグせずに戻します。
- 最後に、スナップを再度有効にして、スクロールエリアを別のスナップポイントにスナップできるようにします🤯
クールですね!
コードは、下記の通りです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
<section class="carousel" aria-label="Gallery"> <ol class="carousel__viewport"> <li id="carousel__slide1" tabindex="0" class="carousel__slide"> <div class="carousel__snapper"> <a href="#carousel__slide4" class="carousel__prev">Go to last slide</a> <a href="#carousel__slide2" class="carousel__next">Go to next slide</a> </div> </li> <li id="carousel__slide2" tabindex="0" class="carousel__slide"> <div class="carousel__snapper"></div> <a href="#carousel__slide1" class="carousel__prev">Go to previous slide</a> <a href="#carousel__slide3" class="carousel__next">Go to next slide</a> </li> <li id="carousel__slide3" tabindex="0" class="carousel__slide"> <div class="carousel__snapper"></div> <a href="#carousel__slide2" class="carousel__prev">Go to previous slide</a> <a href="#carousel__slide4" class="carousel__next">Go to next slide</a> </li> <li id="carousel__slide4" tabindex="0" class="carousel__slide"> <div class="carousel__snapper"></div> <a href="#carousel__slide3" class="carousel__prev">Go to previous slide</a> <a href="#carousel__slide1" class="carousel__next">Go to first slide</a> </li> </ol> <aside class="carousel__navigation"> <ol class="carousel__navigation-list"> <li class="carousel__navigation-item"> <a href="#carousel__slide1" class="carousel__navigation-button">Go to slide 1</a> </li> <li class="carousel__navigation-item"> <a href="#carousel__slide2" class="carousel__navigation-button">Go to slide 2</a> </li> <li class="carousel__navigation-item"> <a href="#carousel__slide3" class="carousel__navigation-button">Go to slide 3</a> </li> <li class="carousel__navigation-item"> <a href="#carousel__slide4" class="carousel__navigation-button">Go to slide 4</a> </li> </ol> </aside> </section> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 |
@keyframes tonext { 75% { left: 0; } 95% { left: 100%; } 98% { left: 100%; } 99% { left: 0; } } @keyframes tostart { 75% { left: 0; } 95% { left: -300%; } 98% { left: -300%; } 99% { left: 0; } } @keyframes snap { 96% { scroll-snap-align: center; } 97% { scroll-snap-align: none; } 99% { scroll-snap-align: none; } 100% { scroll-snap-align: center; } } body { max-width: 37.5rem; margin: 0 auto; padding: 0 1.25rem; font-family: 'Lato', sans-serif; } * { box-sizing: border-box; scrollbar-color: transparent transparent; /* thumb and track color */ scrollbar-width: 0px; } *::-webkit-scrollbar { width: 0; } *::-webkit-scrollbar-track { background: transparent; } *::-webkit-scrollbar-thumb { background: transparent; border: none; } * { -ms-overflow-style: none; } ol, li { list-style: none; margin: 0; padding: 0; } .carousel { position: relative; padding-top: 75%; filter: drop-shadow(0 0 10px #0003); perspective: 100px; } .carousel__viewport { position: absolute; top: 0; right: 0; bottom: 0; left: 0; display: flex; overflow-x: scroll; counter-reset: item; scroll-behavior: smooth; scroll-snap-type: x mandatory; } .carousel__slide { position: relative; flex: 0 0 100%; width: 100%; background-color: #f99; counter-increment: item; } .carousel__slide:nth-child(even) { background-color: #99f; } .carousel__slide:before { content: counter(item); position: absolute; top: 50%; left: 50%; transform: translate3d(-50%,-40%,70px); color: #fff; font-size: 2em; } .carousel__snapper { position: absolute; top: 0; left: 0; width: 100%; height: 100%; scroll-snap-align: center; } @media (hover: hover) { .carousel__snapper { animation-name: tonext, snap; animation-timing-function: ease; animation-duration: 4s; animation-iteration-count: infinite; } .carousel__slide:last-child .carousel__snapper { animation-name: tostart, snap; } } @media (prefers-reduced-motion: reduce) { .carousel__snapper { animation-name: none; } } .carousel:hover .carousel__snapper, .carousel:focus-within .carousel__snapper { animation-name: none; } .carousel__navigation { position: absolute; right: 0; bottom: 0; left: 0; text-align: center; } .carousel__navigation-list, .carousel__navigation-item { display: inline-block; } .carousel__navigation-button { display: inline-block; width: 1.5rem; height: 1.5rem; background-color: #333; background-clip: content-box; border: 0.25rem solid transparent; border-radius: 50%; font-size: 0; transition: transform 0.1s; } .carousel::before, .carousel::after, .carousel__prev, .carousel__next { position: absolute; top: 0; margin-top: 37.5%; width: 4rem; height: 4rem; transform: translateY(-50%); border-radius: 50%; font-size: 0; outline: 0; } .carousel::before, .carousel__prev { left: -1rem; } .carousel::after, .carousel__next { right: -1rem; } .carousel::before, .carousel::after { content: ''; z-index: 1; background-color: #333; background-size: 1.5rem 1.5rem; background-repeat: no-repeat; background-position: center center; color: #fff; font-size: 2.5rem; line-height: 4rem; text-align: center; pointer-events: none; } .carousel::before { background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 100 100' xmlns='http://www.w3.org/2000/svg'%3E%3Cpolygon points='0,50 80,100 80,0' fill='%23fff'/%3E%3C/svg%3E"); } .carousel::after { background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 100 100' xmlns='http://www.w3.org/2000/svg'%3E%3Cpolygon points='100,50 20,100 20,0' fill='%23fff'/%3E%3C/svg%3E"); } |
ボーナスとして最後に、JavaScriptを少しだけ加えたものを紹介します。カルーセルはHTMLとCSSのみで実装できますが、ほんの少しだけJavaScriptを加えると更に高性能なもの(Flickity)が実装できます。
See the Pen
Flickity - wrapAround by Dave DeSandro (@desandro)
on CodePen.
コードは、下記の通りです。
実装には外部ファイルが必要です。
1 2 3 4 5 6 7 8 |
<div class="gallery js-flickity" data-flickity-options='{ "wrapAround": true }'> <div class="gallery-cell"></div> <div class="gallery-cell"></div> <div class="gallery-cell"></div> <div class="gallery-cell"></div> <div class="gallery-cell"></div> </div> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
/* external css: flickity.css */ * { -webkit-box-sizing: border-box; box-sizing: border-box; } body { font-family: sans-serif; } .gallery { background: #EEE; } .gallery-cell { width: 66%; height: 200px; margin-right: 10px; background: #8C8; counter-increment: gallery-cell; } /* cell number */ .gallery-cell:before { display: block; text-align: center; content: counter(gallery-cell); line-height: 200px; font-size: 80px; color: white; } |
sponsors