概要
カルーセルとは
複数の表示領域を横に並べて、マウス操作やタッチ操作で左右にスライドし中央に表示する項目を次々に入れ替えるしくみ。由来は回転木馬。
1.基本 CSS編
1-1.リスト
リスト形式にするのが基本。
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
See the Pen test22-1-1 by yambe jp (@yambejp) on CodePen.
1-2.サイズと色を設定
ulにcontainerクラスを付加して、liにサイズ設定。
<style>
.r{background-Color:red;}
.b{background-Color:blue;}
.g{background-Color:green;}
.container li{
width:100px;
height:100px;
}
</style>
<ul class="container">
<li class="r">1</li>
<li class="b">2</li>
<li class="g">3</li>
</ul>
See the Pen test22-1-2 by yambe jp (@yambejp) on CodePen.
1-3.横並び
liの内容をセンタリングしてリストを横並びにする
<style>
.r{background-Color:red;}
.b{background-Color:blue;}
.g{background-Color:green;}
.container li{
width:100px;
height:100px;
color:white;
/**
*
* flexにしてjustify-contentとalign-itemsでセンタリングができる
*
*/
display: flex;
justify-content: center;
align-items: center;
}
.container{
list-style:none;
padding:0;
display:flex;
}
</style>
<ul class="container">
<li class="r">1</li>
<li class="b">2</li>
<li class="g">3</li>
</ul>
See the Pen Untitled by yambe jp (@yambejp) on CodePen.
2.基本 JS編
2-1.入れ替えボタンを用意し入れ替え
leftボタンを押すと先頭のliが末尾に、rightボタンを押すと末尾のボタンを先頭に移動
<script>
document.addEventListener('click',({target})=>{
const c=document.querySelector('.container');
const f=c.querySelector('li:first-child');
const l=c.querySelector('li:last-child');
if(target.matches('.left')){
l.after(f);
}
if(target.matches('.right')){
f.before(l);
}
});
</script>
<ul class="container">
<li class="r">1</li>
<li class="b">2</li>
<li class="g">3</li>
</ul>
<input type="button" value="left" class="left">
<input type="button" value="left" class="right">
See the Pen test22-2-1 by yambe jp (@yambejp) on CodePen.
2-2.表示領域を制限
<style>
.r{background-Color:red;}
.b{background-Color:blue;}
.g{background-Color:green;}
.container li{
width:100px;
height:100px;
color:white;
display: flex;
justify-content: center;
align-items: center;
/**
*
* flex要素を縮小させない
*
*/
flex-shrink :0;
}
.container{
list-style:none;
padding:0;
display:flex;
/**
*
* 表示領域を制限
*
*/
width:100px;
height:100px;
overflow:hidden;
}
</style>
<script>
document.addEventListener('click',({target})=>{
const c=document.querySelector('.container');
const f=c.querySelector('li:first-child');
const l=c.querySelector('li:last-child');
if(target.matches('.left')){
l.after(f);
}
if(target.matches('.right')){
f.before(l);
}
});
</script>
<ul class="container">
<li class="r">1</li>
<li class="b">2</li>
<li class="g">3</li>
</ul>
<input type="button" value="left" class="left">
<input type="button" value="right" class="right">
See the Pen test22-2-2 by yambe jp (@yambejp) on CodePen.
3.スライド
3-1.左にスライド
leftボタンをおしたときにcontainerにmoveleftクラスをつける。moveleftクラスには下位liを左に100スライドするcssを設定。
移動が終了したら、先頭のliを末尾の移動しmoveleftクラスを外す。
<style>
.r{background-Color:red;}
.b{background-Color:blue;}
.g{background-Color:green;}
.container li{
width:100px;
height:100px;
color:white;
display: flex;
justify-content: center;
align-items: center;
flex-shrink :0;
}
.container{
list-style:none;
padding:0;
display:flex;
width:100px;
height:100px;
overflow:hidden;
}
/**
*
* moveleftクラスで100左にスライド
*
*/
.moveleft li{
transition: translate 1s;
translate:-100px;
}
</style>
<script>
document.addEventListener('click',({target})=>{
const c=document.querySelector('.container');
/**
*
* leftボタンを押したときにcontainerにmoveleftクラスを追加
*
*/
if(target.matches('.left')){
c.classList.add('moveleft');
}
});
document.addEventListener('transitionend',({target})=>{
const c=document.querySelector('.container');
const f=c.querySelector('li:first-child');
const l=c.querySelector('li:last-child');
/**
*
* 移動が終わったらcontainerのmoveleftクラスを削除
*
*/
if(c.matches('.moveleft')){
l.after(f);
c.classList.remove('moveleft');
}
})
</script>
<ul class="container">
<li class="r">1</li>
<li class="b">2</li>
<li class="g">3</li>
</ul>
<input type="button" value="left" class="left">
<input type="button" value="right" class="right">
See the Pen test22-3-1 by yambe jp (@yambejp) on CodePen.
3-2.右にスライド(失敗)
<style>
.r{background-Color:red;}
.b{background-Color:blue;}
.g{background-Color:green;}
.container li{
width:100px;
height:100px;
color:white;
display: flex;
justify-content: center;
align-items: center;
flex-shrink :0;
}
.container{
list-style:none;
padding:0;
display:flex;
width:100px;
height:100px;
overflow:hidden;
}
/**
*
* moverightクラスがつくと右にスライド
* x位置の起点が指定できない
*
*/
.moveright li{
transition: translate 1s;
translate:0px;
}
</style>
<script>
document.addEventListener('click',({target})=>{
const c=document.querySelector('.container');
const f=c.querySelector('li:first-child');
const l=c.querySelector('li:last-child');
/**
*
* rightボタンを押したら末尾のliを先頭に移動し
* moverightクラスを付加して右にスライド
* 失敗:移動を待たずにスライドが実行される
*
*/
if(target.matches('.right')){
f.before(l);
c.classList.add('moveright');
}
});
document.addEventListener('transitionend',({target})=>{
const c=document.querySelector('.container');
/**
*
* スライドが終わったらmoverightクラスを外す
*
*/
if(c.matches('.moveright')){
c.classList.remove('moveright');
}
})
</script>
<ul class="container">
<li class="r">1</li>
<li class="b">2</li>
<li class="g">3</li>
</ul>
<input type="button" value="right" class="right">
See the Pen test22-3-2 by yambe jp (@yambejp) on CodePen.
3-3.右にスライド(成功)
3-2で失敗した、liの移動を先にしてからクラスを付加する
移動によって右にずれるためスライドの起点を左にずらすためのrightmove-originを付加
<style>
.r{background-Color:red;}
.b{background-Color:blue;}
.g{background-Color:green;}
.container li{
width:100px;
height:100px;
color:white;
display: flex;
justify-content: center;
align-items: center;
flex-shrink :0;
}
.container{
list-style:none;
padding:0;
display:flex;
width:100px;
height:100px;
overflow:hidden;
}
.moveright-origin li{
translate:-100px;
}
.moveright li{
transition: translate 1s;
translate:0px;
}
</style>
<script>
document.addEventListener('click',({target})=>{
const c=document.querySelector('.container');
const f=c.querySelector('li:first-child');
const l=c.querySelector('li:last-child');
/**
* liの移動してmoveright-originクラスで起点を決める
* setTimeoutでタイミングをずらしてmoverightクラスで右にスライド加
* この辺処理が汚いので要調整
*
*/
if(target.matches('.right')){
f.before(l);
c.classList.add('moveright-origin');
setTimeout(()=>c.classList.add('moveright'),0);
}
});
document.addEventListener('transitionend',({target})=>{
const c=document.querySelector('.container');
/**
*
* スライドが終わったらmoveright/moveright-originクラスを外す
*
*/
if(c.matches('.moveright')){
c.classList.remove('moveright');
c.classList.remove('moveright-origin');
}
})
</script>
<ul class="container">
<li class="r">1</li>
<li class="b">2</li>
<li class="g">3</li>
</ul>
<input type="button" value="right" class="right">
See the Pen test22-3-3 by yambe jp (@yambejp) on CodePen.
3-4.左右にスライド(実装)
まとめ:
- 左スライド:スライドしてから先頭を末尾に
- 右スライド:末尾を先頭にしてからスライド
<style>
.r{background-Color:red;}
.b{background-Color:blue;}
.g{background-Color:green;}
.container li{
width:100px;
height:100px;
color:white;
display: flex;
justify-content: center;
align-items: center;
flex-shrink :0;
}
.container{
list-style:none;
padding:0;
display:flex;
width:100px;
height:100px;
overflow:hidden;
}
.moveleft li{
transition: translate 1s;
translate:-100px;
}
.moveright-origin li{
translate:-100px;
}
.moveright li{
transition: translate 1s;
translate:0px;
}
</style>
<script>
document.addEventListener('click',({target})=>{
const c=document.querySelector('.container');
const f=c.querySelector('li:first-child');
const l=c.querySelector('li:last-child');
if(target.matches('.left')){
c.classList.add('moveleft');
}
if(target.matches('.right') &&
!c.match('moveright-origin')){
f.before(l);
c.classList.add('moveright-origin');
setTimeout(()=>c.classList.add('moveright'),0);
}
});
document.addEventListener('transitionend',({target})=>{
const c=document.querySelector('.container');
const f=c.querySelector('li:first-child');
const l=c.querySelector('li:last-child');
if(c.matches('.moveleft')){
l.after(f);
c.classList.remove('moveleft');
}
if(c.matches('.moveright')){
c.classList.remove('moveright');
c.classList.remove('moveright-origin');
}
})
</script>
<ul class="container">
<li class="r">1</li>
<li class="b">2</li>
<li class="g">3</li>
</ul>
<input type="button" value="left" class="left">
<input type="button" value="right" class="right">
See the Pen test22-3-4 by yambe jp (@yambejp) on CodePen.
3-5.バグ
-
moveleftとmoverightが両方つけることができるため挙動がおかしい
-
rightボタンを押した場合、moverightクラスが付く前に末尾のliが先頭にくるため、rightボタン2度押しをすると挙動がおかしい
-
解決策:containerにmoveleftやmooverightクラスがついている場合はクリックが弾かれればよい。
document.addEventListener('click',({target})=>{
const c=document.querySelector('.container');
const f=c.querySelector('li:first-child');
const l=c.querySelector('li:last-child');
if(target.matches('.left')){
c.classList.add('moveleft');
}
if(target.matches('.right')){
f.before(l);
c.classList.add('moveright-origin');
setTimeout(()=>c.classList.add('moveright'),0);
}
});
↓↓↓
document.addEventListener('click',({target})=>{
const c=document.querySelector('.container');
const f=c.querySelector('li:first-child');
const l=c.querySelector('li:last-child');
if(c.matches(':not(.moveleft):not(.moveright)')){
if(target.matches('.left')){
c.classList.add('moveleft');
}
if(target.matches('.right')){
f.before(l);
c.classList.add('moveright-origin');
setTimeout(()=>c.classList.add('moveright'),0);
}
}
});
3-6.完成
<style>
.r{background-Color:red;}
.b{background-Color:blue;}
.g{background-Color:green;}
.container li{
width:100px;
height:100px;
color:white;
display: flex;
justify-content: center;
align-items: center;
flex-shrink :0;
}
.container{
list-style:none;
padding:0;
display:flex;
width:100px;
height:100px;
overflow:hidden;
}
.moveleft li{
transition: translate 1s;
translate:-100px;
}
.moveright-origin li{
translate:-100px;
}
.moveright li{
transition: translate 1s;
translate:0px;
}
</style>
<script>
document.addEventListener('click',({target})=>{
const c=document.querySelector('.container');
const f=c.querySelector('li:first-child');
const l=c.querySelector('li:last-child');
if(c.matches(':not(.moveleft):not(.moveright)')){
if(target.matches('.left')){
c.classList.add('moveleft');
}
if(target.matches('.right')){
f.before(l);
c.classList.add('moveright-origin');
setTimeout(()=>c.classList.add('moveright'),0);
}
}
});
document.addEventListener('transitionend',({target})=>{
const c=document.querySelector('.container');
const f=c.querySelector('li:first-child');
const l=c.querySelector('li:last-child');
if(c.matches('.moveleft')){
l.after(f);
c.classList.remove('moveleft');
}
if(c.matches('.moveright')){
c.classList.remove('moveright');
c.classList.remove('moveright-origin');
}
})
</script>
<ul class="container">
<li class="r">1</li>
<li class="b">2</li>
<li class="g">3</li>
</ul>
<input type="button" value="left" class="left">
<input type="button" value="right" class="right">