CSSだけで作る縦の無限スクロール|アニメーションで自動スクロールを実現する方法

CSS

Webサイトを見ていると、テキストや画像がずっと縦に流れ続ける「無限スクロール」の演出を見かけることがあります。

これをJavaScriptを使わずに、CSSだけで簡単に作れたら便利ですよね。

この記事では、以下のことを初心者にもわかるように解説します:

  • CSSアニメーションで縦方向に無限スクロールする方法
  • 実際のコード例と活用パターン
  • よくある問題点と解決策
  • パフォーマンスを考慮した実装のコツ

ニュースティッカーやお知らせ欄、商品リストなど、様々な場面で活用できるテクニックです!

スポンサーリンク

無限スクロールの基本仕組み

CSS無限スクロールの原理

CSSだけで縦に無限スクロールさせるには、以下の仕組みを使います:

基本的なアプローチ

  1. コンテンツの複製:同じ内容を2セット用意
  2. アニメーション@keyframesでスクロール動作を定義
  3. ループ処理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加速の活用(transformwill-change
  • 適切なアニメーション時間の設定
  • 不要な装飾の削減

アクセシビリティ配慮

  • prefers-reduced-motionへの対応
  • フォーカス管理とキーボードナビゲーション
  • 適切な速度設定

成功のためのポイント

設計時の考慮事項

  1. コンテンツの構造:完全な複製による継ぎ目のない循環
  2. 速度の調整:ユーザビリティを損なわない適切な速度
  3. レスポンシブ対応:デバイスに応じた最適化
  4. パフォーマンス:滑らかな動作の維持

実装時のベストプラクティス

  1. シンプルな構造:複雑すぎないHTML構造
  2. CSS変数の活用:動的な制御の実現
  3. フォールバック:古いブラウザや設定への配慮
  4. テスト:様々な環境での動作確認

コメント

タイトルとURLをコピーしました