Webサイトを見ていると、テキストや画像がずっと縦に流れ続ける「無限スクロール」の演出を見かけることがあります。
これをJavaScriptを使わずに、CSSだけで簡単に作れたら便利ですよね。
この記事では、以下のことを初心者にもわかるように解説します:
- CSSアニメーションで縦方向に無限スクロールする方法
- 実際のコード例と活用パターン
- よくある問題点と解決策
- パフォーマンスを考慮した実装のコツ
ニュースティッカーやお知らせ欄、商品リストなど、様々な場面で活用できるテクニックです!
無限スクロールの基本仕組み

CSS無限スクロールの原理
CSSだけで縦に無限スクロールさせるには、以下の仕組みを使います:
基本的なアプローチ
- コンテンツの複製:同じ内容を2セット用意
- アニメーション:
@keyframes
でスクロール動作を定義 - ループ処理:
animation
でその動きを無限に繰り返し
動作原理
- 要素を
translateY
で上方向に移動 - コンテンツの50%移動したところで最初に戻る
- 複製があるため継ぎ目が見えない
必要な要素の構成
HTML構造
.scroll-container (表示枠)
└ .scroll-content (動く内容)
├ コンテンツセット1
└ コンテンツセット2 (同じ内容の複製)
この構造により、スムーズな無限ループを実現します。
基本的な実装方法
シンプルなニュースティッカー
HTML構造
<div class="scroll-container">
<div class="scroll-content">
<!-- オリジナルコンテンツ -->
<div class="news-item">重要なお知らせ:新製品が発売されました</div>
<div class="news-item">イベント情報:来月セミナーを開催します</div>
<div class="news-item">メンテナンス:明日午前2時〜5時</div>
<div class="news-item">キャンペーン:今なら送料無料です</div>
<!-- 同じ内容の複製 -->
<div class="news-item">重要なお知らせ:新製品が発売されました</div>
<div class="news-item">イベント情報:来月セミナーを開催します</div>
<div class="news-item">メンテナンス:明日午前2時〜5時</div>
<div class="news-item">キャンペーン:今なら送料無料です</div>
</div>
</div>
CSS実装
.scroll-container {
height: 200px; /* 表示エリアの高さ */
overflow: hidden;
border: 1px solid #ddd;
background: #f9f9f9;
}
.scroll-content {
display: flex;
flex-direction: column;
animation: scrollUp 15s linear infinite;
}
.news-item {
padding: 15px;
border-bottom: 1px solid #eee;
font-size: 14px;
white-space: nowrap;
flex-shrink: 0; /* アイテムの縮小を防ぐ */
}
@keyframes scrollUp {
0% {
transform: translateY(0);
}
100% {
transform: translateY(-50%);
}
}
より高度な実装
画像付きカードのスクロール
<div class="card-scroll-container">
<div class="card-scroll-content">
<!-- オリジナルセット -->
<div class="card">
<img src="product1.jpg" alt="商品1">
<h3>商品名1</h3>
<p>価格: ¥1,000</p>
</div>
<div class="card">
<img src="product2.jpg" alt="商品2">
<h3>商品名2</h3>
<p>価格: ¥2,000</p>
</div>
<div class="card">
<img src="product3.jpg" alt="商品3">
<h3>商品名3</h3>
<p>価格: ¥3,000</p>
</div>
<!-- 複製セット -->
<div class="card">
<img src="product1.jpg" alt="商品1">
<h3>商品名1</h3>
<p>価格: ¥1,000</p>
</div>
<div class="card">
<img src="product2.jpg" alt="商品2">
<h3>商品名2</h3>
<p>価格: ¥2,000</p>
</div>
<div class="card">
<img src="product3.jpg" alt="商品3">
<h3>商品名3</h3>
<p>価格: ¥3,000</p>
</div>
</div>
</div>
.card-scroll-container {
height: 400px;
overflow: hidden;
background: white;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.card-scroll-content {
display: flex;
flex-direction: column;
gap: 16px;
padding: 16px;
animation: cardScroll 20s linear infinite;
}
.card {
background: white;
border: 1px solid #e1e5e9;
border-radius: 8px;
padding: 16px;
flex-shrink: 0;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
.card img {
width: 100%;
height: 120px;
object-fit: cover;
border-radius: 4px;
margin-bottom: 8px;
}
.card h3 {
margin: 0 0 8px 0;
font-size: 16px;
color: #333;
}
.card p {
margin: 0;
color: #666;
font-size: 14px;
}
@keyframes cardScroll {
0% {
transform: translateY(0);
}
100% {
transform: translateY(-50%);
}
}
速度とタイミングの調整
アニメーション速度の制御
基本的な速度調整
/* 速い(5秒で一周) */
.fast-scroll {
animation: scrollUp 5s linear infinite;
}
/* 標準(15秒で一周) */
.normal-scroll {
animation: scrollUp 15s linear infinite;
}
/* 遅い(30秒で一周) */
.slow-scroll {
animation: scrollUp 30s linear infinite;
}
段階的な速度変化
@keyframes variableSpeed {
0% {
transform: translateY(0);
animation-timing-function: ease-in;
}
50% {
transform: translateY(-25%);
animation-timing-function: linear;
}
100% {
transform: translateY(-50%);
animation-timing-function: ease-out;
}
}
.variable-scroll {
animation: variableSpeed 20s infinite;
}
ホバー時の制御
マウスオーバーで一時停止
.pauseable-scroll {
animation: scrollUp 15s linear infinite;
transition: animation-play-state 0.3s ease;
}
.pauseable-scroll:hover {
animation-play-state: paused;
}
ホバー時の速度変更
.speed-control-scroll {
animation: scrollUp 15s linear infinite;
transition: animation-duration 0.5s ease;
}
.speed-control-scroll:hover {
animation-duration: 30s; /* ホバー時に半分の速度 */
}
インタラクティブな機能
方向切り替え
上下のスクロール方向切り替え
@keyframes scrollDown {
0% {
transform: translateY(-50%);
}
100% {
transform: translateY(0);
}
}
.scroll-up {
animation: scrollUp 15s linear infinite;
}
.scroll-down {
animation: scrollDown 15s linear infinite;
}
/* JavaScript での切り替え */
/*
document.querySelector('.scroll-content').addEventListener('click', function() {
this.classList.toggle('scroll-up');
this.classList.toggle('scroll-down');
});
*/
CSS変数を使った動的制御
カスタムプロパティでの速度制御
:root {
--scroll-duration: 15s;
--scroll-direction: -50%;
}
.dynamic-scroll {
animation: dynamicScrollUp var(--scroll-duration) linear infinite;
}
@keyframes dynamicScrollUp {
0% {
transform: translateY(0);
}
100% {
transform: translateY(var(--scroll-direction));
}
}
/* JavaScript での動的変更 */
/*
document.documentElement.style.setProperty('--scroll-duration', '10s');
document.documentElement.style.setProperty('--scroll-direction', '-75%');
*/
高度な実装パターン

複数列の無限スクロール
異なる速度での並列スクロール
<div class="multi-column-scroll">
<div class="column">
<div class="scroll-content slow">
<div class="item">アイテムA1</div>
<div class="item">アイテムA2</div>
<div class="item">アイテムA3</div>
<!-- 複製 -->
<div class="item">アイテムA1</div>
<div class="item">アイテムA2</div>
<div class="item">アイテムA3</div>
</div>
</div>
<div class="column">
<div class="scroll-content fast">
<div class="item">アイテムB1</div>
<div class="item">アイテムB2</div>
<div class="item">アイテムB3</div>
<!-- 複製 -->
<div class="item">アイテムB1</div>
<div class="item">アイテムB2</div>
<div class="item">アイテムB3</div>
</div>
</div>
</div>
.multi-column-scroll {
display: flex;
gap: 20px;
}
.column {
flex: 1;
height: 300px;
overflow: hidden;
border: 1px solid #ddd;
}
.scroll-content {
display: flex;
flex-direction: column;
}
.scroll-content.slow {
animation: scrollUp 25s linear infinite;
}
.scroll-content.fast {
animation: scrollUp 10s linear infinite;
}
.item {
padding: 15px;
border-bottom: 1px solid #eee;
flex-shrink: 0;
}
逆方向スクロールの組み合わせ
.opposite-scroll-container {
display: flex;
height: 200px;
}
.left-scroll {
animation: scrollUp 15s linear infinite;
}
.right-scroll {
animation: scrollDown 15s linear infinite;
}
フェードイン・アウト効果
上下端でのフェード処理
.fade-scroll-container {
height: 300px;
overflow: hidden;
position: relative;
}
.fade-scroll-container::before,
.fade-scroll-container::after {
content: '';
position: absolute;
left: 0;
right: 0;
height: 30px;
pointer-events: none;
z-index: 1;
}
.fade-scroll-container::before {
top: 0;
background: linear-gradient(to bottom, white, transparent);
}
.fade-scroll-container::after {
bottom: 0;
background: linear-gradient(to top, white, transparent);
}
パフォーマンスの最適化
GPU加速の活用
transform の最適化
.optimized-scroll {
/* GPU加速を有効にする */
transform: translateZ(0);
will-change: transform;
animation: scrollUp 15s linear infinite;
}
/* アニメーション終了後のクリーンアップ */
.optimized-scroll.animation-ended {
will-change: auto;
}
大量コンテンツでの考慮点
メモリ効率の良い実装
/* 大量のコンテンツでは短いアニメーション時間を避ける */
.memory-efficient {
animation: scrollUp 20s linear infinite;
/* 短すぎる時間はCPU負荷が高くなる */
}
/* コンテンツの最適化 */
.content-item {
/* 不要な装飾を避ける */
box-shadow: none;
border-radius: 0;
/* シンプルなスタイルでパフォーマンス向上 */
}
ブラウザ対応
古いブラウザでの fallback
/* モダンブラウザ用 */
.modern-scroll {
animation: scrollUp 15s linear infinite;
}
/* フィーチャークエリでの分岐 */
@supports not (animation: scrollUp 15s linear infinite) {
.modern-scroll {
/* fallback: 静的表示 */
animation: none;
position: relative;
}
}
よくある問題と解決策
継ぎ目が見える問題
原因と対策
問題
- コンテンツの複製が不完全
- アニメーションの移動距離が不正確
解決方法
/* 正確に50%移動させる */
@keyframes precisescroll {
0% { transform: translateY(0); }
100% { transform: translateY(-50%); }
}
/* コンテンツを完全に複製 */
.scroll-content {
/* オリジナル + 完全な複製の構造 */
}
アニメーションのカクつき
スムーズな動作の実現
.smooth-scroll {
animation: scrollUp 15s linear infinite;
/* 重要:linearで一定速度を保つ */
transform: translateZ(0); /* GPU加速 */
backface-visibility: hidden; /* ちらつき防止 */
}
レスポンシブ対応
画面サイズに応じた調整
.responsive-scroll {
animation: scrollUp 15s linear infinite;
}
@media (max-width: 768px) {
.responsive-scroll {
animation-duration: 20s; /* スマホでは少し遅く */
}
.scroll-container {
height: 150px; /* 高さも調整 */
}
}
@media (max-width: 480px) {
.responsive-scroll {
animation-duration: 25s;
}
}
アクセシビリティの配慮
モーション軽減への対応
prefers-reduced-motion の活用
.accessible-scroll {
animation: scrollUp 15s linear infinite;
}
/* アニメーションを減らしたいユーザーへの配慮 */
@media (prefers-reduced-motion: reduce) {
.accessible-scroll {
animation: none;
/* 静的表示または非常にゆっくりとした動き */
}
/* 代替として、非常にゆっくりした動きも可能 */
.accessible-scroll.slow-motion {
animation: scrollUp 60s linear infinite;
}
}
キーボードナビゲーション
フォーカス管理
.scroll-content .item:focus {
outline: 2px solid #007acc;
outline-offset: 2px;
animation-play-state: paused; /* フォーカス時は停止 */
}
実際のプロジェクトでの活用例

ニュースサイトのティッカー
.news-ticker {
background: #1a1a1a;
color: #fff;
height: 40px;
overflow: hidden;
position: relative;
}
.news-ticker::before {
content: 'Breaking News';
position: absolute;
left: 0;
top: 0;
bottom: 0;
background: #e74c3c;
color: white;
padding: 10px 15px;
font-weight: bold;
z-index: 1;
}
.news-content {
display: flex;
flex-direction: column;
margin-left: 140px;
animation: scrollUp 25s linear infinite;
}
.news-item {
height: 40px;
line-height: 40px;
padding: 0 15px;
white-space: nowrap;
flex-shrink: 0;
}
ECサイトの商品一覧
.product-showcase {
height: 500px;
overflow: hidden;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 12px;
padding: 20px;
}
.product-list {
display: flex;
flex-direction: column;
gap: 20px;
animation: scrollUp 30s linear infinite;
}
.product-card {
background: white;
border-radius: 8px;
padding: 16px;
display: flex;
align-items: center;
gap: 16px;
flex-shrink: 0;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
株価情報の表示
.stock-ticker {
background: #000;
color: #00ff00;
font-family: 'Courier New', monospace;
height: 60px;
overflow: hidden;
border: 1px solid #333;
}
.stock-content {
display: flex;
flex-direction: column;
animation: scrollUp 20s linear infinite;
}
.stock-item {
height: 60px;
display: flex;
align-items: center;
padding: 0 20px;
border-bottom: 1px solid #333;
flex-shrink: 0;
}
.stock-symbol {
font-weight: bold;
margin-right: 20px;
}
.stock-price {
margin-right: 10px;
}
.stock-change {
font-size: 12px;
}
.positive { color: #00ff00; }
.negative { color: #ff0000; }
まとめ
CSSだけで作る無限スクロールは、JavaScriptを使わずに魅力的な動的コンテンツを実現できる便利な技術です。
重要なポイントの再確認
基本実装
- コンテンツを2セット用意(オリジナル + 複製)
translateY(-50%)
で正確に半分移動linear
タイミング関数で一定速度を維持
パフォーマンス最適化
- GPU加速の活用(
transform
、will-change
) - 適切なアニメーション時間の設定
- 不要な装飾の削減
アクセシビリティ配慮
prefers-reduced-motion
への対応- フォーカス管理とキーボードナビゲーション
- 適切な速度設定
成功のためのポイント
設計時の考慮事項
- コンテンツの構造:完全な複製による継ぎ目のない循環
- 速度の調整:ユーザビリティを損なわない適切な速度
- レスポンシブ対応:デバイスに応じた最適化
- パフォーマンス:滑らかな動作の維持
実装時のベストプラクティス
- シンプルな構造:複雑すぎないHTML構造
- CSS変数の活用:動的な制御の実現
- フォールバック:古いブラウザや設定への配慮
- テスト:様々な環境での動作確認
コメント