JavaScriptを用いた横スクロールの作り方

# はじめに
はじめまして～
学生IT団体watnow所属しています、いとうです。
開発についてはまだまだ知識足らずですがどうぞよろしくお願いします！

この記事はwatnow アドベントカレンダー2024 20日目です
（訳あって当日投稿になりました…本当に申し訳ありません...）

今回はJavaScriptを用いた横スクロールアクションの実装方法について話していこうと思います。

初投稿、初コーディング時の内容となっている為、稚拙な文ではありますがどうぞお付き合い願います。


## やりたかったこと
主に左から右に画面が動く通常の横スクロールと大差ありません。
ただ、操作する際は画面を横スワイプするのではなく縦スクロールと同じ**縦スワイプで横に移動**する仕様を目指しました。

なぜわざわざその仕様にしたかというと、
今回制作したサイトでは最初縦スクロールから始まるようにしているので、操作の切り替えで迷って横スクロールに移った瞬間画面が動かない！...となることを無くしたかったから。
あとは純粋な「やってみたかった」好奇心が6割です。


# 実装してみる
## 基本構造
### 1.HTML
写真を3枚横並びで出すコードを以下のように書きました(一部省略)
```html:index.html
<section class="picture" id="scroll">

            <div class="contents">
                <div class="horizontal_scroll">

                    <div class="picture">
                        <img src="./image/1.png" alt="1">
                        <img src="./image/2.jpg" alt="2">
                        <img src="./image/3.jpg" alt="3">
                    </div>

                </div>
            </div>

</section>
...
<script src="scroll.js"></script>
```
大雑把な構造としては
```
<section> ← idでJSと紐づけ
　  <div> ← 横スクロール画面
　　  　<div> ← 中身
```
で最低3つタグを用意をしていた方が見返した時に分かりやすいし、この後のCSSが書きやすくなるのでおすすめです。ページ構成を見ながら分けていきましょう！


### 2.CSS
cssでは、画面サイズの大きさがスクロールにとって重要な要素になります。
画面外までスクロールするため最初は感覚がつかみにくいですが、1回慣れると簡単に書くことができます！
上記（HTML記載）部分のみのコード↓
```css: style.css
.picture {

    height: 320vh;

    .contents{
        overflow: hidden;
        position: sticky;  /* 固定 */
        top: 0;
        height: 100vh;
    }

    .horizontal_scroll {
        position: absolute;
        top: 0;
        height: 100%;
        width: 320vw;
        display: flex;
    }
    
    .pic {
        margin: auto 0px;
    }

    img {
        width: 400px;
        height: 500px;
        margin: -7px;
        border-radius: 50%;
    }
    
}
```
:::note warn
注意
ここではsectionクラスの`height`は横スクロールの分も合わせて大きく取る必要があります
:::

#### 仕組み
簡易的な図で表すと以下の通りです。
黒＝外側、白＝内側
<img width="200" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3965876/c6bc3030-f984-7d8b-43f5-a7a9d3aea091.jpeg">   <img width="178" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3965876/af80e0e8-1376-0154-5d30-bacd920c8c45.jpeg">


固定された外側の`div`で画面の`height`を設定し、内側の`div`(横スクロール部分)で`width`をsectionクラスの`height`に合わせて非常に広い幅を持たせます。
外の箱が縦移動するのに合わせて内の箱を横移動させていくと画面が水平移動しているように見せることができます。

よって横スクロールでもスクロール量は `window.scrollY` が変化します。



これで最低限のCSSは用意できました。
次にJavaScriptを使って実際に横スクロールを行っていきます！

### 3.JavaScript
上記（HTML記載）部分のみのコード↓
```javascript: scroll.js

const stickySections = [...document.querySelectorAll('.contents')]


window.addEventListener('scroll', (e) => {
  for (let i = 0; i < stickySections.length; i++) {
    transform(stickySections[i])
  }
})

function transform(section) {

  const offsetTop = section.parentElement.offsetTop;

  const scrollSection = section.querySelector('.horizontal_scroll')

  let percentage = ((window.scrollY - offsetTop) / window.innerHeight) * 100;

  percentage = percentage < 0 ? 0 : percentage > 300 ? 300 : percentage;

  scrollSection.style.transform = `translate3d(${-(percentage)}vw, 0, 0)`
}
```
コメントを忘れていたので、上から順に大まかな説明をしていきます！

1\. 　`contents` クラスを持つすべての要素を取得し、`stickySections`配列に格納

2\.　スクロールイベントが発生すると、`stickySections`配列内の各要素に対して`transform`関数を呼び出す

>`transform`関数は、特定のセクション要素のスクロール位置に基づいて、そのスタイルを変更するための関数です


3\.　`offsetTop`
section 要素の親要素のオフセットトップ（ページの上端からの距離）を取得

4\.　`scrollSection`
section 内の `.horizontal_scroll`クラスを持つ要素を取得

5\.　`percentage`
現在のスクロール位置 `(window.scrollY)` と `offsetTop` の差を計算し、それをウィンドウの高さ `(window.innerHeight)` で割ってパーセンテージを計算し任意の範囲に制限

**6\.　`scrollSection.style.transform = translate3d(${-(percentage)}vw, 0, 0);`**

・`scrollSection` の 要素を***水平方向***に移動
・`translate3d` を使用して、要素を `-(percentage)` vw（ビューポート幅）だけ移動

この関数によって、スクロールイベントに応じて特定の要素を水平に移動させることができます。


基本的な横スクロールの説明は以上です！
JavaScriptとCSSの組み合わせで横スクロールアクションを作ることができました。



# おわりに
ここまでお付き合いくださり、ありがとうございました。
今回JavaScriptを初めて触りましたが、動的表示のWebページを簡単に構築できるので思うがまま創意工夫を凝らすことができてとても面白かったです！

今回は横スクロールのみの紹介となりましたが、実は今回制作したサイトには他のイベントリスナーも多く組み合わせているので次回はそちらも紹介できたらいいなと思います！