clip-pathを使用した様々なCSSアニメーションの作成方法

画像読み込みに時間がかかっている表現

Webページをスクロールして画像が表示する際には、CSSアニメーションを使用して以下のいずれかのアニメーションで表示されることが一般的です。

  • 非表示から表示
  • 画面外から表示
  • 拡大して表示
  • ぼかして表示
  • 光って表示
  • etc.

色々な方法があるのですが、下の画像のように読み込みに時間がかかっているようなCSSアニメーションの表現が見当たらなかったので、clip-pathを使用して作成してみました。

画像読み込みに時間がかかっているようなCSSアニメーション

※ 上の画像の例だとアニメーションが繰り返し確認できるよう、アニメーションプロパティに「infinite」を指定していますが、これから作成するのは「forwards」を指定した1回だけのアニメーションです。

clip-pathとは

clip-pathはCSSで要素の表示領域を切り抜くためのプロパティです。

このプロパティを使用することで、要素を指定した形状に切り抜くことができます。

例えば以下のように要素に「clip-path: inset(0 0 50% 0);」を指定すれば、上から50%の位置までを切り抜いて表示できます。

.clip-half {
  clip-path: inset(0 0 50% 0);
}
画像読み込みに時間がかかっているようなCSSアニメーション

clip-pathでCSSアニメーション

clip-pathは指定した範囲を切り抜けるので、これを使えば画像読み込みに時間がかかっているようなCSSアニメーションを作成できます。

まず切り抜く要素に「opacity: 0」を指定して非表示にするコードを作成します。

そしてスクロールして表示された要素に追加する「.lazy-load-anime」に「opacity: 1」と「clip-path: inset(0 0 100% 0)」を指定して、animationプロパティで「clip-path: inset(0 0 0 0);」まで10分割したアニメーションを作成します。

「clip-path: inset(0 0 100% 0)」で非表示になるのだから、opacityは使わなくて良いのでは?…と疑問に思われる方もいるかと思います。

実はこのあとに説明するJavaScriptのIntersectionObserverは「clip-path: inset(0 0 100% 0)」で切りの抜いた状態だと交差を検知できないため、このようなコードになっています。

animationの「4s」は「再生時間4秒」です。forwardsがないとアニメーション終了後に最初の状態に戻ってしまうので、必ず追加してください。

.lazy-load-anime-hide {
  opacity: 0;
}

.lazy-load-anime {
  opacity: 1;
  clip-path: inset(0 0 100% 0);
  animation: lazy-load-anime 4s steps(10) forwards;
}

@keyframes lazy-load-anime {
  to {
    clip-path: inset(0 0 0 0);
  }
}

あとはJavaScriptのIntersectionObserverでスクロールして「.lazy-load-anime-hide」に近づいたら「.lazy-load-anime」を追加するコードを作成すれば完成です。

const options = {
  root: null,
  rootMargin: '0px',
  threshold: 0.5,
}

const observerCallback = (entries, observer) => {
  entries.forEach((entry) => {
    if (entry.isIntersecting) {
      entry.target.classList.add('lazy-load-anime')
      observer.unobserve(entry.target)
    }
  })
}

const observer = new IntersectionObserver(observerCallback, options)
const targetElements = document.querySelectorAll('.lazy-load-anime-hide')

if (targetElements) {
  targetElements.forEach((element) => {
    observer.observe(element)
  })
}

clip path inset animation sample

stepを使わずinset(100%)からinset(0)にするとスムーズに四角形が広がって表示されるアニメーションになります。

.lazy-load-anime {
  opacity: 1;
  clip-path: inset(100%);
  animation: lazy-load-anime 2s forwards;
}

@keyframes lazy-load-anime {
  to {
    clip-path: inset(0);
  }
}

clip path inset smooth animation sample

円形の場合はcircleを使います。

circleの場合は開始は0で終了は100%になります。

.lazy-load-anime {
  opacity: 1;
  clip-path: circle(0);
  animation: lazy-load-anime 2s forwards;
}

@keyframes lazy-load-anime {
  to {
    clip-path: circle(100%);
  }
}

clip path circle animation sample

さらにclip-path: polygon()を使えば多角形の切り抜きができるので、次のような中心点(50% 50%)から星型に変形する複雑なCSSアニメーションなども作成できます。

.lazy-load-anime {
  opacity: 1;
  clip-path: polygon(
    50% 50%,
    50% 50%,
    50% 50%,
    50% 50%,
    50% 50%,
    50% 50%,
    50% 50%,
    50% 50%,
    50% 50%,
    50% 50%
  );
  animation: lazy-load-anime 2s forwards;
}

@keyframes lazy-load-anime {
  to {
    clip-path: polygon(
      50% 0%,
      61% 35%,
      98% 35%,
      68% 57%,
      79% 91%,
      50% 70%,
      21% 91%,
      32% 57%,
      2% 35%,
      39% 35%
    );
  }
}

clip path polygon animation sample