[test22]カルーセル

# 概要
## カルーセルとは
複数の表示領域を横に並べて、マウス操作やタッチ操作で左右にスライドし中央に表示する項目を次々に入れ替えるしくみ。由来は回転木馬。

## 1.基本 CSS編
### 1-1.リスト
リスト形式にするのが基本。
```HTML
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
```
<p class="codepen" data-height="300" data-default-tab="result" data-slug-hash="poGQPZa" data-user="yambejp" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/yambejp/pen/poGQPZa">
  test22-1-1</a> by yambe jp (<a href="https://codepen.io/yambejp">@yambejp</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

### 1-2.サイズと色を設定
ulにcontainerクラスを付加して、liにサイズ設定。
```HTML
<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>
```

<p class="codepen" data-height="300" data-default-tab="result" data-slug-hash="BaMGRvR" data-user="yambejp" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/yambejp/pen/BaMGRvR">
  test22-1-2</a> by yambe jp (<a href="https://codepen.io/yambejp">@yambejp</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>


### 1-3.横並び
liの内容をセンタリングしてリストを横並びにする
```HTML
<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>
```
<p class="codepen" data-height="300" data-default-tab="result" data-slug-hash="ExrOmGM" data-user="yambejp" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/yambejp/pen/ExrOmGM">
  Untitled</a> by yambe jp (<a href="https://codepen.io/yambejp">@yambejp</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

## 2.基本 JS編

### 2-1.入れ替えボタンを用意し入れ替え
leftボタンを押すと先頭のliが末尾に、rightボタンを押すと末尾のボタンを先頭に移動
```html
<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">
```
<p class="codepen" data-height="300" data-default-tab="result" data-slug-hash="MWLzmLB" data-user="yambejp" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/yambejp/pen/MWLzmLB">
  test22-2-1</a> by yambe jp (<a href="https://codepen.io/yambejp">@yambejp</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

### 2-2.表示領域を制限
```html
<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">
```
<p class="codepen" data-height="300" data-default-tab="result" data-slug-hash="gOqQWEL" data-user="yambejp" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/yambejp/pen/gOqQWEL">
  test22-2-2</a> by yambe jp (<a href="https://codepen.io/yambejp">@yambejp</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

## 3.スライド

### 3-1.左にスライド

leftボタンをおしたときにcontainerにmoveleftクラスをつける。moveleftクラスには下位liを左に100スライドするcssを設定。
移動が終了したら、先頭のliを末尾の移動しmoveleftクラスを外す。

```html
<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">
```

<p class="codepen" data-height="300" data-default-tab="result" data-slug-hash="mdvQKbY" data-user="yambejp" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/yambejp/pen/mdvQKbY">
  test22-3-1</a> by yambe jp (<a href="https://codepen.io/yambejp">@yambejp</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

### 3-2.右にスライド(失敗)
```HTML
<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">
```

<p class="codepen" data-height="300" data-default-tab="result" data-slug-hash="PoVxaVb" data-user="yambejp" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/yambejp/pen/PoVxaVb">
  test22-3-2</a> by yambe jp (<a href="https://codepen.io/yambejp">@yambejp</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

### 3-3.右にスライド(成功)
3-2で失敗した、liの移動を先にしてからクラスを付加する
移動によって右にずれるためスライドの起点を左にずらすためのrightmove-originを付加
```HTML
<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">
```
<p class="codepen" data-height="300" data-default-tab="result" data-slug-hash="ZEwmRwj" data-user="yambejp" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/yambejp/pen/ZEwmRwj">
  test22-3-3</a> by yambe jp (<a href="https://codepen.io/yambejp">@yambejp</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

### 3-4.左右にスライド(実装)
まとめ：
- 左スライド：スライドしてから先頭を末尾に
- 右スライド：末尾を先頭にしてからスライド

```HTML
<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">
```

<p class="codepen" data-height="300" data-default-tab="result" data-slug-hash="gOqQKVg" data-user="yambejp" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/yambejp/pen/gOqQKVg">
  test22-3-4</a> by yambe jp (<a href="https://codepen.io/yambejp">@yambejp</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

### 3-5.バグ

- moveleftとmoverightが両方つけることができるため挙動がおかしい
- rightボタンを押した場合、moverightクラスが付く前に末尾のliが先頭にくるため、rightボタン2度押しをすると挙動がおかしい

- 解決策：containerにmoveleftやmooverightクラスがついている場合はクリックが弾かれればよい。

```javascript
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);
  }
});
```
↓↓↓
```javascript
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.完成
```HTML
<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">
```

<p class="codepen" data-height="300" data-default-tab="result" data-slug-hash="poGqbLQ" data-user="yambejp" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/yambejp/pen/poGqbLQ">
  Untitled</a> by yambe jp (<a href="https://codepen.io/yambejp">@yambejp</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>