LoginSignup
4
3

More than 1 year has passed since last update.

Intersection Observerでスクロールアニメーションをやってみた

Last updated at Posted at 2021-12-31

動きのあるサイトは見てて楽しくていいですね。
スマホの縦スクロールを生かしたサイトなど憧れます。
Qiitaの横にある目次もそうですが、スクロールに連動して変化するものはよく見るのでやってみたいなと思いました。

作ったもの

文字や画像がフェードインするやつをやってみました。

今回作ったのはこちら

下にスクロールするとエレベーターが下がります。
1階下がる毎に1人乗って、1階に着いたら全員降りるというのを表現してみました。
説明しないとよくわかんなくなりました。

参考にさせていただいたのはこちら

Intersection Observerを使いました

従来はJavaScriptの scroll というイベントを利用することが多かったようです。
しかし画面サイズが変わったら再計算しないといけず、スクロールやリサイズのたびに呼び出されるため、パフォーマンスへの悪影響が懸念されるとのこと。
Intersection Observer は要素と要素の交差を検知するAPIだそうで、これを利用すればパフォーマンスの問題が解消でき、SEO対策としてgoogleでも推奨されているそうです。

HTML

<div class="wrapper">
        <div class="card">
            <div class="image animate" data-animate="animImage 2s"><span class="text animate" data-animate="animFloorNumber 2s ">4</span></div>
            <p class="animate" data-animate="animOut 2s "><img class="human" src="image/figure_stand_side.png"></p>
        </div>
    </div>
    <div class="wrapper">
        <div class="card">
            <div class="image animate" data-animate="animImage 2s"><span class="text animate" data-animate="animFloorNumber 2s ">3</span></div>
            <p class="animate" data-animate="animOut 2s "><img class="human" src="image/figure_stand_side.png"></p>
        </div>
    </div>
    <div class="wrapper">
        <div class="card">
            <div class="image animate" data-animate="animImage 2s"><span class="text animate" data-animate="animFloorNumber 2s ">2</span></div>
            <p class="animate" data-animate="animOut 2s "><img class="human" src="image/figure_stand_side.png"></p>
        </div>
    </div>
    <div class="wrapper">
        <div class="card">
            <div class="image animate" data-animate="animImage 2s"><span class="text animate" data-animate="animFloorNumber 2s ">1</span></div>
            <p class="animate" data-animate="animIn 2s ">
              <img class="human_2" src="image/figure_stand_side_3.png">
            </p>
        </div>
    </div>

CSS

.wrapper {
    min-height: 100vh;
    display: flex;
    align-items: center;
    justify-content: space-around;
    background-color: #5dafb8;
    color:white;
    font-family: Verdana, Geneva, Tahoma, sans-serif;
}

.card {
    height: 50vh;
    border-radius: 13px;
    box-shadow: 20px 40px 33px rgba(0,0,0,0.3);
    padding: 2rem;
    width: 35vh;
    background-color:  #6cc2ce;

}

.image {
    width: 35vh;
    height: 20vh;
    background-image: linear-gradient(70deg, RoyalBlue   , DarkTurquoise );
    background-size: cover;
    background-position: center center;
    box-shadow:  10px 15px 15px 6px #3891b4;
    border-radius: 15px;

}

.text {
    font-size:  50px;
    color:      #fff;
    position:   absolute;
  margin: 40px 110px;
}

.human {
  width: 50%;
  margin-left: 100%;
}

.human_2 {
  width: 50%;
  margin-left: 120%;
  transform: scale(-1, 1);
}

.card h2 {
    animation: animOut 2s infinite;
}

.card p {
    animation: animIn 2s infinite;
}

.card .image {
    animation: animImage 2s infinite;
}

@keyframes animImage {
    0% {
      -webkit-transform: scale3d(1, 1, 1);
              transform: scale3d(1, 1, 1);
    }
    30% {
      -webkit-transform: scale3d(1.25, 0.75, 1);
              transform: scale3d(1.25, 0.75, 1);
    }
    40% {
      -webkit-transform: scale3d(0.75, 1.25, 1);
              transform: scale3d(0.75, 1.25, 1);
    }
    50% {
      -webkit-transform: scale3d(1.15, 0.85, 1);
              transform: scale3d(1.15, 0.85, 1);
    }
    65% {
      -webkit-transform: scale3d(0.95, 1.05, 1);
              transform: scale3d(0.95, 1.05, 1);
    }
    75% {
      -webkit-transform: scale3d(1.05, 0.95, 1);
              transform: scale3d(1.05, 0.95, 1);
    }
    100% {
      -webkit-transform: scale3d(1, 1, 1);
              transform: scale3d(1, 1, 1);
    }
  }

@keyframes animIn {
    from {
            transform: translateX(-50px);
            opacity: 0;
    }
    to  {
            transform: translateX(0);
            opacity: 1;
   }
 }

 @keyframes animOut {
     from {
             transform: translateX(50px);
             opacity: 1;
     }
     to  {
             transform: translateX(0);
             opacity: 0;
    }
  }

  @keyframes animFloorNumber {
      from {
              opacity: 0;
      }
      to  {
              opacity: 1;
     }
   }

JavaScript

const callback = (entries) => {
  entries.forEach(
    (entry) => {
      if (entry.isIntersecting) {
        console.log("The element is intersecting >");
        entry.target.style.animation =
        entry.target.dataset.animate;
      } else {
        entry.target.style.animation="none";
      }
    }
  );
}

let observer = new IntersectionObserver(callback);

const animationItems = document.querySelectorAll('.animate');

animationItems.forEach(item => {
  observer.observe(item)
})

まとめ

いろいろ応用して面白いことができそうだなと思いました。
コンセプトをしっかり決めてサイトを作るのを来年の目標にしたいです。

4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3