Shopifyで簡単実装!商品カルーセルの無限ループを作るHTML・CSS・JS・liquidコード完全ガイド

Shopifyで商品の魅力を最大限に引き出すためには、カルーセル(スライダー)が非常に効果的なツールとなります。多くの商品をコンパクトかつ視覚的に表示できるため、ユーザーの購買意欲を高め、結果として売上向上に大きく寄与します。本記事では、無限ループする商品カルーセルを簡単に実装するためのHTML・CSS・JavaScriptのコードを詳しく紹介するとともに、導入時の注意点、具体的な使い方、そしてさらなる効果を生み出すカスタマイズ方法を解説します。

目次

商品カルーセルとは?

商品カルーセルは、複数の商品画像を横方向にスライド表示できるUIコンポーネントのことです。商品ページやトップページなどに設置されることが多く、ユーザーにとって商品を直感的に閲覧しやすくします。また、限られたスペースで多くの商品をアピールできるため、特にECサイトにおいて効果的なデザイン手法です。

導入方法の手順

Shopifyのページ作成から作る方法

今回紹介するコードは、HTML・CSS・JavaScriptの3つの要素で構成されています。以下の手順で簡単にShopifyページに導入できます

Shopifyの管理画面から「オンラインストア」→「ページ」→「ページを追加」をクリックし、テーマの「コードを編集」を開き、HTMLを見るボタンを押す

コードエディタに下記コードを貼り付けて保存する。
※画像やリンク先、商品名などは変更してご使用ください。

ページを確認。スライドショーの挙動など確認してください。お使いの環境によってはうまく動作品場合がございます。

ページ作成の時の実際のサンプルコード

下記コードを張り付けてご使用ください。
下記箇所を変更してご利用ください。
①タイトル
  <h2 class=”my-carousel-heading”>    サンプル商品が並ぶカルーセル</h2>
②商品画像・商品名
  <img src=”https://via.placeholder.com/300?text=Product+1″ alt=”サンプル商品1″>
③商品リンク
<a href=”#” class=”my-carousel-button”>詳細を見る</a>(#をリンク先に設定してください。)
④商品説明
<p class=”my-carousel-spec”>1個~注文OK<br></p>
⑤詳細ボタンの色
.my-carousel-button {background-color: #ff5722;}
⑥一覧ボタンの色
 .view-all-button {background-color: #ff5722;}

<!DOCTYPE html> 
<html lang="ja">
<head>
  <meta charset="UTF-8" />
  <title>サンプル:無限ループのカルーセル</title>
  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <style>
    /****************************************
     * (1) 全体スタイル
     ****************************************/
    body {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
      font-family: sans-serif;
      background-color: #f5f5f5;
    }

    /* カルーセルをまとめるセクション */
    .my-carousel-section {
      position: relative;
      width: 100vw; /* 幅をブラウザに合わせる */
      left: 50%;
      transform: translateX(-50%);
      background-color: #e0e0e0;
      padding: 60px 0;
      box-sizing: border-box;
    }
    .my-carousel-wrapper {
      max-width: 1200px;
      margin: 0 auto;
      padding: 0 20px;
      box-sizing: border-box;
    }
    .my-carousel-heading {
      max-width: 700px;
      margin: 0 auto;
      font-weight: bold;
      text-align: center;
      font-size: 24px;
      padding: 20px;
      position: relative;
      border-radius: 5px;
      color: #515151;
      margin-bottom: 30px;
    }

    /****************************************
     * (2) カルーセル部分
     ****************************************/
    .carousel-container {
      position: relative;
      margin: 0 auto;
      overflow: hidden; /* スクロール領域外を隠す */
    }

    /* スムーズスクロール。ただし最初とジャンプ時だけOFFにするためのクラスと併用 */
    .my-carousel-grid {
      display: flex;
      flex-wrap: nowrap;
      gap: 30px;
      overflow-x: auto;
      -webkit-overflow-scrolling: touch;
      user-select: none;
      cursor: grab;
      scrollbar-width: none; /* Firefoxでスクロールバー非表示 */
      -ms-overflow-style: none; /* IE/Edgeで非表示 */
      scroll-behavior: smooth; /* スムーズスクロール */
      box-sizing: border-box;
      padding-bottom: 10px;
    }
    .my-carousel-grid::-webkit-scrollbar {
      display: none; /* Chrome, Safari 等のスクロールバー非表示 */
    }
    .my-carousel-grid:active {
      cursor: grabbing;
    }

    /* ジャンプ時だけスムーズスクロールをOFFにするためのクラス */
    .no-scroll-transition {
      scroll-behavior: auto !important;
    }

    /****************************************
     * (3) カードデザイン
     ****************************************/
    .my-carousel-item {
      background-color: #fff;
      border-radius: 8px;
      box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
      overflow: hidden;
      transition: transform 0.3s ease, box-shadow 0.3s ease;
      color: #333;
      display: flex;
      flex-direction: column;
      align-items: center;
      flex: 0 0 280px; /* 横幅(お好みで調整) */
      margin-bottom: 0;
    }
    .my-carousel-item:hover {
      transform: translateY(-3px);
      box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15);
    }
    .my-carousel-content {
      display: flex;
      flex-direction: column;
      align-items: center;
      gap: 10px;
      padding: 20px;
    }
    .my-carousel-image {
      width: 250px;
      height: 250px;
      border-radius: 4px;
      overflow: hidden;
      flex-shrink: 0;
    }
    .my-carousel-image img {
      width: 100%;
      height: 100%;
      object-fit: cover;
      display: block;
    }
    .my-carousel-info {
      width: 100%;
      text-align: center;
    }
    .my-carousel-title {
      font-size: 18px;
      font-weight: bold;
      margin-bottom: 6px;
      color: #333;
    }
    .my-carousel-spec {
      font-size: 14px;
      color: #666;
      margin-bottom: 6px;
      line-height: 1.4;
      font-weight: bold;
    }
    .my-carousel-price {
      font-size: 22px;
      font-weight: bold;
      color: #ff5722;
    }
    .my-carousel-price .tax-included {
      font-size: 0.6em;
      vertical-align: baseline;
    }
    .my-carousel-button {
      display: inline-block;
      margin-top: 10px;
      font-size: 14px;
      font-weight: bold;
      color: #fff !important;
      background-color: #ff5722;
      padding: 8px 16px;
      border-radius: 4px;
      text-decoration: none;
      border: none;
      cursor: pointer;
      transition: opacity 0.2s ease;
    }
    .my-carousel-button:hover {
      opacity: 0.9;
    }

    /****************************************
     * (4) 左右矢印ボタン
     ****************************************/
    .carousel-arrow {
      position: absolute;
      top: 50%;
      z-index: 5;
      transform: translateY(-50%);
      background: rgba(0, 0, 0, 0.5);
      color: #fff;
      border: none;
      width: 40px;
      height: 40px;
      cursor: pointer;
      border-radius: 50%;
      font-size: 20px;
    }
    .carousel-arrow.prev {
      left: 10px;
    }
    .carousel-arrow.next {
      right: 10px;
    }

    /****************************************
     * (5) 下部ボタン部分 (任意)
     ****************************************/
    .my-carousel-footer {
      margin-top: 40px;
      text-align: center;
      line-height: 1.8;
      font-size: 16px;
      max-width: 800px;
      margin-left: auto;
      margin-right: auto;
    }
    .view-all-button {
      display: inline-block;
      background-color: #ff5722;
      color: #fff !important;
      font-size: 18px;
      font-weight: bold;
      padding: 12px 40px;
      border-radius: 6px;
      text-decoration: none;
      margin-top: 20px;
      transition: transform 0.1s ease-in-out, background-color 0.3s;
      box-shadow: 0 4px 6px rgba(0,0,0,0.2);
    }
    .view-all-button:hover {
      background-color: #e64a19;
    }
    .view-all-button:active {
      transform: scale(0.9);
      box-shadow: inset 0 4px 6px rgba(0,0,0,0.2);
    }

  </style>
</head>
<body>

  <!-- カルーセルセクション -->
  <section class="my-carousel-section">
    <div class="my-carousel-wrapper">
      <h2 class="my-carousel-heading">
        サンプル商品が並ぶカルーセル!
      </h2>

      <!-- カルーセル全体のラッパ -->
      <div class="carousel-container">

        <!-- 左ボタン -->
        <button class="carousel-arrow prev">←</button>

        <!-- スライド領域 -->
        <!-- 初期ロード時は「no-scroll-transition」をつけてガタつきを防止 -->
        <div class="my-carousel-grid no-scroll-transition" id="carousel">
          <!-- 以下、サンプル商品カードを複数 (例では5個) 並べています -->
          <div class="my-carousel-item">
            <div class="my-carousel-content">
              <div class="my-carousel-image">
                <img src="https://via.placeholder.com/300?text=Product+1" alt="サンプル商品1">
              </div>
              <div class="my-carousel-info">
                <h3 class="my-carousel-title">サンプル商品1</h3>
                <p class="my-carousel-spec">
                  1個~注文OK<br>
                  フルカラー印刷
                </p>
                <p class="my-carousel-price">
                  <span class="tax-included">@</span>500円
                  <span class="tax-included">(税込)</span>
                </p>
                <a href="#" class="my-carousel-button">詳細を見る</a>
              </div>
            </div>
          </div>

          <div class="my-carousel-item">
            <div class="my-carousel-content">
              <div class="my-carousel-image">
                <img src="https://via.placeholder.com/300?text=Product+2" alt="サンプル商品2">
              </div>
              <div class="my-carousel-info">
                <h3 class="my-carousel-title">サンプル商品2</h3>
                <p class="my-carousel-spec">
                  1個~注文OK<br>
                  フルカラー印刷
                </p>
                <p class="my-carousel-price">
                  <span class="tax-included">@</span>700円
                  <span class="tax-included">(税込)</span>
                </p>
                <a href="#" class="my-carousel-button">詳細を見る</a>
              </div>
            </div>
          </div>

          <div class="my-carousel-item">
            <div class="my-carousel-content">
              <div class="my-carousel-image">
                <img src="https://via.placeholder.com/300?text=Product+3" alt="サンプル商品3">
              </div>
              <div class="my-carousel-info">
                <h3 class="my-carousel-title">サンプル商品3</h3>
                <p class="my-carousel-spec">
                  1個~注文OK<br>
                  フルカラー印刷
                </p>
                <p class="my-carousel-price">
                  <span class="tax-included">@</span>900円
                  <span class="tax-included">(税込)</span>
                </p>
                <a href="#" class="my-carousel-button">詳細を見る</a>
              </div>
            </div>
          </div>

          <div class="my-carousel-item">
            <div class="my-carousel-content">
              <div class="my-carousel-image">
                <img src="https://via.placeholder.com/300?text=Product+4" alt="サンプル商品4">
              </div>
              <div class="my-carousel-info">
                <h3 class="my-carousel-title">サンプル商品4</h3>
                <p class="my-carousel-spec">
                  1個~注文OK<br>
                  フルカラー印刷
                </p>
                <p class="my-carousel-price">
                  <span class="tax-included">@</span>1200円
                  <span class="tax-included">(税込)</span>
                </p>
                <a href="#" class="my-carousel-button">詳細を見る</a>
              </div>
            </div>
          </div>

          <div class="my-carousel-item">
            <div class="my-carousel-content">
              <div class="my-carousel-image">
                <img src="https://via.placeholder.com/300?text=Product+5" alt="サンプル商品5">
              </div>
              <div class="my-carousel-info">
                <h3 class="my-carousel-title">サンプル商品5</h3>
                <p class="my-carousel-spec">
                  1個~注文OK<br>
                  フルカラー印刷
                </p>
                <p class="my-carousel-price">
                  <span class="tax-included">@</span>1500円
                  <span class="tax-included">(税込)</span>
                </p>
                <a href="#" class="my-carousel-button">詳細を見る</a>
              </div>
            </div>
          </div>
        </div>
        <!-- /#carousel -->

        <!-- 右ボタン -->
        <button class="carousel-arrow next">→</button>
      </div>
      <!-- /.carousel-container -->

      <!-- もし全商品の一覧ページなどに飛ばしたい場合 -->
      <div class="my-carousel-footer">
        <a href="#" class="view-all-button">一覧を見る</a>
      </div>
    </div>
  </section>

  <script>
    document.addEventListener('DOMContentLoaded', function() {
      const carousel = document.getElementById('carousel');
      if (!carousel) return;

      // ▼ (A) 最初だけスムーズスクロールOFFにして初期位置調整時のガタつきを防ぐ
      carousel.classList.add('no-scroll-transition');

      // ▼ (1) 元々のアイテムを取得し、クローンで合計3セット分に増やす
      const originalItems = Array.from(carousel.children);
      const itemCount = originalItems.length;

      for (let i = 0; i < 2; i++) {
        originalItems.forEach(item => {
          const clone = item.cloneNode(true);
          carousel.appendChild(clone);
        });
      }

      // ▼ (2) 1カードあたりの幅(カード+gap)を算出
      const itemWidth = originalItems[0].getBoundingClientRect().width + 30;

      // ▼ (3) 初期位置を「真ん中のセット」になるように調整
      const startPos = itemCount * itemWidth;
      carousel.scrollLeft = startPos;

      // ▼ (4) 1フレーム後にスムーズスクロールを元に戻す
      requestAnimationFrame(() => {
        carousel.classList.remove('no-scroll-transition');
      });

      // ▼ (5) 無限ループ処理(端に来たら反対側へ飛ばす)
      function onScroll() {
        const currentScroll = carousel.scrollLeft;
        const maxScrollLeft = itemWidth * (itemCount * 2); // 2周分

        // 左端近くまで行ったら右端付近へジャンプ
        if (currentScroll < itemWidth) {
          carousel.classList.add('no-scroll-transition');
          carousel.scrollLeft = currentScroll + itemWidth * itemCount;
          carousel.classList.remove('no-scroll-transition');
        }
        // 右端近くまで行ったら左端付近へジャンプ
        else if (currentScroll > maxScrollLeft) {
          carousel.classList.add('no-scroll-transition');
          carousel.scrollLeft = currentScroll - itemWidth * itemCount;
          carousel.classList.remove('no-scroll-transition');
        }
      }
      carousel.addEventListener('scroll', onScroll);

      // ▼ (6) ドラッグ操作(PCマウス用)
      let isDown = false;
      let startX = 0;
      let scrollLeftVal = 0;

      carousel.addEventListener('mousedown', e => {
        isDown = true;
        startX = e.pageX - carousel.offsetLeft;
        scrollLeftVal = carousel.scrollLeft;
        e.preventDefault();
        carousel.style.cursor = 'grabbing';
      });
      carousel.addEventListener('mouseleave', () => {
        isDown = false;
        carousel.style.cursor = 'grab';
      });
      carousel.addEventListener('mouseup', () => {
        isDown = false;
        carousel.style.cursor = 'grab';
      });
      carousel.addEventListener('mousemove', e => {
        if (!isDown) return;
        e.preventDefault();
        const x = e.pageX - carousel.offsetLeft;
        const walk = (x - startX);
        // ドラッグ時の移動量を0.3倍で調整してスムーズに
        carousel.scrollLeft = scrollLeftVal - walk * 0.3;
      });

      // ▼ (7) 左右ボタンスクロール
      const prevBtn = document.querySelector('.carousel-arrow.prev');
      const nextBtn = document.querySelector('.carousel-arrow.next');
      const scrollAmount = 280; // 一回の移動量(お好みで)

      prevBtn.addEventListener('click', () => {
        carousel.scrollBy({ left: -scrollAmount });
      });
      nextBtn.addEventListener('click', () => {
        carousel.scrollBy({ left: scrollAmount });
      });
    });
  </script>

</body>
</html>

Shopifyのテーマエディタから作る方法

Shopifyの管理画面から「オンラインストア」→「テーマ」→「・・・」をクリックし、テーマの「コード編集」を開く
※コード編集は自己責任でお願いいたします。

以下のコードを、そのまま「sections フォルダ」にある .liquid ファイル(例:infinite-carousel.liquid)として保存してください。ShopifyのOnline Store 2.0対応テーマの場合は、このセクションをテーマエディタから追加できます。

下記コードを張り付けてご使用ください。
下記箇所を変更してご利用ください。
①タイトル
  <h2 class=”my-carousel-heading”>    サンプル商品が並ぶカルーセル</h2>
②商品画像・商品名
  <img src=”https://via.placeholder.com/300?text=Product+1″ alt=”サンプル商品1″>
③商品リンク
<a href=”#” class=”my-carousel-button”>詳細を見る</a>(#をリンク先に設定してください。)
④商品説明
<p class=”my-carousel-spec”>1個~注文OK<br></p>
⑤詳細ボタンの色
.my-carousel-button {background-color: #ff5722;}
⑥一覧ボタンの色
 .view-all-button {background-color: #ff5722;}

{% comment %}
  ==============================
  Infinite Carousel Section
  ==============================
{% endcomment %}

<section class="my-carousel-section">
  <style>
    /****************************************
     * (1) 全体スタイル
     ****************************************/
    .my-carousel-section {
      position: relative;
      width: 100vw; /* 幅をブラウザに合わせる */
      left: 50%;
      transform: translateX(-50%);
      background-color: #e0e0e0;
      padding: 60px 0;
      box-sizing: border-box;
    }
    .my-carousel-wrapper {
      max-width: 1200px;
      margin: 0 auto;
      padding: 0 20px;
      box-sizing: border-box;
    }
    .my-carousel-heading {
      max-width: 700px;
      margin: 0 auto;
      font-weight: bold;
      text-align: center;
      font-size: 24px;
      padding: 20px;
      position: relative;
      border-radius: 5px;
      color: #515151;
      margin-bottom: 30px;
    }

    /****************************************
     * (2) カルーセル部分
     ****************************************/
    .carousel-container {
      position: relative;
      margin: 0 auto;
      overflow: hidden; /* スクロール領域外を隠す */
    }

    /* スムーズスクロール。ただし最初とジャンプ時だけOFFにするためのクラスと併用 */
    .my-carousel-grid {
      display: flex;
      flex-wrap: nowrap;
      gap: 30px;
      overflow-x: auto;
      -webkit-overflow-scrolling: touch;
      user-select: none;
      cursor: grab;
      scrollbar-width: none; /* Firefoxでスクロールバー非表示 */
      -ms-overflow-style: none; /* IE/Edgeで非表示 */
      scroll-behavior: smooth; /* スムーズスクロール */
      box-sizing: border-box;
      padding-bottom: 10px;
    }
    .my-carousel-grid::-webkit-scrollbar {
      display: none; /* Chrome, Safari 等のスクロールバー非表示 */
    }
    .my-carousel-grid:active {
      cursor: grabbing;
    }
    /* ジャンプ時だけスムーズスクロールをOFFにするためのクラス */
    .no-scroll-transition {
      scroll-behavior: auto !important;
    }

    /****************************************
     * (3) カードデザイン
     ****************************************/
    .my-carousel-item {
      background-color: #fff;
      border-radius: 8px;
      box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
      overflow: hidden;
      transition: transform 0.3s ease, box-shadow 0.3s ease;
      color: #333;
      display: flex;
      flex-direction: column;
      align-items: center;
      flex: 0 0 280px; /* 横幅(お好みで調整) */
      margin-bottom: 0;
    }
    .my-carousel-item:hover {
      transform: translateY(-3px);
      box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15);
    }
    .my-carousel-content {
      display: flex;
      flex-direction: column;
      align-items: center;
      gap: 10px;
      padding: 20px;
    }
    .my-carousel-image {
      width: 250px;
      height: 250px;
      border-radius: 4px;
      overflow: hidden;
      flex-shrink: 0;
    }
    .my-carousel-image img {
      width: 100%;
      height: 100%;
      object-fit: cover;
      display: block;
    }
    .my-carousel-info {
      width: 100%;
      text-align: center;
    }
    .my-carousel-title {
      font-size: 18px;
      font-weight: bold;
      margin-bottom: 6px;
      color: #333;
    }
    .my-carousel-spec {
      font-size: 14px;
      color: #666;
      margin-bottom: 6px;
      line-height: 1.4;
      font-weight: bold;
    }
    .my-carousel-price {
      font-size: 22px;
      font-weight: bold;
      color: #ff5722;
    }
    .my-carousel-price .tax-included {
      font-size: 0.6em;
      vertical-align: baseline;
    }
    .my-carousel-button {
      display: inline-block;
      margin-top: 10px;
      font-size: 14px;
      font-weight: bold;
      color: #fff !important;
      background-color: #ff5722;
      padding: 8px 16px;
      border-radius: 4px;
      text-decoration: none;
      border: none;
      cursor: pointer;
      transition: opacity 0.2s ease;
    }
    .my-carousel-button:hover {
      opacity: 0.9;
    }

    /****************************************
     * (4) 左右矢印ボタン
     ****************************************/
    .carousel-arrow {
      position: absolute;
      top: 50%;
      z-index: 5;
      transform: translateY(-50%);
      background: rgba(0, 0, 0, 0.5);
      color: #fff;
      border: none;
      width: 40px;
      height: 40px;
      cursor: pointer;
      border-radius: 50%;
      font-size: 20px;
    }
    .carousel-arrow.prev {
      left: 10px;
    }
    .carousel-arrow.next {
      right: 10px;
    }

    /****************************************
     * (5) 下部ボタン部分
     ****************************************/
    .my-carousel-footer {
      margin-top: 40px;
      text-align: center;
      line-height: 1.8;
      font-size: 16px;
      max-width: 800px;
      margin-left: auto;
      margin-right: auto;
    }
    .view-all-button {
      display: inline-block;
      background-color: #ff5722;
      color: #fff !important;
      font-size: 18px;
      font-weight: bold;
      padding: 12px 40px;
      border-radius: 6px;
      text-decoration: none;
      margin-top: 20px;
      transition: transform 0.1s ease-in-out, background-color 0.3s;
      box-shadow: 0 4px 6px rgba(0,0,0,0.2);
    }
    .view-all-button:hover {
      background-color: #e64a19;
    }
    .view-all-button:active {
      transform: scale(0.9);
      box-shadow: inset 0 4px 6px rgba(0,0,0,0.2);
    }
  </style>

  <div class="my-carousel-wrapper">
    <h2 class="my-carousel-heading">
      サンプル商品が並ぶカルーセル!
    </h2>

    <!-- カルーセル全体のラッパ -->
    <div class="carousel-container">
      <!-- 左ボタン -->
      <button class="carousel-arrow prev">←</button>

      <!-- スライド領域 -->
      <!-- 初期ロード時は「no-scroll-transition」を付与してガタつき防止 -->
      <div class="my-carousel-grid no-scroll-transition" id="carousel">
        <!-- 以下、サンプル商品カードを複数(5個) -->
        <div class="my-carousel-item">
          <div class="my-carousel-content">
            <div class="my-carousel-image">
              <img src="https://via.placeholder.com/300?text=Product+1" alt="サンプル商品1">
            </div>
            <div class="my-carousel-info">
              <h3 class="my-carousel-title">サンプル商品1</h3>
              <p class="my-carousel-spec">
                1個~注文OK<br>
                フルカラー印刷
              </p>
              <p class="my-carousel-price">
                <span class="tax-included">@</span>500円
                <span class="tax-included">(税込)</span>
              </p>
              <a href="#" class="my-carousel-button">詳細を見る</a>
            </div>
          </div>
        </div>

        <div class="my-carousel-item">
          <div class="my-carousel-content">
            <div class="my-carousel-image">
              <img src="https://via.placeholder.com/300?text=Product+2" alt="サンプル商品2">
            </div>
            <div class="my-carousel-info">
              <h3 class="my-carousel-title">サンプル商品2</h3>
              <p class="my-carousel-spec">
                1個~注文OK<br>
                フルカラー印刷
              </p>
              <p class="my-carousel-price">
                <span class="tax-included">@</span>700円
                <span class="tax-included">(税込)</span>
              </p>
              <a href="#" class="my-carousel-button">詳細を見る</a>
            </div>
          </div>
        </div>

        <div class="my-carousel-item">
          <div class="my-carousel-content">
            <div class="my-carousel-image">
              <img src="https://via.placeholder.com/300?text=Product+3" alt="サンプル商品3">
            </div>
            <div class="my-carousel-info">
              <h3 class="my-carousel-title">サンプル商品3</h3>
              <p class="my-carousel-spec">
                1個~注文OK<br>
                フルカラー印刷
              </p>
              <p class="my-carousel-price">
                <span class="tax-included">@</span>900円
                <span class="tax-included">(税込)</span>
              </p>
              <a href="#" class="my-carousel-button">詳細を見る</a>
            </div>
          </div>
        </div>

        <div class="my-carousel-item">
          <div class="my-carousel-content">
            <div class="my-carousel-image">
              <img src="https://via.placeholder.com/300?text=Product+4" alt="サンプル商品4">
            </div>
            <div class="my-carousel-info">
              <h3 class="my-carousel-title">サンプル商品4</h3>
              <p class="my-carousel-spec">
                1個~注文OK<br>
                フルカラー印刷
              </p>
              <p class="my-carousel-price">
                <span class="tax-included">@</span>1200円
                <span class="tax-included">(税込)</span>
              </p>
              <a href="#" class="my-carousel-button">詳細を見る</a>
            </div>
          </div>
        </div>

        <div class="my-carousel-item">
          <div class="my-carousel-content">
            <div class="my-carousel-image">
              <img src="https://via.placeholder.com/300?text=Product+5" alt="サンプル商品5">
            </div>
            <div class="my-carousel-info">
              <h3 class="my-carousel-title">サンプル商品5</h3>
              <p class="my-carousel-spec">
                1個~注文OK<br>
                フルカラー印刷
              </p>
              <p class="my-carousel-price">
                <span class="tax-included">@</span>1500円
                <span class="tax-included">(税込)</span>
              </p>
              <a href="#" class="my-carousel-button">詳細を見る</a>
            </div>
          </div>
        </div>
      </div>
      <!-- /#carousel -->

      <!-- 右ボタン -->
      <button class="carousel-arrow next">→</button>
    </div>
    <!-- /.carousel-container -->

    <!-- 下部ボタン例 -->
    <div class="my-carousel-footer">
      <a href="#" class="view-all-button">一覧を見る</a>
    </div>
  </div>

  <script>
    document.addEventListener('DOMContentLoaded', function() {
      const carousel = document.getElementById('carousel');
      if (!carousel) return;

      // ▼ (A) 初期アニメーションOFF: ガタつき防止
      carousel.classList.add('no-scroll-transition');

      // ▼ (1) 元々のアイテムを取得し、クローンで合計3セット分に増やす
      const originalItems = Array.from(carousel.children);
      const itemCount = originalItems.length;

      for (let i = 0; i < 2; i++) {
        originalItems.forEach(item => {
          const clone = item.cloneNode(true);
          carousel.appendChild(clone);
        });
      }

      // ▼ (2) 1カードあたりの幅(カード+gap)
      const itemWidth = originalItems[0].getBoundingClientRect().width + 30;

      // ▼ (3) 初期位置 = 真ん中のセット
      const startPos = itemCount * itemWidth;
      carousel.scrollLeft = startPos;

      // ▼ (4) 1フレーム後にスムーズスクロールONに戻す
      requestAnimationFrame(() => {
        carousel.classList.remove('no-scroll-transition');
      });

      // ▼ (5) 無限ループ処理 (端で反対側へジャンプ)
      function onScroll() {
        const currentScroll = carousel.scrollLeft;
        const maxScrollLeft = itemWidth * (itemCount * 2); // 2周分

        // 左端近く → 右端付近へ
        if (currentScroll < itemWidth) {
          carousel.classList.add('no-scroll-transition');
          carousel.scrollLeft = currentScroll + itemWidth * itemCount;
          carousel.classList.remove('no-scroll-transition');
        }
        // 右端近く → 左端付近へ
        else if (currentScroll > maxScrollLeft) {
          carousel.classList.add('no-scroll-transition');
          carousel.scrollLeft = currentScroll - itemWidth * itemCount;
          carousel.classList.remove('no-scroll-transition');
        }
      }
      carousel.addEventListener('scroll', onScroll);

      // ▼ (6) ドラッグ操作(PCマウス用)
      let isDown = false;
      let startX = 0;
      let scrollLeftVal = 0;

      carousel.addEventListener('mousedown', e => {
        isDown = true;
        startX = e.pageX - carousel.offsetLeft;
        scrollLeftVal = carousel.scrollLeft;
        e.preventDefault();
        carousel.style.cursor = 'grabbing';
      });
      carousel.addEventListener('mouseleave', () => {
        isDown = false;
        carousel.style.cursor = 'grab';
      });
      carousel.addEventListener('mouseup', () => {
        isDown = false;
        carousel.style.cursor = 'grab';
      });
      carousel.addEventListener('mousemove', e => {
        if (!isDown) return;
        e.preventDefault();
        const x = e.pageX - carousel.offsetLeft;
        const walk = (x - startX);
        // ドラッグ移動量を0.3倍に
        carousel.scrollLeft = scrollLeftVal - walk * 0.3;
      });

      // ▼ (7) 左右ボタンスクロール
      const prevBtn = document.querySelector('.carousel-arrow.prev');
      const nextBtn = document.querySelector('.carousel-arrow.next');
      const scrollAmount = 280; // 一回の移動量(お好みで)

      prevBtn.addEventListener('click', () => {
        carousel.scrollBy({ left: -scrollAmount });
      });
      nextBtn.addEventListener('click', () => {
        carousel.scrollBy({ left: scrollAmount });
      });
    });
  </script>
</section>

{% schema %}
{
  "name": "Infinite Carousel",
  "settings": [],
  "blocks": [],
  "presets": [
    {
      "name": "Infinite Carousel"
    }
  ]
}
{% endschema %}

テーマエディタに移動して、セクションを追加を押すと先ほど作成したinfinite-carouse
が表示されます。

コードを導入する際の重要な注意点

1. 商品画像サイズを統一する

商品画像を表示する際には、画像サイズを統一することが大切です。推奨サイズは300×300pxですが、必要に応じて適切なサイズを設定しましょう。サイズが異なる画像が混在すると、カルーセルのデザインが崩れる可能性があります。

2. レスポンシブデザインへの対応

本コードはレスポンシブ設計となっていますが、使用する画像数やカルーセルの幅によって細かな調整が必要な場合があります。モバイル端末でもきれいに表示されるよう、必ずスマートフォンやタブレットでも動作確認を行ってください。

3. JavaScript動作の確認とトラブルシューティング

ShopifyのテーマによってはJavaScriptが他のコードと競合し、カルーセルがうまく動作しないことがあります。その場合はブラウザの開発者ツール(コンソール)でエラーがないか確認し、問題を特定しましょう。また、他のアプリやカスタムコードとの衝突も確認が必要です。

追加のカスタマイズ方法

矢印ボタンのデザインや位置調整

左右に表示される矢印ボタンの位置やサイズ、色などはCSSの「.carousel-arrow」クラスを編集することで自由に変更可能です。ブランドやサイトのイメージに合ったデザインに調整しましょう。

カルーセルのスクロール速度・移動量調整

JavaScriptコードの「scrollAmount」という変数を編集すると、スクロール時の移動量を調整できます。商品カードのサイズや個数に合わせてユーザーが快適に操作できるよう調整してください。

商品カードのデザインカスタマイズ

商品カードの背景色、テキストフォント、余白など細かなデザイン変更はCSSの「.my-carousel-item」「.my-carousel-title」「.my-carousel-price」などを調整することで簡単に実現できます。独自性の高いデザインに仕上げ、ブランドの個性を演出しましょう。

まとめ

Shopifyで商品カルーセルを効果的に活用することで、ユーザーエクスペリエンスの向上と売上増加が期待できます。本記事で解説した導入手順や注意点、カスタマイズ方法を参考に、自社サイトに最適なカルーセルを構築してみてください。導入後は定期的にユーザーの反応を分析し、さらなる改善を続けましょう。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

コメント

コメントする

目次