画像にスクロール連動のアニメーションを付ける方法を解説します。
結論から書くと、まずCSSだけで書けないかを検討してください。animation-timeline: view() を使えば、ライブラリを読み込まずにスクロール連動のアニメーションが作れます。WebGLを使った本格的な歪みや波打ちの表現が必要になった段階で、初めてライブラリを足します。
スポンサーリンク
CSSだけでスクロール連動アニメーションを作る
結論:animation-timeline: view() を指定すると、その要素が画面を横切る間だけアニメーションが進みます。JavaScriptは不要です。
通常の animation は時間で進みますが、animation-timeline を指定すると、スクロール位置で進むようになります。スクロールを戻せばアニメーションも巻き戻ります。
@keyframes reveal {
from {
opacity: 0;
transform: translateY(40px) scale(0.95);
}
to {
opacity: 1;
transform: none;
}
}
.fade-img {
animation: reveal linear both;
/* その要素が画面に入る間で進める */
animation-timeline: view();
/* 進行の範囲を指定する */
animation-range: entry 0% cover 40%;
}
animation-range で「いつ始まっていつ終わるか」を決めます。entry 0% は要素が画面の下から入り始めた時点、cover 40% は画面を4割横切った時点です。
カーテンが開くような表現も、クリップパスを動かせばCSSだけで作れます。
@keyframes curtain-open {
from { clip-path: inset(0 50% 0 50%); }
to { clip-path: inset(0 0 0 0); }
}
.curtain-img {
animation: curtain-open linear both;
animation-timeline: view();
animation-range: entry 10% cover 50%;
}
inset(0 50% 0 50%) は左右から50%ずつ隠した状態です。これを0にしていくと、中央から左右に開くカーテンのように見えます。
対応していないブラウザへの備え
結論:Chrome・Edge 115以降と Safari 18以降が対応しています。Firefox は2026年9月時点でまだ既定で無効なので、@supports で分岐させてください。
/* 未対応ブラウザでは最初から表示された状態にする */
.fade-img {
opacity: 1;
}
@supports (animation-timeline: view()) {
.fade-img {
animation: reveal linear both;
animation-timeline: view();
animation-range: entry 0% cover 40%;
}
}
未対応の環境で「画像が見えないまま」にならないよう、初期状態は表示にしておいてください。アニメーションが効かない環境で opacity: 0 のままだと、画像がまったく出てこなくなります。
動きを減らす設定にしている人への配慮も入れます。
@media (prefers-reduced-motion: reduce) {
.fade-img {
animation: none;
opacity: 1;
}
}
JavaScriptで制御する場合
結論:「一度表示したら戻さない」といった制御が要る場合は、Intersection Observer を使います。
animation-timeline はスクロールに完全連動するため、上にスクロールし直すとアニメーションも戻ります。一度きりの演出にしたいなら、クラスを付けるだけにします。
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
entry.target.classList.add('is-visible');
// 一度きりにするので監視を外す
observer.unobserve(entry.target);
}
});
}, { threshold: 0.2 });
document.querySelectorAll('.fade-img').forEach((el) => observer.observe(el));
.fade-img {
opacity: 0;
transform: translateY(40px);
transition: opacity 0.6s, transform 0.6s;
}
.fade-img.is-visible {
opacity: 1;
transform: none;
}
unobserve() を呼んで監視を外すのがポイントです。これを忘れると、画面外に出るたびに判定が走り続けます。
スポンサーリンク
WebGLで画像を歪ませたい場合
結論:画像を波打たせる、マウスに反応して歪ませるといった表現が必要なら、curtains.js を使います。HTMLの画像要素をWebGLのテクスチャとして扱えるライブラリです。
curtains.js は Martin Laxenaire さんが開発しているバニラJavaScriptのライブラリで、2025年時点でも更新が続いています。<img> や <video> をそのままWebGLの平面(プレーン)に変換し、シェーダーで加工できます。
npmから導入できます。
npm install curtainsjs
基本の考え方は次のとおりです。
- キャンバスを置く要素を用意する
- その中にアニメーションさせたい
<img>を配置する Curtainsを初期化し、画像を含む要素をPlaneとして登録する- 頂点シェーダーとフラグメントシェーダーで見た目を加工する
シェーダーを書く必要があるため、CSSと比べると学習コストははっきり上がります。「ふわっと出す」「カーテンのように開く」程度の表現なら、前半のCSSで十分です。
導入手順とサンプルはcurtains.js の公式サイトにまとまっています。デモも豊富なので、どこまでできるかを先に見てから判断するのがおすすめです。
どの方法を選ぶか
結論:表現したいことの複雑さで決めてください。ライブラリを読み込むほど、ページは重くなります。
▼方法の使い分け
| やりたいこと | 方法 | 追加の読み込み |
|---|---|---|
| ふわっと表示する | animation-timeline: view() |
なし |
| カーテンのように開く | clip-path + 同上 |
なし |
| 一度きりの演出にする | Intersection Observer | なし(自前のJS) |
| 画像を歪ませる・波打たせる | curtains.js | ライブラリ |
上から順に試して、足りなくなったら次へ進むのが結果的にいちばん軽く済みます。最初からライブラリを入れると、実際には使わない機能まで読み込むことになります。
スクロールに応じて色を変える実装はスクロールに応じて色を変える方法5つにまとめました。この記事と同じ animation-timeline を使います。
マウス操作に反応させる演出なら、マウスストーカーの作り方も参考になります。
この記事の訂正について
この記事は当初「curtain.js」というライブラリを紹介していましたが、内容に誤りがありました。2026年9月に全面的に見直しています。
- 紹介していた「curtain.js」(作者 Victa)は画像用ではなく、全画面のパネルをめくるjQueryプラグインでした
- そのcurtain.jsはGitHubで「PROJECT IS NOT MAINTAINED」と明記され、開発が終了しています
- 記載していた
new Curtain()という書き方やオプションは、実際には存在しないものでした - 掲載していた公式サイトのURLも現在つながりません
名前のよく似た「curtains.js」(末尾にs)は別のライブラリで、こちらは現役です。画像にアニメーションを付けるという目的に合うのはこちらでした。