第18回 JavaScript アニメーションによるサイドバー（translateX）

# はじめに
Webサイトによく出てくるサイドバーを作りたいと思います。

# 今回実施する内容
今回は、Webでよく出てくるサイドバーを`anitemate`と`translateX`を使用して作成したいと思います。
メニューアイコン（ハンバーガーアイコン）をクリックすると、画面左からサイドバーがスライドして表示され、再クリックするとサイドバーが左側へ消えていくものです。
`scaleX`を使用しようと思ったのですが、サイドバーは`translateX`の動作をしているようだなと思いました。
![sidebar2-1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405480/87c2f6ea-1ce4-00b1-b7e4-407f9ac98289.gif)

# ソースコード(Git Hub)
https://github.com/Build-Mount/JS_18_sidebar

# 環境
OS: Windows 11 JP (64bit)
バージョン 125.0.2535.51 (公式ビルド) (64 ビット)

# 参考

- [Element: animate() メソッド](https://developer.mozilla.org/ja/docs/Web/API/Element/animate)
- [translateX()](https://developer.mozilla.org/ja/docs/Web/CSS/transform-function/translateX)
- [第13回 JavaScript アニメーションによる拡大・縮小（animate版）](https://qiita.com/Mount/items/22c1fc1b9577c44e695c)
- [第17回 JavaScript 「三」、「✕」みたいなメニューアイコン作成（CSSです）](https://qiita.com/Mount/items/79241e77da356b884b23)

# 用語

# `translateX`の動作
まずは、画面内でサイドバーを動かす動作を試します。
以下の通り、メニューアイコン（ハンバーガーアイコン）をクリックすると、サイドバーが右、左、右などと交互に移動します。
サイドバーが移動中にクリックしても方向が変わります。
これを画面外から画面内へ移動するようにしてあげれば、サイドバーの出来上がりです。
![sidebar.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405480/2a8db6ba-de1c-4b06-b63e-3cccac216feb.gif)

ソースは以下の通り。
<details>
<summary>sideBar_translateX.html</summary>

```html:sideBar_translateX.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>sideBar</title>
    <link rel="stylesheet" href="sideBar_translateX.css" type="text/css">
    <script src="sideBar_translateX.js" defer></script>
</head>
<body>
    <div id="menuIcon">
        <span class="bar barTop"></span>
        <span class="bar barMiddle"></span>
        <span class="bar barBottom"></span>
    </div>
    <div id="sideBar">
        <div id="home">ホーム</div>
        <div id="mypage">マイページ</div>
        <div id="channel">チャンネル</div>
    </div>
</body>
</html>
```
</details>

<details>
<summary>sideBar_translateX.js</summary>

```javascript:sideBar_translateX.js
let offset = -200;
/** side barの表示・非表示設定 */
document.getElementById("menuIcon").addEventListener("click", () => {
    showSideBar();
});

function showSideBar() {
    offset = -offset;
    const animation = document.getElementById("sideBar").animate(
        [
            {   transform: `translateX(${offset}px)`    }
        ],
        {
            duration: 500,
            fill: "forwards"
        },
    );
}
```
</details>

<details>
<summary>sideBar_translateX.css</summary>

```css:sideBar_translateX.css
/* menu Icon用開始 */
#menuIcon {
  position: relative;
  top: 2px;
  left: 2px;
  width: 48px;
  height: 48px;
}

#menuIcon:hover {
  background-color: #999;
  border-radius: 50%;
}

.bar {
  position: absolute;
  left: 14px;
  width: 20px;
  height: 2px;
  background-color: #333;
}

.barTop {
  top: 14px;
}

.barMiddle {
  top: 23px;
}

.barBottom {
  top: 32px;
}
/* menu Icon用終了*/

/* side Bar用開始 */
#sideBar {
  position: absolute;
  bottom: 0px;
  left: 200px;
  width: 160px;
  height: calc(100vh - 70px);
  border: 2px solid gray;
}
```
</details>

## sideBar_translatex.htmlの説明
まずはhtmlです。

```html:html抜粋
<body>
    <div id="menuIcon">
        <span class="bar barTop"></span>
        <span class="bar barMiddle"></span>
        <span class="bar barBottom"></span>
    </div>
    <div id="sideBar">
        <div id="home">ホーム</div>
        <div id="mypage">マイページ</div>
        <div id="channel">チャンネル</div>
    </div>
</body>
```

`div`要素の`id="menuIcon"`は、メニューアイコンを作成する要素です。
メニューアイコンの作成は、以下で行っていますので、ここでは説明を割愛します。
[第17回 JavaScript 「三」、「✕」みたいなメニューアイコン作成（CSSです）](https://qiita.com/Mount/items/79241e77da356b884b23)

`div`要素のid="sideBar"`は、サイドバーを作成する要素です。
今回はサイドバーを作るのが目的なのでそれっぽい名前の`div`要素を使っていますが、中身は今回の本質ではないため、適当です。

## sideBar_translateX.jsの説明

```javascript:javascriptの抜粋
let offset = -200;
/** side barの表示・非表示設定 */
document.getElementById("menuIcon").addEventListener("click", () => {
    showSideBar();
});
```

`let offset = -200;`は、サイドバーの左右の移動量を設定しています。今回は200px移動予定なので200としています。
そのあとは、メニューアイコンをクリックしたら、`showSideBar()`を実行するように、`addEventListener`を追加しています。

```javascript:javascriptの抜粋
function showSideBar() {
    offset = -offset;
    const animation = document.getElementById("sideBar").animate(
        [
            {   transform: `translateX(${offset}px)`    }
        ],
        {
            duration: 500,
            fill: "forwards"
        },
    );
}
```

`showSideBar`は中身はシンプルです。
`offset = -offset;`は、`showSideBar`が実行されるたびにサイドバーの左右の移動量をプラス方向とマイナス方向を入れ替えています。
その後の`animate`を使用して、`translateX`でアニメーションさせています。
`animate`の使用方法は、[第13回 JavaScript アニメーションによる拡大・縮小（animate版）](https://qiita.com/Mount/items/22c1fc1b9577c44e695c)で紹介していますので、詳細は割愛します。
`translateX`を実施して気づいたのですが、設定する値で、プラス方向やマイナス方向に移動するのですが、移動の基準は初期位置です。なので図の通りに移動することになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405480/354b44f8-21fc-f424-79ff-23713933395c.png)

ついでに思ったのは、`duration: 500`に設定していますが、1回目も、2回目以降も500msで移動するため、1回目だけ移動距離が200pxで2回目以降は400px移動となるため、移動速度が少し違っているなと思いました。
500msだと気になるほどではないなと思いましたが。

## sideBar_translateX.cssの説明
`/* menu Icon用開始 */`のところは、メニューアイコン用なので、割愛します。

```css:cssの抜粋
/* side Bar用開始 */
#sideBar {
  position: absolute;
  bottom: 0px;
  left: 200px;
  width: 160px;
  height: calc(100vh - 70px);
  border: 2px solid gray;
}
```
中身はシンプルで、`position: absolute;`として、`bottm: 0px`で画面下からの配置にしました。
`left: 200px;`は画面の真ん中くらいにするようにしたためで、実運用時は`left: -200px`とかになると思います。
サイドバーの幅は、`width: 160px;`としました。
サイドバーの高さは、`height: calc(100vh -70px);`としました。
これは、メニューアイコンが`64px`で作成したため、その部分が被らないように少しマージンをいれて、100vhから70px短くした高さにしました。
`border`は、今回は移動がわかるように枠をつけただけです。

# サイドバーを作る
「`translateX`の動作」をベースに、サイドバーの初期位置を画面外に持ってこれば完成です。

<details>
<summary>sideBar_translateX2.html</summary>

```html:sideBar_translateX2.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>sideBar</title>
    <link rel="stylesheet" href="sideBar_translateX2.css" type="text/css">
    <script src="sideBar_translateX2.js" defer></script>
</head>
<body>
    <div id="menuIcon">
        <span class="bar barTop"></span>
        <span class="bar barMiddle"></span>
        <span class="bar barBottom"></span>
    </div>
    <div id="sideBar">
        <div id="home">ホーム</div>
        <div id="mypage">マイページ</div>
        <div id="channel">チャンネル</div>
    </div>
</body>
</html>
```
</details>

<details>
<summary>sideBar_translateX2.js</summary>

```javascript:sideBar_translateX2.js
let offset = -165;
/** side barの表示・非表示設定 */
document.getElementById("menuIcon").addEventListener("click", () => {
    showSideBar();
});

function showSideBar() {
    offset = -offset;
    const animation = document.getElementById("sideBar").animate(
        [
            {   transform: `translateX(${offset}px)`    }
        ],
        {
            duration: 100,
            fill: "forwards"
        },
    );
}
```
</details>

<details>
<summary>sideBar_translateX2.css</summary>

```css:sideBar_translateX2.css
/* menu Icon用開始 */
#menuIcon {
  position: relative;
  top: 2px;
  left: 2px;
  width: 48px;
  height: 48px;
}

#menuIcon:hover {
  background-color: #999;
  border-radius: 50%;
}

.bar {
  position: absolute;
  left: 14px;
  width: 20px;
  height: 2px;
  background-color: #333;
}

.barTop {
  top: 14px;
}

.barMiddle {
  top: 23px;
}

.barBottom {
  top: 32px;
}
/* menu Icon用終了*/

/* side Bar用開始 */
#sideBar {
  position: absolute;
  bottom: 0px;
  left: -165px;
  width: 160px;
  height: calc(100vh - 70px);
  border: 2px solid gray;
}
```
</details>
JavaScriptの変数`offset`の値やCSS上の`left`の値だけ少し調整をしました。
Edgeでは問題なさそうでしたが、他のブラウザでどうなるかわかりません。その場合もう少しマージンを入れてもいいのかなとは思いました。
ちなみに、`width`と同じ160を`offset`や`left`に設定すると、少しサイドバーの枠が画面内に表示されてしまったため、多少のマージンは必要そうです。

# おわりに
今回はサイドバーを作ってみました。
正直、思っていたよりも簡単に実現できて驚きました。
少しずつJavaScriptも慣れてきてソースコードも自然に書けるようになってきました。