Shopify 产品详情根据product options选中的variant 渲染其指定的一组媒体图

需求: 根据产品Variant Picker选中的options 渲染其媒体,如option1:红、黄、蓝,option2:大、中、小,option3: 。。。,选中红、大,渲染对应的一组图片媒体

改变shopify后台产品媒体图的alt:

alt 规则:

  • 只有 1 个维度:"... ~0" / "... ~1"

  • 2 维度:"... ~0-1"

  • 3 维度:"... ~0-1-2"

  • 通用图:"... ~common"

javascript 复制代码
{%- assign image_medias = product.media | where: "media_type", "image" -%}
{%- assign default_media = product.selected_or_first_available_variant.featured_media | default: product.featured_media -%}

{% if product.variants.size > 1 %}

  {%- assign selected_variant = product.selected_or_first_available_variant -%}

  {%- comment %}
    最多 3 个 option:
    - 至少有 option1
    - option2 / option3 可能没有
  {%- endcomment -%}
  {%- assign options_size = product.options_with_values.size -%}
  {%- assign option1 = product.options_with_values[0] -%}
  {%- if options_size > 1 -%}
    {%- assign option2 = product.options_with_values[1] -%}
  {%- endif -%}
  {%- if options_size > 2 -%}
    {%- assign option3 = product.options_with_values[2] -%}
  {%- endif -%}

  {%- comment %}
    先初始化下标
  {%- endcomment -%}
  {%- assign idx1 = 0 -%}
  {%- assign idx2 = '' -%}
  {%- assign idx3 = '' -%}

  {%- comment %}
    1. 找 selected_variant.option1 在 option1.values 里的下标 idx1(必有)
  {%- endcomment -%}
  {%- for v in option1.values -%}
    {%- if v == selected_variant.option1 -%}
      {%- assign idx1 = forloop.index0 -%}
      {%- break -%}
    {%- endif -%}
  {%- endfor -%}

  {%- comment %}
    2. 如果有 option2,则找 idx2
  {%- endcomment -%}
  {%- if options_size > 1 -%}
    {%- for v in option2.values -%}
      {%- if v == selected_variant.option2 -%}
        {%- assign idx2 = forloop.index0 -%}
        {%- break -%}
      {%- endif -%}
    {%- endfor -%}
  {%- endif -%}

  {%- comment %}
    3. 如果有 option3,则找 idx3
  {%- endcomment -%}
  {%- if options_size > 2 -%}
    {%- for v in option3.values -%}
      {%- if v == selected_variant.option3 -%}
        {%- assign idx3 = forloop.index0 -%}
        {%- break -%}
      {%- endif -%}
    {%- endfor -%}
  {%- endif -%}

  {%- comment %}
    4. 组合 current_group_key:
       - 只有 option1    => "0"
       - option1+2       => "0-1"
       - option1+2+3     => "0-1-2"
  {%- endcomment -%}
  {%- assign current_group_key = idx1 | append: '' -%}
  {%- if options_size > 1 and idx2 != '' -%}
    {%- assign current_group_key = current_group_key | append: '-' | append: idx2 -%}
  {%- endif -%}
  {%- if options_size > 2 and idx3 != '' -%}
    {%- assign current_group_key = current_group_key | append: '-' | append: idx3 -%}
  {%- endif -%}

  <product-media-images
    class="swiper-media-images"
    data-group="{{ current_group_key }}"
    data-variant-id="{{ selected_variant.id }}"
    {%- if section.settings.enable_image_zoom -%}
      allow-zoom
    {%- endif -%}
  >
    <div class="swiper-container">
      <div class="swiper">
        <div class="swiper-wrapper">
          {%- for media in image_medias -%}
            {%- if default_media == media -%}
              {%- assign loading = 'eager' -%}
              {%- assign fetchpriority = 'high' -%}
            {%- else -%}
              {%- assign loading = 'lazy' -%}
              {%- assign fetchpriority = 'auto' -%}
            {%- endif -%}

            {%- assign alt = media.alt | default: '' -%}
            {%- assign parts = alt | split: "~" -%}
            {%- assign key = parts | last | strip | downcase -%}

            {%- if key == current_group_key -%}
              <div class="swiper-slide" data-media-id="{{ media.id }}">
                <img
                  width="{{ media.width }}"
                  height="{{ media.height }}"
                  src="{{ media | image_url }}"
                  alt="{{ media.alt }}"
                  loading="{{ loading }}"
                  fetchpriority="{{ fetchpriority }}"
                >
              </div>
            {%- elsif key == 'scene' -%}
              <div class="swiper-slide" data-media-id="{{ media.id }}">
                <img
                  width="{{ media.width }}"
                  height="{{ media.height }}"
                  src="{{ media | image_url }}"
                  alt="{{ media.alt }}"
                  loading="{{ loading }}"
                  fetchpriority="{{ fetchpriority }}"
                >
              </div>
            {%- endif -%}
          {%- endfor -%}
        </div>
      </div>

      <div class="swiper-button swiper-button-prev">
      </div>
      <div class="swiper-button swiper-button-next">
      </div>

      {%- if section.settings.carousel_control == 'thumbnails' -%}
        <div>缩略图</div>
      {%- else -%}
        <div class="swiper-pagination-wrapper">
          <div class="swiper-pagination"></div>
        </div>
      {%- endif -%}
      {%- if section.settings.enable_image_zoom -%}
        <div class="product-zoom">与模板的放大联动, 推荐PSWP</div>
      {%- endif -%}
    </div>
  </product-media-images>

{% else %}
  <swiper-media-images 
    {%- if section.settings.enable_image_zoom -%}allow-zoom{% endif %}
  >
    <div class="swiper-container">
      <div class="swiper">
        <div class="swiper-wrapper">
          {%- for media in image_medias -%}
            {%- assign parts = media.alt | split: "~" -%}
            {%- assign key = parts | last | downcase -%}
            <div class="swiper-slide">
              {%- if key == 'common' -%}
                <img width="{{ media.width }}" height="{{ media.height }}" src="{{ media | image_url }}" alt="{{ media.alt }}" />
              {%- else -%}
                <img width="{{ media.width }}" height="{{ media.height }}" src="{{ media | image_url }}" alt="{{ media.alt }}" />
              {%- endif -%}
            </div>
          {%- endfor -%}
        </div>
      </div>
      <div class="swiper-button swiper-button-prev">
      </div>
      <div class="swiper-button swiper-button-next">
      </div>
      {%- if section.settings.carousel_control == 'thumbnails' -%}
        <div>缩略图</div>
      {%- else -%}
        <div class="swiper-pagination-wrapper">
          <div class="swiper-pagination"></div>
        </div>
      {%- endif -%}
      {%- if section.settings.enable_image_zoom -%}
        <div class="product-zoom">与模板的放大联动, 推荐PSWP</div>
      {%- endif -%}
    </div>
  </swiper-media-images>
{% endif %}

web 组件代码

javascript 复制代码
class ProductMediaImages extends HTMLElement {
  constructor() {
    super();
  }
  connectedCallback() {
    this.initSwiper();
    this.addEventListener("swiperControl:select", (event) => this.select(event.detail.index));
  }

  select(idx) {
    this.swiper.slideToLoop(idx);
  }
  initSwiper() {
    const swiperEl = this.querySelector('.swiper');
    if (!swiperEl) return;
    const lastIdx = this.dataset.lastIdx;
    this.swiper = new Swiper(swiperEl, {
      observer: true,
      observeParents: true,
      effect: "fade",
      loop: true,
      fadeEffect: {
        crossFade: true 
      },
      touchStartPreventDefault: true,
      passiveListeners: false,
      slidesPerView: 1,
      pagination: {
        el: this.querySelector('.swiper-pagination'),
        clickable: true,
      },
      on: {
        slideChange(swiperInstance) {
          const realIndex = swiperInstance.realIndex;
          const pageDots = swiperInstance.el.closest('.swiper-container').querySelector('page-dots-swiper');
          pageDots.select(realIndex, false);
        }
      },
      navigation: {
        nextEl: this.querySelector('.swiper-button-next'),
        prevEl: this.querySelector('.swiper-button-prev'),
      },
    });
    if(lastIdx) {
      this.swiper.slideToLoop(lastIdx);
    }
  }
}
if (!customElements.get('product-media-images')) {
  customElements.define('product-media-images', ProductMediaImages );
}

一般模板都会有VariantPicker这个组件,需要在选中变体后请求section rendering api:如果模板本身切变体就是通过section rendering而不是请求product json就可以直接用,没有就需要写一个api请求。

只需要在变体选择后执行这个方法即可this._initSwiperMedias(id)

javascript 复制代码
  _initSwiperMedias(variantId) { // 只要切变体就把上一次的缓存起来 然后去缓存里找variantId
    const sectionId = this.sectionId;
    if (!sectionId || !variantId) return;

    const oldSwiper = document.querySelector(
      `#shopify-section-${sectionId} .swiper-media-images`
    );
    if (!oldSwiper) return;
    const swiperEl = oldSwiper.querySelector('.swiper');
    this._swiperCache.set(+oldSwiper.dataset.variantId, {
      lastIdx: swiperEl.swiper.realIndex ?? 0,
      oldSwiper
    }); // id和dom缓存
    const swiperInstance = swiperEl?.swiper;
    // 如果缓存中有这个 variant,直接还原
    if (this._swiperCache.has(variantId)) {
      const cachedDom = this._swiperCache.get(variantId);
      cachedDom.oldSwiper.setAttribute('data-last-idx', cachedDom.lastIdx);
      oldSwiper.replaceWith(cachedDom.oldSwiper);
      return;
    }

    // 缓存没有,再走 Section Rendering API
    const url = new URL(window.location.href);
    url.searchParams.set('variant', variantId);
    url.searchParams.set('section_id', sectionId);

    document.querySelector('loading-dots').setAttribute('loading', 'true');
    fetch(url.toString())
      .then(response => response.text())
      .then(html => {
        const temp = document.createElement('div');
        temp.innerHTML = html;
        const newSwiper = temp.querySelector('.swiper-media-images');
        if (!newSwiper) return;
        const cloned = newSwiper.cloneNode(true);
        oldSwiper.replaceWith(cloned);
        document.querySelector('loading-dots').setAttribute('loading', 'false');
        if (swiperInstance) {
          swiperInstance.destroy(true, true);
        }
      })
      .catch(err => {
        console.error('fetch section error', err);
      });
  }
相关推荐
SuppperSA5 小时前
Shopify 视频懒加载,性能优化
shopify
SuppperSA4 个月前
Shopify Section Rendering API
shopify
平台工程社区2 年前
对话Shopify:平台工程如何帮助其自动化应对流量高峰
团队开发·idp·shopify·平台工程
麦豇豆2 年前
Ruby 版本升级
ruby·openssl·shopify
苍溟丶2 年前
WordPress还是Shopify?如何选择最适合您业务的网站建设平台?
wordpress·shopify·建站选择·优缺点选择