icon

【Locomotive Scroll】と【GSAP】 を使用してパララックスサイトを制作してみた

Locomotive Scrollでパララックスサイトを実装
投稿日時

更新日時

Locomotive Scrollを使用して、簡単なパララックスサイトを実装してみました。GSAPのScorllTriggerと併用してスクロールアクションも取り入れてみました。

今回は、Locomotive Scrollを実装方法とコードを記載していこうと思います。まずはDEMOサイトをご覧ください。(レスポンシブ対応まで行なっていません。)

デモサイトはこちら

Locomotive Scrollの実装方法

読み込み

GitHubページよりzipファイルをダウンロードしてください。zipファイルを解凍し、locomotive-scroll.min.cssをhead内、locomotive-scroll.min.jsをbodyの前に読み込みます。

GitHubはこちらから

HTML

Locomotive Scrollをかけたい領域の親要素に「data-scroll-containerをいれます。

/**html**/  

<div class="site__main__contents" data-scroll-container></div>

JavaScript

Javascriptファイルに下記のコードを書き込んで呼び出します。

/**js**/

const locoScroll = new LocomotiveScroll({
  el: document.querySelector('[data-scroll-container]'),
  smooth: true,
});

GSAPのScrollTriggerと併用する場合は、以下のコードを追加します。

/**js**/

ScrollTrigger.refresh();
ScrollTrigger.scrollerProxy(".site__main__contents", {
  scrollTop(value) {
    return arguments.length
      ? locoScroll.scrollTo(value, 0, 0)
      : locoScroll.scroll.instance.scroll.y;
  },
  getBoundingClientRect() {
    return {
      top: 0,
      left: 0,
      width: window.innerWidth,
      height: window.innerHeight,
    };
  },

  pinType: document.querySelector(".site__main__contents").style.transform
    ? "transform"
    : "fixed",
});

各セクションに「data-scroll-section」を追加

各セクションの親要素に「data-scroll-section」を追加します。また、セクションの番号を上から指定します。

/**html**/

<section class="fv" data-scroll-section data-scroll-section-id="section0">
</section>

その他要素にLocomotive Scrollをかけたい場合は「data-scroll」を追加

その他の要素にかけたい場合は「data-scroll」を指定します。

/**html**/

<ul class="news__ul" data-scroll data-scroll-speed="1"></ul>

その他オプション

その他オプションを指定することで、要素がスクロールに合わせて動くスピードを変化させたり、要素を固定させたりすることができます。オプションの詳細はGitHubページより確認してください。

GitHubはこちらから

GSAPのScrollTriggerとの併用

GSAPのScrollTriggerと併用する場合の注意点があります。JavascriptファイルにScrollTriggerを書き込む際に「scroller: (data-scroll-containerを追加した要素のクラス名)」を指定してあげてください。

/**js**/

const jsFadeUpTitles = document.querySelectorAll('.fade-up-ttl');

jsFadeUpTitles.forEach((jsFadeUpTitle, index) => {
  gsap.to(
    jsFadeUpTitle,
  {
    scrollTrigger: {
      trigger: jsFadeUpTitle,
      start: 'top-=100 bottom',
      end: 'top top',
      once: true,
      //下記のように追加
      scroller: ".site__main__contents",
      onEnter: () => jsFadeUpTitle.classList.add("js-fadeUp-ttl"),
    }
  });
});

DEMOサイトのコードまとめ

DEMOサイトのコードをまとめて記載します。

HTML

/**hmtl**/

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta name="robots" content="noindex">
  <title>Locomotive-scroll</title>
  <link rel="stylesheet" href="https://unpkg.com/ress/dist/ress.min.css">
  <link rel="stylesheet" href="css/locomotive-scroll.min.css">
  <link rel="stylesheet" href="css/style.css">
  <link rel="preconnect" href="https://fonts.googleapis.com">
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
  <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@500;700;800&family=Zen+Kaku+Gothic+Antique:wght@500;700&display=swap" rel="stylesheet">
</head>
<body class="over-hidden">

  <div id="load__mask"></div>
  <div class="loader__box" id="js__loader">
    <div class="loader__img01 loader__img">
      <img src="images/fashion-11.jpg" alt="">
    </div>
    <div class="loader__img02 loader__img">
      <img src="images/fashion-12.jpg" alt="">
    </div>
    <div class="loader__img03 loader__img">
      <img src="images/fashion-13.jpg" alt="">
    </div>
    <div class="loader__img04 loader__img">
      <img src="images/fashion-14.jpg" alt="">
    </div>
    <div class="loader__img05 loader__img">
      <img src="images/fashion-15.jpg" alt="">
    </div>
  </div>

  <div class="site__main__contents" data-scroll-container>

  <header class="header" id="js-header">
    <div class="header__inner">
      <nav>
        <ul>
          <li><a href="">menu01</a></li>
          <li><a href="">menu01</a></li>
          <li><a href="">menu01</a></li>
          <li><a href="">menu01</a></li>
        </ul>
      </nav>
      <div class="header__logo">
        <h2>ONE SITE</h2>
      </div>
    </div>
  </header>

  <section class="fv" data-scroll-section data-scroll-section-id="section0">
    <div class="fv__inner">
      <div class="fv__ttl">
        <h1>
          <span class="fv-span"><span class="fv-span-01">ONE</span></span>
          <span class="fv-span"><span class="fv-span-02">Locomotive</span></span>
          <span class="fv-span"><span class="fv-span-03">Scroll.</span></span>
        </h1>
      </div>
      <div class="fv__img">
        <img src="images/fashion-yellow.jpg" alt="" id="js-fv-img">
      </div>
    </div>
  </section>

  <section class="message" data-scroll-section data-scroll-section-id="section1" data-scroll-section-inview>
    <div class="message__inner">
      <div class="message__txt" data-scroll data-scroll-speed="2">
        <p class="fade-up-ttl">MESSAGE</p>
        <h3 class="fade-up-ttl">パララックスサイトを実装します</h3>
        <h4 class="fade-up-ttl"><span>このデモサイトは、Locomotive Scrollを使用して慣性スクロールを実装しています。</span><br>
        <span>果たして、このようなサイトを作る案件はでてくるのでしょうか。いつかモダンな技術を使用した案件に携わってみたいとおもうこの頃。</span><br>
        <span>まあ、とりあえずこれ使いこなせるようにならないと始まらないということで、実装していきますよ。</span><br>
        <span>てきすと□てきすと□てきすと□てきすと□てきすと□てきすと□てきすと□てきすと□てきすと□てきすと□てきすと□</span><br>
        <span>てきすと□てきすと□てきすと□てきすと□てきすと□てきすと□てきすと□てきすと□てきすと□てきすと□てきすと□てきすと□</span>
        </h4>
      </div>
      <p class="message__img message__img__01 js-message__img"><img src="images/fashion-01.jpg" alt=""></p>
      <p class="message__img message__img__02 js-message__img" data-scroll data-scroll-speed="1"><img src="images/fashion-03.jpg" alt=""></p>
    </div>
    <div class="border"></div>
  </section>


  <section class="news" data-scroll-section data-scroll-section-id="section2">
    <div class="news__inner">
      <ul class="news__ul" data-scroll data-scroll-speed="1">
        <li class="fade-up-ttl"><a href="">GWのお知らせ</a>
          <p>news</p>
          <span>2022.03.28</span>
        </li class="fade-up-ttl">
        <li class="fade-up-ttl"><a href="">夏季休業について</a>
          <p>news</p>
          <span>2022.02.20</span>
        </li>
        <li class="fade-up-ttl"><a href="">今後の方針</a>
          <p>news</p>
          <span>2022.01.22</span>
        </li>
      </ul>
      <p class="news__logo" data-scroll data-scroll-speed="4">ONE SITE</p>
    </div>
    <div class="border"></div>
  </section>


  <section class="about" data-scroll-section data-scroll-section-id="section3" id="about-box">
    <div class="about__inner">
      <div class="about__box">
        <div>
            <h4 class="fade-up-ttl-02">服を着よう<br>
            なぜなら捕まるから</h4>
            <p class="fade-up-ttl">fashionアイテムfashionアイテムfashionアイテムfashionアイテムfashionアイテムfashionアイテムfashionアイテムfashionアイテムfashionアイテムfashionアイテムfashionアイテムfashionアイテムfashionアイテムfashionアイテムfashionアイテムfashionアイテム</p>
        </div>
        <p class="about__box__img js-message__img" id="fixed-img" data-scroll data-scroll-sticky data-scroll-target="#about-box"><img src="images/fashion-04.jpg" alt=""></p>
      </div>
    </div>
  </section>

  <section class="product" data-scroll-section data-scroll-section-id="section4">
    <div class="border__x"></div>
    <div class="product__inner">
      <div class="product__box" data-scroll data-scroll-speed="0">
        <h5 data-scroll>promotion item</h5>
        <p class="product__txt" data-scroll>服は着るものだ服は着るものだ服は着るものだ服は着るものだ服は着るものだ</p>
        <p class="product__txt" data-scroll>服は着るものだ服は着るものだ服は着るものだ</p>
        <p class="product__txt" data-scroll>服は着るものだ服は着るものだ服は着るものだ服は着るものだ</p>
        <p class="product__sub" data-scroll>fashion item is big ootanisan fashion item is big <br>ootanisan fashion item is big ootanisan fashion item is big ootanisan fashion item is big ootanisan fashion item is big ootanisan</p>
        <p class="product__btn" data-scroll><a href="">read more</a></p>
      </div>
    </div>
    <div class="product__img js-img01">
      <img src="images/fashion-05.jpg" alt="">
    </div>
    <div class="product__img js-img02">
      <img src="images/fashion-06.jpg" alt="">
    </div>
    <div class="product__img js-img03">
      <img src="images/fashion-07.jpg" alt="">
    </div>
    <div class="product__img js-img04">
      <img src="images/fashion-08.jpg" alt="">
    </div>
    <div class="product__img js-img05">
      <img src="images/fashion-09.jpg" alt="">
    </div>
  </section>


  <section class="works" id="works" data-scroll-section data-scroll-section-id="section5">
    <div class="works__inner" id="works-inner-js">
      <div class="works__contents">

        <div class="works__scroll__bar" data-scroll data-scroll-sticky data-scroll-target="#works-inner-js"></div>

        <div class="works__left">
          <div class="works__left__list">
            <article class="works__left__item" id="works__item__01">
              <h3 class="fade-up-ttl">テスト投稿Works01<br>
              <span>Worksテスト</span>
              </h3>
            </article>
            <article class="works__left__item" id="works__item__02">
              <h3 class="fade-up-ttl">テスト投稿Works02<br>
                <span>Worksテスト</span>
                </h3>
            </article>
            <article class="works__left__item" id="works__item__03">
              <h3 class="fade-up-ttl">テスト投稿Works03<br>
                <span>Worksテスト</span>
                </h3>
            </article>
            <article class="works__left__item" id="works__item__04">
              <h3 class="fade-up-ttl">テスト投稿Works04<br>
                <span>Worksテスト</span>
                </h3>
            </article>
            <article class="works__left__item" id="works__item__05">
              <h3 class="fade-up-ttl">テスト投稿Works05<br>
                <span>Worksテスト</span>
                </h3>
            </article>
            <article class="works__left__item" id="works__item__06">
              <h3 class="fade-up-ttl">テスト投稿Works06<br>
                <span>Worksテスト</span>
                </h3>
            </article>
          </div>
        </div>

        <div class="works__right">
          <div id="works__right__js" data-scroll data-scroll-sticky data-scroll-target="#works-inner-js">
            <div class="works__right__inner">
              <ul class="works__right__gallery">
                <li id="works__right__list1" class="top__img"><img src="images/fashion-10.jpg" alt=""></li>
                <li id="works__right__list2" class="top__img"><img src="images/fashion-11.jpg" alt=""></li>
                <li id="works__right__list3" class="top__img"><img src="images/fashion-12.jpg" alt=""></li>
                <li id="works__right__list4" class="top__img"><img src="images/fashion-13.jpg" alt=""></li>
                <li id="works__right__list5" class="top__img"><img src="images/fashion-14.jpg" alt=""></li>
                <li id="works__right__list6" class="top__img"><img src="images/fashion-15.jpg" alt=""></li>
              </ul>                
            </div>
          </div>
        </div>
      </div>
    </div>
  </section>

  <footer class="footer" data-scroll-section data-scroll data-scroll-section-id="section6" data-scroll-section-inview>
    <div class="footer__inner" data-scroll data-scroll-speed="-7.5">
      <p><img src="images/fashion-08.jpg" alt="">
      </p>
    </div>
  </footer>

</div>


  <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.10.2/gsap.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.10.2/ScrollTrigger.min.js"></script>
  <script src="js/locomotive-scroll.min.js"></script>
  <script type="text/javascript" src="js/main.js"></script>
</body>
</html>

Scss

ローディング・ヘッダー・ファーストビュー

/**scss**/

@use '../setting/setting' as *;


body {
  font-family: $family-poppins;
  font-family: $family-zen-kaku;
  background-color: $color-bg;
  color: $color-white;
  word-break: break-all;
}

.over-hidden {
  overflow: hidden;
}

html {
  font-size: 62.5%;
}

a {
  text-decoration: none;
  color: $color-white;
}

li {
  list-style: none;
}


#load__mask {
  background-color: $color-bg-02;
  display: block;
  height: 100%;
  position: fixed;
  left: 0;
  opacity: 1;
  pointer-events: auto;
  top: 0;
  transform: scaleY(1) translateZ(10px);
  transform-origin: center top;
  width: 100%;
  z-index: 9999;
  &.reval {
    height: 0;
    opacity: 0;
    pointer-events: none;
    transform: scaleY(0) translateZ(10px);
    transform-origin: center top;
    width: 0;
    transition: transform 1s cubic-bezier(.19,1,.22,1) 0s, opacity 0s linear 1s, width 0s linear 1s, height 0s linear 1s;
  }
}


#js__loader {
  &.js-fadeOut {
    animation: fadeOut 1s cubic-bezier(.18,.58,.58,.9) forwards;
  }
}
@keyframes fadeOut {
  0% {
    opacity: 1;
  }
  100% {
    opacity: 0;
    display: none;
    visibility: hidden;
  }
}
.loader__box {
  width: 100%;
  height: 100vh;
  background-color: $color-bg;
  overflow: hidden;
  position: fixed;
  top: 0;
  z-index: 999;
  .loader__img {
    opacity: 0;
    overflow: hidden;
    transition: all .7s ease;
    position: absolute;
    &.js-animated {
      opacity: 1;
      img {
        transform: scale(1);
      }
    }
    &01 {
      width: 25%;
      top: 0%;
      left: 50%;
    }
    &02 {
      width: 20%;
      top: 36%;
      right: 0;
    }
    &03 {
      width: 16%;
      top: 13%;
      left: 17%;
    }
    &04 {
      width: 23%;
      left: 0;
      bottom: 0;
    }
    &05 {
      width: 18%;
      left: 40%;
      bottom: 0;
    }
    img {
      width: 100%;
      height: auto;
      transform-origin: center;
      transform: scale(1.2);
      transition: all .8s ease-out;
      display: block;
    }
  }
}

.site__main__contents {
  overflow: hidden;
}

.header {
  opacity: 0;
  transform: translateY(-110%);
  transition:opacity .3s, transform .5s;
  position: relative;
  z-index: 100;
  &__inner {
    display: flex;
    justify-content: space-between;
    align-items: center;
    @include inner;
    padding-top: 2rem;
    nav {
      ul {
        display: flex;
        li {
          &:nth-child(even) {
            margin: {
              left: 8%;
              right: 8%;
            };
          }
          a {
            font-size: 1.8rem;
            letter-spacing: 0.03em;
            font-weight: $weight-bold;
            font-family: $family-poppins;
            white-space: nowrap;
          }
        }
      }
    }
  }
  &__logo {
    h2 {
      font-size: 3rem;
      letter-spacing: 0.05em;
      font-family: $family-poppins;
    }
  }
}

.fv {
  &__inner {
    width: 90%;
    height: 100%;
    max-width: 1380px;
    margin: 0 auto;
    position: relative;
    margin: {
      bottom: 25%;
    };
  }
  &__ttl {
    position: relative;
    z-index: 2;
    padding: {
      top: 20%;
    };
    h1 {
      font-size: clamp(5rem, 13vw ,17rem);
      font-family: $family-poppins;
      font-weight: $weight-bold;
      line-height: 1;
      .fv-span {
        overflow: hidden;
        display: block;
        span {
          display: block;
          opacity: 0;
          margin: -11px 0;
          transform: translateY(110%);
          transition:opacity 1s, transform 1.2s;
        }
      }
    }
  }
  &__img {
    position: absolute;
    width: 53.444%;
    right: 0%;
    top: 28px;
    z-index: 1;
    overflow: hidden;
    transition: opacity 1s cubic-bezier(.19,1,.22,1), transform 1s cubic-bezier(.19,1,.22,1);
    img {
      width: 100%;
      display: block;
      object-fit: cover;
      transform: scale(1.2);
      transform-origin: center;
      opacity: 0;
      transition: transform 1.5s cubic-bezier(.19,1,.22,1), opacity 1s cubic-bezier(.19,1,.22,1);
    }
  }
}


.border {
  height: 1px;
  background-color: $color-white;
  width: 90%;
  margin: 12% auto;
  transform-origin: center left;
  transform: scale(0);
  transition: transform .6s cubic-bezier(.215,.61,.355,1);
  &.jsBorder {
    transform: scale(1);
  }
}

MESSAGEセクション

/**scss**/

@use '../setting/setting' as *;

.message {
  margin: {
    top: 10%;
  };
  &__inner {
    @include inner;
    position: relative;
    padding: {
      bottom: 30%;
    };
  }
  &__txt {
    width: 58%;
    min-width: 408px;
    margin: 0 5% 0 auto;
    position: relative;
    z-index: 2;
    p {
      font-size: clamp(3.5rem, 9vw ,11rem);
      font-family: $family-poppins;
      font-weight: $weight-bold;
      letter-spacing: 0.05em;
      padding-top: 14%;
    }
    h3 {
      font-size: clamp(2rem, 3vw ,4rem);
      font-weight: $weight-bold;
      margin: {
        top: 9%;
        bottom: 6%;
      };
    }
    h4 {
      font-size: clamp(1.6rem, 1.8vw ,1.8rem);
      font-weight: $weight-medium;
      span {
        display: block;
        margin: -5px 0;
      }
    }
  }
  &__img {
    position: absolute;
    z-index: 1;
    &__01 {
      left: 0;
      top: -5%;
      width: 50%;
    }
    &__02 {
      right: 0;
      bottom: 0;
      width: 33%;
    }
    img {
      width: 100%;
      object-fit: cover;
      display: block;
    }
    }
  }


.fade-up-ttl {
  transform: translateY(60px);
  opacity: 0;
  transition: opacity 1.4s cubic-bezier(0.215, 0.61, 0.355, 1) 0.5s, transform 1.4s cubic-bezier(0.215, 0.61, 0.355, 1) 0.5s;
  &.js-fadeUp-ttl {
    transform: translateY(0);
    opacity: 1;
  }
}

.js-message__img {
  opacity: 0;
  overflow: hidden;
  transition: opacity .5s linear 0s;
  &.js-fadeImg {
    opacity: 1;
    img {
      transform: scale(1);
    }
  }
  img {
    transform-origin: center;
    transform: scale(1.4);
    transition: transform 3s cubic-bezier(.19,1,.22,1) 0s;
  }
}

NEWSセクション

/**scss**/

@use '../setting/setting' as *;

.news {
  padding: 10% 0;
  &__inner {
    @include inner;
    position: relative;
  }
  &__ul {
    width: 50%;
    min-width: 570px;
    margin: 0 auto;
    li {
      &:nth-child(even) {
        margin: 40px 0;
      }
      a {
          font-size: clamp(2.6rem, 3.5vw ,5.5rem);
          letter-spacing: 0.02em;
          font-weight: $weight-black;
      }
      p {
        font-size: 1.8rem;
        font-weight: $weight-medium;
        font-family: $family-poppins;
        margin: {
          bottom: 16px;
        };
      }
      span {
          font-size: 1.5rem;
          font-weight: $weight-medium;
          font-family: $family-poppins;
      }
    }
  }
  &__logo {
    font-size: clamp(4rem, 6vw, 7rem);
    font-weight: $weight-bold;
    font-family: $family-poppins;
    letter-spacing: 0.1em;
    position: absolute;
    right: 10%;
    top: 63%;
  }
}

ABOUTセクション

/**scss**/

@use '../setting/setting' as *;

.about {
  padding: 0 0 20%;
  margin-bottom: 20%;
  &__inner {
    @include inner;
  }
  &__box {
    display: flex;
    align-items: center;
    justify-content: space-between;
    div {
      width: 40%;
      h4 {
        font-size: clamp(3rem, 4vw, 5.6rem);
        font-weight: $weight-black;
        margin: {
          bottom: 40px;
        };
      }
      p {
        font-size: clamp(1.8rem, 2vw, 2.2rem);
        font-weight: $weight-medium;
      }
    }
    &__img {
      width: 50%;
      img {
        width: 100%;
        display: block;
      }
    }
  }
}

.fade-up-ttl-02 {
  opacity: 0;
  transform-origin: center top;
  transform-style: preserve-3d;
  transform: translateY(100%) rotateX(-80deg);
  transition: opacity 0s cubic-bezier(0.215, 0.61, 0.355, 1) 0.5s,transform 0s cubic-bezier(0.215, 0.61, 0.355, 1) 0.5s;
  transition-duration: 0.8s;
  &.js-fadeUp-ttl-02 {
    transform: none;
    opacity: 1;
    transition-duration: 0.8s;
  }
}

PRODUCTセクション

/**scss**/

@use '../setting/setting' as *;

.product {
  position: relative;
  height: calc(180vh + 200px);
  padding: 20% 0 0;
  margin-bottom: 13%;
  .border__x {
    width: 1px;
    height: 200px;
    background-color: $color-white;
    position: absolute;
    left: 50%;
    top: 0;
    transform: translateX(-50%);
    animation: verticalAnime 2s cubic-bezier(.19,1,.22,1) 0s infinite;
  }
  &__inner {
    @include inner;
  }
  &__box {
    width: 50%;
    min-width: 650px;
    margin: 0 auto;
    text-align: center;
    position: relative;
    z-index: 2;
    padding: {
      top: 25%;
    };
    h5 {
      font-size: clamp(4.5rem, 7vw, 7rem);
      font-weight: $weight-black;
      margin-bottom: 8%;
    }
    .product__txt {
      font-size: 1.8rem;
      line-height: 36px;
    }
    .product__sub {
      font-size: 1.6rem;
      font-family: $family-poppins;
      margin-top: 4%;
    }
    .product__btn {
      width: 60%;
      text-align: center;
      margin: 10% auto 0;
      a {
        font-size: 2rem;
        letter-spacing: 0.02em;
        display: block;
        padding: 20px;
        background-color: $color-btn;
      }
    }
  }
  &__img {
    position: absolute;
    transition: transform .4s;
    img {
      width: 100%;
      display: block;
    }
  }
  .js-img01 {
    width: 38%;
    top: 8%;
    left: 0;
  }
  .js-img02 {
    width: 31%;
    right: 0;
    top: 21%;
  }
  .js-img03 {
    width: 33%;
    left: 37%;
    top: 58%;
  }
  .js-img04 {
    width: 42%;
    left: 5%;
    top: 38%;
  }
  .js-img05 {
    width: 36%;
    bottom: 10%;
    right: 0%;
  }
}

@keyframes verticalAnime {
  0% {
    clip-path: inset(0 0 100% 0);
  }
  25% {
    clip-path: inset(0 0 0 0);
  }
  75% {
    clip-path: inset(0 0 0 0);
  }
  100% {
    clip-path: inset(100% 0 0 0);
  }
}
.product__img {
  opacity: 0;
}

WORKSセクション

/**scss**/

@use '../setting/setting' as *;

.works {
  background-color: $color-bg-02;
  color: $color-white;
  transition: background-color 1.9s cubic-bezier(0.215, 0.61, 0.355, 1);
  &.js-bg-change01 {
    background-color: $bg-change01;
  }
  &.js-bg-change02 {
    background-color: $bg-change02;
  }
  &.js-bg-change03 {
    background-color: $bg-change03;
  }
  &.js-bg-change04 {
    background-color: $bg-change04;
  }
  &.js-bg-change05 {
    background-color: $bg-change05;
  }
  &__contents {
    position: relative;
    display: flex;
    flex-wrap: wrap;
    overflow: hidden;
    width: 100%;
    min-height: 100vh;
  }
  &__scroll__bar {
    position: absolute;
    top: 0;
    bottom: 0;
    right: 50%;
    width: 1px;
    height: 0;
    z-index: 100;
    background-color: $color-white;
    transition: height 1.9s cubic-bezier(0.215, 0.61, 0.355, 1);
  }
  &__left {
    position: relative;
    width: 50%;
    &__item {
      position: relative;
      display: flex;
      flex-flow: column;
      justify-content: center;
      align-items: center;
      height: 100vh;
      padding: 0 10%;
      h3 {
        font-size: 2.8rem;
        line-height: 4.3rem;
        letter-spacing: 0.02em;
        span {
          font-size: 2rem;
          font-weight: $weight-medium;
        }
      }
    }
  }
  &__right {
    width: 50%;
    #works__right__js {
      height: 100vh;
    }
    &__inner {
      width: 100%;
      height: 100vh;
    }
    ul {
      position: relative;
      width: 100%;
      height: 100vh;
      overflow: hidden;
      li {
        position: absolute;
        bottom: 0;
        left: 0;
        width: 100%;
        height: 100vh;
        overflow: hidden;
        z-index: -1;
        opacity: 0;
        display: flex;
        flex-flow: column;
        flex-wrap: wrap;
        justify-content: center;
        align-items: center;
        margin: auto;
        transition: transform 1.9s cubic-bezier(0.215, 0.61, 0.355, 1),opacity 1.9s cubic-bezier(0.215, 0.61, 0.355, 1),z-index 0.5s cubic-bezier(0.215, 0.61, 0.355, 1);
        &.js-active {
          opacity: 1;
          z-index: 1;
        }
        img {
          width: 25vw;
          height: auto;
        }
      }
    }
  }
}


.footer {
  height: 100vh;
  overflow: hidden;
  width: 100%;
  &__inner {
    height: 100%;
  }
  p {
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-flow: column;
    img {
      width: 30%;
      height: auto;
      display: block;
      margin: 0 auto;
    }
  }
}

JavaScript

/**js**/

// 読み込み後アニメーション
window.addEventListener("load",function(){
  const LoadMask = document.getElementById('load__mask');

  setTimeout(function () {
    LoadMask.classList.add('reval');
  },500);

  
  const LoaderImgs = document.querySelectorAll('.loader__img');
  
  LoaderImgs.forEach(function( LoaderImg,index){
    setTimeout(function(){
      LoaderImg.classList.add('js-animated');
    }, 1100 + 300 * (index + 1));
  });


  const JsLoader = document.getElementById('js__loader');
  const LoaderImg05 = document.querySelectorAll('.loader__img05');
  const body = document.querySelector('body');

  setTimeout(function(){
    JsLoader.classList.add('js-fadeOut');
    body.classList.remove('over-hidden');
  }, 4000);


  const tl = gsap.timeline();
  const fvImg = document.querySelectorAll('.fv__img');
  const jsFvImg = document.getElementById('js-fv-img');
  const jsFvSpan01 = document.querySelectorAll('.fv-span-01');
  const jsFvSpan02 = document.querySelectorAll('.fv-span-02');
  const jsFvSpan03 = document.querySelectorAll('.fv-span-03');
  const jsHeader = document.getElementById('js-header');

  tl.to(
    fvImg,
    {
      y:0,
      opacity:1,
    },
    5
  )

  .to(
    jsFvImg,
    {
      scale: 1,
      opacity:1,
    },
    5
  )

  .to(
    jsFvSpan01,
    {
      y:0,
      opacity: 1,
      ease: Power4.out,
    },
    "-=.5"
  )

  .to(
    jsFvSpan02,
    {
      y:0,
      opacity: 1,
      ease: Power4.out,
    },
    "-=0.4"
  )

  .to(
    jsFvSpan03,
    {
      y:0,
      opacity: 1,
      ease: Power4.out,
    },
    "-=0.4"
  )

  .to(
    jsHeader,
    {
      y:0,
      opacity:1,
      ease: Circ.out,
    },
    "+=.2"
  )

});

//locomotiveScroll
const locoScroll = new LocomotiveScroll({
  el: document.querySelector('[data-scroll-container]'),
  smooth: true,
});

locoScroll.on("scroll", ScrollTrigger.update);

ScrollTrigger.refresh();
ScrollTrigger.scrollerProxy(".site__main__contents", {
  scrollTop(value) {
    return arguments.length
      ? locoScroll.scrollTo(value, 0, 0)
      : locoScroll.scroll.instance.scroll.y;
  },
  getBoundingClientRect() {
    return {
      top: 0,
      left: 0,
      width: window.innerWidth,
      height: window.innerHeight,
    };
  },

  pinType: document.querySelector(".site__main__contents").style.transform
    ? "transform"
    : "fixed",
});


// スクロールに合わせて画像を表示させる
gsap.registerPlugin(ScrollTrigger);

// messageの画像
const messageImgs = document.querySelectorAll('.js-message__img');

messageImgs.forEach((messageImg, index) => {
  gsap.to(
    messageImg,
    {
      scrollTrigger: {
        trigger: messageImg,
        start: 'top bottom-=300',
        end: 'top top',
        once: true,
        scroller: ".site__main__contents",
        onEnter: () => messageImg.classList.add("js-fadeImg"),
      }
    }
  );
});

// messageのタイトル
const jsFadeUpTitles = document.querySelectorAll('.fade-up-ttl');

jsFadeUpTitles.forEach((jsFadeUpTitle, index) => {
  gsap.to(
    jsFadeUpTitle,
  {
    scrollTrigger: {
      trigger: jsFadeUpTitle,
      start: 'top-=100 bottom',
      end: 'top top',
      once: true,
      scroller: ".site__main__contents",
      onEnter: () => jsFadeUpTitle.classList.add("js-fadeUp-ttl"),
    }
  });
});

// セクション間の横線
const jsBorders = document.querySelectorAll('.border');

jsBorders.forEach((jsBorder, index) => {
  gsap.to(
    jsBorder,
  {
    scrollTrigger: {
      trigger: jsBorder,
      start: 'top center',
      end: 'top top',
      once: true,
      scroller: ".site__main__contents",
      onEnter: () => jsBorder.classList.add("jsBorder"),
    }
  });
});

// タイトルが3Dっぽくフェードアップ
const jsFadeUpTitle02s = document.querySelectorAll('.fade-up-ttl-02');

jsFadeUpTitle02s.forEach((jsFadeUpTitle02, index) => {
  gsap.to(
    jsFadeUpTitle02,
  {
    scrollTrigger: {
      trigger: jsFadeUpTitle02,
      start: 'top bottom',
      end: 'top top',
      once: true,
      scroller: ".site__main__contents",
      onEnter: () => jsFadeUpTitle02.classList.add("js-fadeUp-ttl-02"),
    }
  });
});


// 画像がスクロールに合わせて表示されて消えていく
const ProductImgs = document.querySelectorAll('.product__img');

ProductImgs.forEach((ProductImg, index) => {
  gsap.timeline({
    scrollTrigger: {
      trigger: ProductImg,
      start: 'top bottom',
      end:'bottom top',
      scrub: 1.2,
      scroller: ".site__main__contents",
    }
  })
  .to(ProductImg,{
    opacity: -0.2,
    y: 150,
  })
  .to(ProductImg,{
    opacity: 0.8,
    y: 0,
  })
  .to(ProductImg,{
    opacity: -0.2,
    y: -150,
  })
});


//スクロールに合わせて縦に線が伸びていく
const Works = document.getElementById('works');
const WorksItem06 = document.getElementById('works__item__06');
const WorksBar = document.querySelectorAll('.works__scroll__bar');

gsap.fromTo(
  WorksBar, {
    height: '0vh',
  },
  {
    height: '100vh',
    scrollTrigger: {
      trigger: Works,
      start: 'top top',
      endTrigger: WorksItem06,
      end: 'bottom bottom',
      scrub: 1,
      scroller: ".site__main__contents",
    }
  }
)

//背景がスクロールに合わせて変わっていく
ScrollTrigger.create({
  trigger: '#works__item__02',
  start: 'top top',
  endTrigger: '#works__item__03',
  end: 'center center',
  scroller: ".site__main__contents",
  toggleClass: {
    targets: Works,
    className: 'js-bg-change01',
  }
})
ScrollTrigger.create({
  trigger: '#works__item__03',
  start: 'top top',
  endTrigger: '#works__item__04',
  end: 'center center',
  scroller: ".site__main__contents",
  toggleClass: {
    targets: Works,
    className: 'js-bg-change02',
  }
})
ScrollTrigger.create({
  trigger: '#works__item__04',
  start: 'top top',
  endTrigger: '#works__item__05',
  end: 'center center',
  scroller: ".site__main__contents",
  toggleClass: {
    targets: Works,
    className: 'js-bg-change03',
  }
})
ScrollTrigger.create({
  trigger: '#works__item__05',
  start: 'top top',
  endTrigger: '#works__item__06',
  end: 'center center',
  scroller: ".site__main__contents",
  toggleClass: {
    targets: Works,
    className: 'js-bg-change04',
  }
})
ScrollTrigger.create({
  trigger: '#works__item__06',
  start: 'top top',
  end: 'bottom+=100% bottom',
  scroller: ".site__main__contents",
  toggleClass: {
    targets: Works,
    className: 'js-bg-change05',
  }
})
//固定してテキストと画像がスクロールに合わせて入れ替わっていく
const WorksRightList01 = document.getElementById('works__right__list1');
const WorksRightList02 = document.getElementById('works__right__list2');
const WorksRightList03 = document.getElementById('works__right__list3');
const WorksRightList04 = document.getElementById('works__right__list4');
const WorksRightList05 = document.getElementById('works__right__list5');
const WorksRightList06 = document.getElementById('works__right__list6');

ScrollTrigger.create({
  trigger: '#works__item__01',
  start: 'top top',
  endTrigger: '#works__item__02',
  end:'center center',
  scroller: ".site__main__contents",
  toggleClass:{
    targets: WorksRightList01,
    className: 'js-active',
  }
  }
)
ScrollTrigger.create({
  trigger: '#works__item__02',
  start: 'top top',
  endTrigger: '#works__item__03',
  end:'center center',
  scroller: ".site__main__contents",
  toggleClass:{
    targets: WorksRightList02,
    className: 'js-active',
  }
  }
)
ScrollTrigger.create({
  trigger: '#works__item__03',
  start: 'top top',
  endTrigger: '#works__item__04',
  end:'center center',
  scroller: ".site__main__contents",
  toggleClass:{
    targets: WorksRightList03,
    className: 'js-active',
  }
  }
)
ScrollTrigger.create({
  trigger: '#works__item__04',
  start: 'top top',
  endTrigger: '#works__item__05',
  end:'center center',
  scroller: ".site__main__contents",
  toggleClass:{
    targets: WorksRightList04,
    className: 'js-active',
  }
  }
)
ScrollTrigger.create({
  trigger: '#works__item__05',
  start: 'top top',
  endTrigger: '#works__item__06',
  end:'center center',
  scroller: ".site__main__contents",
  toggleClass:{
    targets: WorksRightList05,
    className: 'js-active',
  }
  }
)
ScrollTrigger.create({
  trigger: '#works__item__06',
  start: 'top top',
  end:'bottom+=100% bottom',
  scroller: ".site__main__contents",
  toggleClass:{
    targets: WorksRightList06,
    className: 'js-active',
  }
  }
)