star back image
people4
電飾 電飾
moon
men
shopify swiperスライダーをシンプルに設置したい

【shopify】swiperのスライダーをシンプルに設置したい

BLOG shopifyWEBログ
読了約:62分

shopifyにはかっこいいテンプレートが沢山売っています。
外人さんがバリバリに作り込んだテンプレートは圧巻で、至れり尽くせりです。

shopify テンプレート
https://themes.shopify.com/?locale=ja

写真を入れ替えて商品を登録するだけでWEB SHOP完成じゃね!?
みたいな完成度のテンプレートがいっぱい。

しかしそういう流れには多分なりません。
改造したいというのです。

改造といっても聞いたこともないライブラリを使っていますよ。

デキる外人さんのガチガチのオリジナルscriptです。

テンプレートのソースを睨めっこして理解するだけで、簡単に時間が溶けていきます。
いろんなものがインクルードされていて他の箇所に影響しそう。もう初期状態の最初のプレーンな状態から構築したい。

この記事は、そういう考えに落ち着きました人用の、比較的にカスタムすることが多いなと感じる「スライダー」を設置したいという旨の内容のものになります。

【共有】ソースコード

sectionsで追加作成したliquidファイルにそのまま貼り付けれるように、cssとhtmlとscript、schemaを丸っと記述します。

要件は以下です。

  • 使い慣れたswiperスライダー。
  • sectionで何個もページ内に仕込めるようにしたい。
  • ページネーションとnext/prevのボタンあり。
  • 画像の上にタイトルとかリンクを入れたい時も想定したい。
  • zoom機能【オプション】

html(sections/slideshow-swiper.liquid)

<!-- /sections/slideshow-swiper.liquid -->
{%- style -%}
  #shopify-section-{{ section.id }} {
  }

  .section-{{ section.id }}-padding {
    padding-top: calc({{ section.settings.padding_top }}px * 0.75);
    padding-bottom: calc({{ section.settings.padding_bottom }}px  * 0.75);
  }
  .section-{{ section.id }}-margin {
    margin-left: calc({{ section.settings.margin_left }}px * 0.75);
    margin-right: calc({{ section.settings.margin_right }}px  * 0.75);
  }

  @media screen and (min-width: 750px) {
    .section-{{ section.id }}-padding {
      padding-top: {{ section.settings.padding_top }}px;
      padding-bottom: {{ section.settings.padding_bottom }}px;
    }
    .section-{{ section.id }}-margin {
      margin-left: {{ section.settings.margin_left }}px;
      margin-right: {{ section.settings.margin_right }}px;
    }
  }
  @media screen and (max-width: 749px) {
    .section-{{ section.id }}-margin {
      margin-left: 0px;
      margin-right: 0px;
    }
  }

  .swiper-slideshow-info {
    height: 100%;
    display: flex;
    align-items: center;
  }
  .swiper-slideshow-info .slideshow-box {
    max-width: 72.6rem;
  }

  .swiper-slideshow-info.slideshow__box--top-left {
    align-items: flex-start;
    justify-content: flex-start;
    text-align: start;
  }
  .swiper-slideshow-info.slideshow__box--top-center {
    align-items: flex-start;
    justify-content: center;
    text-align: center;
  }
  .swiper-slideshow-info.slideshow__box--top-right {
    align-items: flex-start;
    justify-content: flex-end;
    text-align: end;
  }
  .swiper-slideshow-info.slideshow__box--middle-left {
    align-items: center;
    justify-content: flex-start;
    text-align: start;
  }
  .swiper-slideshow-info.slideshow__box--middle-center {
    align-items: center;
    justify-content: center;
    text-align: center;
  }
  .swiper-slideshow-info.slideshow__box--middle-right {
    align-items: center;
    justify-content: flex-end;
    text-align: end;
  }
  .swiper-slideshow-info.slideshow__box--bottom-left {
    align-items: flex-end;
    justify-content: flex-start;
    text-align: start;
  }
  .swiper-slideshow-info.slideshow__box--bottom-center {
    align-items: flex-end;
    justify-content: center;
    text-align: center;
  }
  .swiper-slideshow-info.slideshow__box--bottom-right {
    align-items: flex-end;
    justify-content: flex-end;
    text-align: end;
  }

  @media screen and (min-width: 750px) {
    #set-{{ section.id }}.container {
      margin-inline: auto;
      max-width: unset;
      position: relative;
    }
    #set-{{ section.id }} .swiper{
      max-width: unset;
      height: auto;
      opacity: 0;
      animation: showSwiper 0.2s ease forwards;
    }
    @keyframes showSwiper {
      0% {
        opacity: 0;
      }
      100% {
        opacity: 1;
      }
    }
    #set-{{ section.id }} .swiper-slide {
      position: relative;
    }
    #set-{{ section.id }} .swiper-slide > picture img {
      object-fit: contain;
      width: 100%;
      height: 80svh;
    }
    #set-{{ section.id }} .swiper-pagination {
      text-align: center;
      position: relative;
    }
    #set-{{ section.id }} .swiper-horizontal>.swiper-pagination-bullets, #set-{{ section.id }} .swiper-pagination-bullets.swiper-pagination-horizontal, #set-{{ section.id }} .swiper-pagination-custom, #set-{{ section.id }} .swiper-pagination-fraction {
      bottom: -5px;
      left: unset;
      right: unset;
      width: 100% !important;
      padding: 0 0;
    }
    #set-{{ section.id }} .swiper-pagination-bullet {
      width: var(--swiper-pagination-bullet-width, var(--swiper-pagination-bullet-size, 8px));
      height: var(--swiper-pagination-bullet-height, var(--swiper-pagination-bullet-size, 8px));
      background: #000 !important;
    }
    #set-{{ section.id }} .swiper-button-next, #set-{{ section.id }} .swiper-button-prev {
      z-index: 1;
    }
    #set-{{ section.id }} .swiper-button-next, #set-{{ section.id }} .swiper-rtl .swiper-button-prev {
      right: var(--swiper-navigation-sides-offset, -55px);
      left: auto;
      display: block;
    }
    #set-{{ section.id }} .swiper-button-prev, #set-{{ section.id }} .swiper-rtl .swiper-button-next {
      left: var(--swiper-navigation-sides-offset, -55px);
      right: auto;
      display: block;
    }
    #set-{{ section.id }} .swiper-button-next:after, #set-{{ section.id }} .swiper-rtl .swiper-button-prev:after {
      content: 'next';
    }
    #set-{{ section.id }} .swiper-button-prev:after, #set-{{ section.id }} .swiper-rtl .swiper-button-next:after {
      content: 'prev';
    }
    #set-{{ section.id }} .swiper-button-next:after, #set-{{ section.id }} .swiper-button-prev:after {
      font-family: swiper-icons;
      font-size: var(--swiper-navigation-size);
      text-transform: none!important;
      letter-spacing: 0;
      font-variant: initial;
      line-height: 1;
      width: 6px;
      height: 12px;
      color: #000;
    }
    #set-{{ section.id }} .swiper-button-prev:after {
      transform: rotate(0deg);
    }
    #set-{{ section.id }} .swiper-slideshow-info {
      position: absolute;
      width: calc(100% - 10%);
      height: 100%;
      left: 0;
      top: 0;
      padding: 1.4rem 5%;
    }
    #set-{{ section.id }} .slideshow-subheading {
      margin: 1.2rem 0;
    }
    #set-{{ section.id }} .slideshow-heading {
      margin: 1.2rem 0;
    }
    #set-{{ section.id }} .slideshow-text {
      margin: 1.2rem 0;
    }
    #set-{{ section.id }} .slideshow-button {
    }
    #set-{{ section.id }} picture {
      display: flex;
    }
  }

  @media screen and (max-width: 749px) {
    #set-{{ section.id }}.container {
      margin-inline: auto;
      max-width: unset;
      position: relative;
      width: auto;
      margin: 0 26px 0;
    }
    #set-{{ section.id }} .swiper{
      max-width: unset;
      height: auto;
      opacity: 0;
      animation: showSwiper 0.2s ease forwards;
    }
    @keyframes showSwiper {
      0% {
        opacity: 0;
      }
      100% {
        opacity: 1;
      }
    }
    #set-{{ section.id }} .swiper-slide {
      position: relative;
    }
    #set-{{ section.id }} .swiper-slide > picture img {
      object-fit: cover;
      width: 100%;
      height: auto;
    }
    #set-{{ section.id }} .swiper-pagination {
      text-align: center;
      position: relative;
    }
    #set-{{ section.id }} .swiper-horizontal>.swiper-pagination-bullets, #set-{{ section.id }} .swiper-pagination-bullets.swiper-pagination-horizontal, #set-{{ section.id }} .swiper-pagination-custom, #set-{{ section.id }} .swiper-pagination-fraction {
      bottom: -5px;
      left: unset;
      right: unset;
      width: 100% !important;
      padding: 0 0;
    }
    #set-{{ section.id }} .swiper-pagination-bullet {
      width: var(--swiper-pagination-bullet-width, var(--swiper-pagination-bullet-size, 5px));
      height: var(--swiper-pagination-bullet-height, var(--swiper-pagination-bullet-size, 5px));
      background: #000 !important;
    }
    #set-{{ section.id }} .swiper-button-next, #set-{{ section.id }} .swiper-button-prev {
      z-index: 1;
    }
    #set-{{ section.id }} .swiper-button-next, #set-{{ section.id }} .swiper-rtl .swiper-button-prev {
      right: var(--swiper-navigation-sides-offset, -35px);
      left: auto;
      display: flex;
    }
    #set-{{ section.id }} .swiper-button-prev, #set-{{ section.id }} .swiper-rtl .swiper-button-next {
      left: var(--swiper-navigation-sides-offset, -35px);
      right: auto;
      display: flex;
    }
    #set-{{ section.id }} .swiper-button-next:after, #set-{{ section.id }} .swiper-rtl .swiper-button-prev:after {
      content: 'next';
    }
    #set-{{ section.id }} .swiper-button-prev:after, #set-{{ section.id }} .swiper-rtl .swiper-button-next:after {
      content: 'prev';
    }
    #set-{{ section.id }} .swiper-button-next:after, #set-{{ section.id }} .swiper-button-prev:after {
      font-family: swiper-icons;
      font-size: 16px;
      text-transform: none!important;
      letter-spacing: 0;
      font-variant: initial;
      line-height: 1;
      width: 6px;
      height: 12px;
      color: #000;
    }
    #set-{{ section.id }} .swiper-button-prev:after {
      transform: rotate(0deg);
    }
    #set-{{ section.id }} .swiper-slideshow-info {
      width: calc(100% - 10%);
      height: 100%;
      left: 0;
      top: 0;
    }
    #set-{{ section.id }} .slideshow-subheading {
      margin: 1.2rem;
    }
    #set-{{ section.id }} .slideshow-heading {
      margin: 1.2rem;
    }
    #set-{{ section.id }} .slideshow-text {
      margin: 1.2rem;
    }
    #set-{{ section.id }} .slideshow-button {
      margin: 1.2rem;
    }
    #set-{{ section.id }} picture {
      display: flex;
    }
  }

  {% comment %}トップページにだけcss{% endcomment %}
  {%- if request.page_type == 'index' -%}
  {%- endif -%}

{%- endstyle -%}

<div class="page-width section-{{ section.id }}-padding section-{{ section.id }}-margin">

  <div id="set-{{ section.id }}" class="container">
    <div class="swiper">
      <!-- Additional required wrapper -->
      <div class="swiper-wrapper">
        <!-- Slides -->
        {% for block in section.blocks %}
        <div class="swiper-slide">
          <picture class="swiper-zoom-container">
            <source srcset="{{ block.settings.image | img_url: 'master' }}" media="(min-width: 1024px)">
            <source srcset="{{ block.settings.image | img_url: 'master' }}" media="(min-width: 768px)">
            <img src="{{ block.settings.image_mobile | img_url: 'master' }}" alt="スライド {{ forloop.index }}">
          </picture>
          <div class="swiper-slideshow-info slideshow__box--{{ block.settings.text_box_position }}">
            <div class="slideshow-box">
            {%- if block.settings.subheading != blank -%}
              <h5 class="slideshow-subheading {{ block.settings.subheading_size }}">
                {{ block.settings.subheading | escape }}
              </h5>
            {%- endif -%}
            {%- if block.settings.heading != blank -%}
              <h2 class="slideshow-heading {{ block.settings.heading_size }}">
                {{ block.settings.heading | escape }}
              </h2>
            {%- endif -%}
            {%- if block.settings.text != blank -%}
              <div class="slideshow-text {{ block.settings.text_size }}">
                {{ block.settings.text | escape }}
              </div>
            {%- endif -%}
            {%- if block.settings.button_label != blank -%}
              <div class="slideshow-button">
                  <a class="button"{% if block.settings.button_link != blank %} href="{{ block.settings.button_link }}"{% else %} role="link" aria-disabled="true"{% endif %}>
                    {{ block.settings.button_label | escape }}
                  </a>
              </div>
            {%- endif -%}
            </div>
          </div>
        </div>
        {% endfor %}

      </div>
    </div>
    <!-- If we need pagination -->
    <div class="swiper-pagination"></div>
    <!-- navigation buttons -->
    <div class="swiper-button-prev"></div>
    <div class="swiper-button-next"></div>
  </div>

</div>

<!-- swiper -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swiper@10/swiper-bundle.min.css" />
<script src="https://cdn.jsdelivr.net/npm/swiper@10/swiper-bundle.min.js"></script>
<script>
const mySwiper = new Swiper('#set-{{ section.id }} .swiper', {
  slidesPerView: 1,
  spaceBetween: 0,
  loop: true,
  zoom: {
    maxRatio: 3, // 最大拡大率
  },
  loopAdditionalSlides: 1,
  speed: 1000,
  autoplay: {
    delay: 50000,
    disableOnInteraction: false,
  },
  pagination: {
    el: '#set-{{ section.id }} .swiper-pagination',
    clickable: true,
  },
  navigation: {
    nextEl: '#set-{{ section.id }} .swiper-button-next',
    prevEl: '#set-{{ section.id }} .swiper-button-prev',
  },
  on: {
    init: function () {
      this.slides.forEach((slide, index) => {
        const formattedIndex = String(index + 1).padStart(2, '0'); // 01, 02, 03の形式にする
        slide.classList.add(`index_${formattedIndex}`);
      });
    },
  },
});
</script>

{% schema %}
{
  "name": "t:sections.slideshow-swiper.name",
  "tag": "section",
  "class": "section",
  "max_blocks": 50,
  "settings": [
    {
      "type": "paragraph",
      "content": "t:sections.slideshow-swiper.settings.paragraph.content"
    },
    {
      "type": "header",
      "content": "t:sections.slideshow-swiper.settings.padding.header.content"
    },
    {
      "type": "range",
      "id": "padding_top",
      "min": 0,
      "max": 100,
      "step": 4,
      "unit": "t:sections.slideshow-swiper.settings.padding.padding_top.unit",
      "label": "t:sections.slideshow-swiper.settings.padding.padding_top.label",
      "default": 36
    },
    {
      "type": "range",
      "id": "padding_bottom",
      "min": 0,
      "max": 100,
      "step": 4,
      "unit": "t:sections.slideshow-swiper.settings.padding.padding_bottom.unit",
      "label": "t:sections.slideshow-swiper.settings.padding.padding_bottom.label",
      "default": 36
    },
    {
      "type": "range",
      "id": "margin_left",
      "min": 0,
      "max": 100,
      "step": 4,
      "unit": "t:sections.slideshow-swiper.settings.padding.margin_left.unit",
      "label": "t:sections.slideshow-swiper.settings.padding.margin_left.label",
      "default": 36
    },
    {
      "type": "range",
      "id": "margin_right",
      "min": 0,
      "max": 100,
      "step": 4,
      "unit": "t:sections.slideshow-swiper.settings.padding.margin_right.unit",
      "label": "t:sections.slideshow-swiper.settings.padding.margin_right.label",
      "default": 36
    }
  ],
  "blocks": [
    {
      "type": "image",
      "name": "t:sections.slideshow-swiper.blocks.image.name",
      "settings": [
        {
          "type": "image_picker",
          "id": "image",
          "label": "t:sections.slideshow-swiper.blocks.image.settings.image.label"
        },
        {
          "type": "image_picker",
          "id": "image_mobile",
          "label": "t:sections.slideshow-swiper.blocks.image.settings.image_mobile.label"
        },
        {
          "type": "select",
          "id": "text_box_position",
          "options": [
            {
              "value": "top-left",
              "label": "t:sections.slideshow-swiper.blocks.image.settings.text_box_position.options__1.label"
            },
            {
              "value": "top-center",
              "label": "t:sections.slideshow-swiper.blocks.image.settings.text_box_position.options__2.label"
            },
            {
              "value": "top-right",
              "label": "t:sections.slideshow-swiper.blocks.image.settings.text_box_position.options__3.label"
            },
            {
              "value": "middle-left",
              "label": "t:sections.slideshow-swiper.blocks.image.settings.text_box_position.options__4.label"
            },
            {
              "value": "middle-center",
              "label": "t:sections.slideshow-swiper.blocks.image.settings.text_box_position.options__5.label"
            },
            {
              "value": "middle-right",
              "label": "t:sections.slideshow-swiper.blocks.image.settings.text_box_position.options__6.label"
            },
            {
              "value": "bottom-left",
              "label": "t:sections.slideshow-swiper.blocks.image.settings.text_box_position.options__7.label"
            },
            {
              "value": "bottom-center",
              "label": "t:sections.slideshow-swiper.blocks.image.settings.text_box_position.options__8.label"
            },
            {
              "value": "bottom-right",
              "label": "t:sections.slideshow-swiper.blocks.image.settings.text_box_position.options__9.label"
            }
          ],
          "default": "middle-center",
          "label": "t:sections.slideshow-swiper.blocks.image.settings.text_box_position.label"
        },
        {
          "type": "text",
          "id": "subheading",
          "label": "t:sections.slideshow-swiper.blocks.image.settings.subheading.label"
        },
        {
          "type": "text",
          "id": "heading",
          "default": "Image slide",
          "label": "t:sections.slideshow-swiper.blocks.image.settings.heading.label"
        },
        {
          "type": "textarea",
          "id": "text",
          "default": "Give customers details about the slide image or content on the template.",
          "label": "t:sections.slideshow-swiper.blocks.image.settings.text.label"
        },
        {
          "type": "text",
          "id": "button_label",
          "default": "Button label",
          "label": "t:sections.slideshow-swiper.blocks.image.settings.button_label.label",
          "info": "t:sections.slideshow-swiper.blocks.image.settings.button_label.info"
        },
        {
          "type": "url",
          "id": "button_link",
          "label": "t:sections.slideshow-swiper.blocks.image.settings.button_link.label"
        }
      ]
    }
  ],
  "presets": [
    {
      "name": "t:sections.slideshow-swiper.presets.name",
      "blocks": [
        {
          "type": "image"
        },
        {
          "type": "image"
        }
      ]
    }
  ]
}
{% endschema %}

cssが多少不備なところもあると思いますが、自由に変えてください。

【オプション】「zoom」機能
スライダーの画像をダブルタップで拡大してくれるswiperのオプション機能です。

358〜360行目
【オプション】で「zoom」機能を入れています。
必要のない場合は消してください。

※もちろん、
「タイトルとかボタン」を画像の上に配置するとなると、重なる部分でうまく動作がいかなくなるでしょう。

その時はzoomを清く諦めましょう。
zoomしたければ、タイトルとかボタンを清く諦めましょう。
zoomしたら非表示にするとか苦労したいときは、AIに相談しましょう。

※next/prevのarrowアイコン(<>)はフォントです。

なんでswiperなんですか?

みんな大好きだからです。

オフィシャルのデモページでは、いろんなスライダーの表現を確認できます。

swiper デモ
https://swiperjs.com/demos

【共有】jsonコード

管理画面の入力箇所の名前のlabelを決めよう。
入力項目のタイトルなどが日本語になるように、以下のファイルにjsonを追加します。

locales/ja.schema.json

{ 
   "slideshow-swiper": {
      "name": "スライドショーswiper",
      "settings": {
        "paragraph": {
          "content": "基本的な画像を使うスライドショーです。"
        },
        "padding": {
          "header": {
            "content": "セクションパディング"
          },
          "padding_top": {
            "label": "上パディング",
            "unit": "px"
          },
          "padding_bottom": {
            "label": "下パディング",
            "unit": "px"
          },
          "margin_right": {
            "label": "右マージン",
            "unit": "px"
          },
          "margin_left": {
            "label": "左マージン",
            "unit": "px"
          }
        }
      },
      "blocks": {
        "image": {
          "name": "画像スライド",
          "settings": {
            "image": {
              "label": "画像"
            },
            "image_mobile": {
              "label": "モバイル画像"
            },
            "text_box_position": {
              "options__1": {
                "label": "左上"
              },
              "options__2": {
                "label": "上・中央"
              },
              "options__3": {
                "label": "右上"
              },
              "options__4": {
                "label": "左・中央"
              },
              "options__5": {
                "label": "上下左右の中央"
              },
              "options__6": {
                "label": "右・中央"
              },
              "options__7": {
                "label": "左下"
              },
              "options__8": {
                "label": "下・中央"
              },
              "options__9": {
                "label": "右下"
              },
              "label": "デスクトップコンテンツポジション",
              "info": "タイトル位置を決めます。"
            },
            "heading": {
              "label": "ヘッディング"
            },
            "subheading": {
              "label": "サブヘディング"
            },
            "text": {
              "label": "詳細"
            },
            "button_label": {
              "label": "ボタンのラベル",
              "info": "ボタンを非表示するには空白のラベルにしてください"
            },
            "button_link": {
              "label": "リンクボタン"
            }
          }
        }
      },
      "presets": {
        "name": "スライドショーswiper"
      }
    }
}

入力項目のタイトルって、何を言ってるんですか?

以下のような箇所(shopifyのCMS)になります。

shopifyにログインして「カスタマイズ」をしてみてください。

「+セクションを追加」する時

セクションを追加する時

作成した「スライドショーswiper」が登場しているのを確認できるでしょう。

セクション要素の設定(スライダーの大枠)

ブロック要素の設定(block要素はスライド枚数も増やせます)

面倒ですが、管理画面をカスタマイズできる感覚で楽しいですね。

※英語の方もやる必要があると思います。その時はlocales/en.default.jsonというファイルがそれなので、同じように英語で仕込みます。

以上、メモになります。
いろいろな方法があると思いますが、諦めてプレーンな状態からスタートさせるのも手だと思いますがいかがでしょうか。

よくあるかも怖い余談話

shopifyでありそうな、怖いお話です。

・仕様的な事柄もデザインXDを見ながらのミーティングで終わり。
・遅れてテンプレートを購入も、デザインとかけ離れている。

さらに以下のようなワードを感じたら要注意です。

・テンプレートの機能は全て活かしつつ改造したい。
・テンプレートのカスタマイズだけなので、4人日(お安く)で可能?
・デザインXDの赤の箇所変更で、あそこトル詰めです。
・これじゃなく、前にどこか別案件でやったアレです。
・わからないことあったら早く聞いてください。

危険ワードです。
言った言わない、仕様も2転3転する可能性が高いかも。

次回のお見積もりは3倍程度、納期も同じく3倍が適当でしょう。

間に合いそうもないと感じたら、早く言っておいた方が良いです。

あなたの作業が的を得ていないのでは。

どうして的を得ない作業をしたのですか?
それに仕様が変わるとか普通なりますか。
何を聞いてたのですか?

ふぇ。。ごめんなさい。

【AI】イラストを描いてもらった

今回の記事のキャッチ画像で使わせてもらいます「Memeplex.app」で作成した画像です。誰でもgoogleアカウントでログインして使えます。

この記事にピッタリなイラストのための考えたリクエストは、「3D電子仮想空間ショッピングモール内を漂いながら買い物を楽しむ女の子」です。

選んだモードは以下の3つです。
・特撮
・サイバーパンク風
・UnrealEngine5風

頭の中で考えてた「こんな感じ」を超えてきます。

女の子、かわいい。

参考サイト置き場

swiper デモ
https://swiperjs.com/demos

shopify テンプレート
https://themes.shopify.com/?locale=ja

wordpress 「サーバーが画像を処理できません」に対処!
https://ineters.com/wordpress/wp-image-upload-error/