メインコンテンツまで移動する

画像を徐々に表示する CSS アニメーション

  • 公開日
  • 更新日

Akira Web クリエイター

左から・右から・真ん中から

画像が際立つ CSS アニメーションの紹介です。アイキャッチ画像や本文中の画像、ランディングページの画像など、いろいろな画像に使えます。

サンプルは、便宜上アニメーションの再生回数を無限にしています(提示している CSS のアニメーション回数は 1 回です)。

共通の HTML 。

<div class="img-wrap">
  <img />
</div>

左から徐々に表示するアニメーション。

CSS 。

.img-wrap {
  overflow: hidden;
  position: relative;
}

.img-wrap::before {
  animation: img-wrap 2s cubic-bezier(0.4, 0, 0.2, 1) forwards;
  background: #fff;
  content: '';
  inset: 0;
  pointer-events: none;
  position: absolute;
  z-index: 1;
}

@keyframes img-wrap {
  100% {
    transform: translateX(100%);
  }
}

上記 CSS の translateX(100%)translateX(-100%) と値をマイナスにすると、右から表示するアニメーションに。

CSS 。

.img-wrap {
  overflow: hidden;
  position: relative;
}

.img-wrap::before {
  animation: img-wrap 2s cubic-bezier(0.4, 0, 0.2, 1) forwards;
  background: #fff;
  content: '';
  inset: 0;
  pointer-events: none;
  position: absolute;
  z-index: 1;
}

@keyframes img-wrap {
  100% {
    transform: translateX(-100%);
  }
}

今度は画像の中心から、円形に広がるアニメーション。

CSS 。

.img-wrap {
  animation: img-wrap 2s cubic-bezier(0.4, 0, 0.2, 1);
}

@keyframes img-wrap {
  0% {
    clip-path: circle(0 at 50% 50%);
    -webkit-clip-path: circle(0 at 50% 50%);
  }

  100% {
    clip-path: circle(100% at 50% 50%);
    -webkit-clip-path: circle(100% at 50% 50%);
  }
}

ただし、clip-path は GPU アクセラレーションで処理されないため、ブラウザに負荷をかけます。また、なめらかなアニメーションにならないデメリットがあります。

2 つの領域で異なるアニメーション

画像を 2 つの領域に分け、それぞれ異なるアニメーションにしてみます。HTML は、これまでのものと変わりません。

<div class="img-wrap">
  <img />
</div>

画像の上部は左から、下部は右から表示するアニメーション。

CSS 。

.img-wrap {
  overflow: hidden;
  position: relative;
}

.img-wrap::before,
.img-wrap::after {
  animation: 2s cubic-bezier(0.4, 0, 0.2, 1) forwards;
  background: #fff;
  content: '';
  pointer-events: none;
  position: absolute;
  z-index: 1;
}

.img-wrap::before {
  animation-name: img-wrap-before;
  inset: 0 0 50%;
}

.img-wrap::after {
  animation-name: img-wrap-after;
  inset: 50% 0 0;
}

@keyframes img-wrap-before {
  100% {
    transform: translateX(100%);
  }
}

@keyframes img-wrap-after {
  100% {
    transform: translateX(-100%);
  }
}

今度は画像の左側は上から、右側は下から表示するアニメーション。

CSS 。

.img-wrap {
  overflow: hidden;
  position: relative;
}

.img-wrap::before,
.img-wrap::after {
  animation: 2s cubic-bezier(0.4, 0, 0.2, 1) forwards;
  background: #fff;
  content: '';
  pointer-events: none;
  position: absolute;
  z-index: 1;
}

.img-wrap::before {
  animation-name: img-wrap-before;
  inset: 0 50% 0 0;
}

.img-wrap::after {
  animation-name: img-wrap-after;
  inset: 0 0 0 50%;
}

@keyframes img-wrap-before {
  100% {
    transform: translateY(100%);
  }
}

@keyframes img-wrap-after {
  100% {
    transform: translateY(-100%);
  }
}

3 つ以上の領域で異なるアニメーション

<div> を使えば画像を 3 つ以上の領域に分け、それぞれ異なるアニメーションを指定できます。

HTML 。

<div class="img-wrap">
  <div class="cover1"></div>
  <div class="cover2"></div>
  <div class="cover3"></div>
  <img />
</div>

CSS 。

.img-wrap {
  overflow: hidden;
  position: relative;
}

.cover1,
.cover2,
.cover3 {
  animation: cover 2s cubic-bezier(0.4, 0, 0.2, 1) forwards;
  background: #fff;
  height: calc(100% / 3);
  left: 0;
  pointer-events: none;
  position: absolute;
  right: 0;
  z-index: 1;
}

.cover1 {
  top: 0;
}

.cover2 {
  animation-delay: 0.2s;
  top: calc(100% / 3);
}

.cover3 {
  animation-delay: 0.4s;
  top: calc(100% / 3 * 2);
}

@keyframes cover {
  100% {
    transform: translateX(100%);
  }
}

2 枚の画像を切り替えながらアニメーション

スライドショーのように 2 枚の画像を切り替えます。最初に表示する画像にも切り替え後の画像にもアニメーションを加えます。アニメーション開始ボタンを押してサンプルのアニメーションをご確認いただけます。サンプルはアニメーションの終了後に画像が 1 枚目に戻りますが、提示している CSS は戻らず 2 枚目の画像を維持します。

HTML 。

<div class="img-wrap">
  <div class="img-container">
    <img />
  </div>
  <div class="img-container">
    <img />
  </div>
</div>

CSS 。

.img-wrap {
  --animation-duration: 6s;

  display: grid;
  overflow: hidden;
  place-content: center;
  position: relative;
}

.img-container {
  aspect-ratio: 16 / 12;
  grid-area: 1 / -1;
}

.img-container:first-child {
  animation: img-container-first var(--animation-duration) linear forwards;
}

.img-container:last-child {
  animation: img-container-last var(--animation-duration) linear calc(
      var(--animation-duration) / 2
    )
    forwards;
  z-index: -1;
}

.img-container::before,
.img-container::after {
  animation: 2s cubic-bezier(0.4, 0, 0.2, 1) forwards;
  background: #fff;
  content: '';
  left: 0;
  pointer-events: none;
  position: absolute;
  right: 0;
  z-index: 1;
}

.img-container::before {
  animation-name: img-container-before;
  bottom: 50%;
  top: 0;
}

.img-container::after {
  animation-name: img-container-after;
  bottom: 0;
  top: 50%;
}

.img-container:last-child::before,
.img-container:last-child::after {
  animation-delay: calc(var(--animation-duration) / 2);
}

.img-wrap img {
  height: 100%;
  object-fit: cover;
  width: 100%;
}

@keyframes img-container-first {
  80% {
    opacity: 1;
  }

  95% {
    opacity: 0;
  }

  100% {
    opacity: 0;
  }
}

@keyframes img-container-last {
  0% {
    opacity: 0;
    z-index: 1;
  }

  10% {
    opacity: 1;
    z-index: 1;
  }

  100% {
    opacity: 1;
    z-index: 1;
  }
}

@keyframes img-container-before {
  100% {
    transform: translateX(100%);
  }
}

@keyframes img-container-after {
  100% {
    transform: translateX(-100%);
  }
}

スクロールアニメーション

画像が画面内に入ると、アニメーションが始まります。画像が見えなくなるまでスクロールすれば、再度アニメーションをご確認いただけます。

HTML 。

<div class="img-wrap">
  <img />
</div>

JavaScript 。

const images = document.querySelectorAll('.img-wrap');
const animationClassName = 'img-animation';

const observer = new IntersectionObserver((entries) => {
  entries.forEach((entry) => {
    if (entry.isIntersecting) {
      entry.target.classList.add(animationClassName);
    } else {
      entry.target.classList.remove(animationClassName);
    }
  });
});

images.forEach((image) => {
  observer.observe(image);
});

アニメーションを最初の 1 回だけ動かす場合は、以下の JavaScript を使います。

const images = document.querySelectorAll('.img-wrap');
const animationClassName = 'img-animation';

const observer = new IntersectionObserver((entries) => {
  entries.forEach((entry) => {
    if (entry.isIntersecting) {
      entry.target.classList.add(animationClassName);
      observer.unobserve(entry.target);
    }
  });
});

images.forEach((image) => {
  observer.observe(image);
});

参考:Intersection observer によるスクロールアニメーション

CSS 。

.img-wrap {
  opacity: 0;
}

.img-animation {
  animation: img-opacity 2s cubic-bezier(0.4, 0, 0.2, 1) forwards;
  overflow: hidden;
  position: relative;
}

.img-animation::before {
  animation: img-animation 2s cubic-bezier(0.4, 0, 0.2, 1) forwards;
  background: #fff;
  content: '';
  inset: 0;
  pointer-events: none;
  position: absolute;
  z-index: 1;
}

@keyframes img-opacity {
  100% {
    opacity: 1;
  }
}

@keyframes img-animation {
  100% {
    transform: translateX(100%);
  }
}

フォローする