画像を徐々に表示する CSS アニメーション
左から・右から・真ん中から
画像が際立つ 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%);
}
}