需求: 根据产品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);
});
}