zz-shopify-components 0.32.0 → 0.32.1-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -109,12 +109,15 @@
109
109
  <div class="tw-w-full tw-px-8">
110
110
  <div class="zz-navigation-pc-tab max-lg:tw-hidden lg:tw-h-[50px] tw-flex tw-items-center tw-justify-between tw-max-w-[1660px] lg:tw-px-[20px] tw-w-full tw-mx-auto">
111
111
  <img
112
- class="tw-w-[134px] tw-h-[12px]"
112
+ class="zz-navigation-pc-logo tw-w-[134px] tw-h-[12px]"
113
113
  src="{{ section.settings.icon | image_url: width: 200 }}" alt="{{ section.settings.icon.alt }}" width="134" height="12">
114
114
  <div class="zz-navigation-pc-item-box tw-flex tw-items-center tw-overflow-x-scroll tw-ml-[40px] tw-pr-[60px]">
115
115
  {%- if section.blocks.size > 0 -%}
116
116
  {% for block in section.blocks %}
117
- <div class="pc-tab-item tw-flex-shrink-0 tw-px-[30px]">
117
+ <div
118
+ class="pc-tab-item tw-flex-shrink-0 tw-px-[30px]"
119
+ data-track-zz-element='zz-navigation_item_{{ block.settings.module_name | downcase }}'
120
+ >
118
121
 
119
122
  <span
120
123
  class="zz-navigation-tab-item-text tw-cursor-pointer tw-text-[#9E9E9E] tw-text-[14px] tw-leading-[14px] tw-font-[400]"
@@ -154,11 +157,14 @@
154
157
  <div class="mb-navigation-box-item zz-navigation-mb-item-box tw-flex tw-items-center tw-gap-[24px] tw-pr-[120px] tw-h-[52px] tw-overflow-x-scroll">
155
158
  {%- if section.blocks.size == 0 -%}
156
159
  <img
157
- class="tw-w-[134px] tw-h-[12px]"
160
+ class="zz-navigation-mb-logo tw-w-[134px] tw-h-[12px]"
158
161
  src="{{ section.settings.icon | image_url: width: 200 }}" alt="{{ section.settings.icon.alt }}" width="134" height="12">
159
162
  {%- endif -%}
160
163
  {% for block in section.blocks %}
161
- <div class="pc-tab-item tw-flex-shrink-0">
164
+ <div
165
+ class="pc-tab-item tw-flex-shrink-0"
166
+ data-track-zz-element='zz-navigation_item_{{ block.settings.module_name | downcase }}'
167
+ >
162
168
 
163
169
  <span
164
170
  class="zz-navigation-tab-item-text tw-cursor-pointer tw-text-[#9E9E9E] tw-text-[13px] tw-leading-[13px] tw-font-[400]"
@@ -241,8 +247,10 @@
241
247
  tab.addEventListener('click', () => {
242
248
  const id = tab.dataset.target;
243
249
  const target = document.querySelector(`[data-zz-module-name="${id}"]`);
250
+ if (!target) return;
244
251
  const headerHeight = isDesktop ? 50 : 50;
245
- const offsetTop = target.offsetTop - headerHeight;
252
+ const rect = target.getBoundingClientRect();
253
+ const offsetTop = rect.top + window.scrollY - headerHeight;
246
254
  scrollPaginationToCenter();
247
255
  window.scrollTo(0, offsetTop);
248
256
  window.zzAnalytics && window.zzAnalytics.trackEvent('141_productpage_click_header_tab_item', {
@@ -0,0 +1,549 @@
1
+
2
+
3
+ {% assign swiper_blocks = section.blocks | where: 'type', 'video' %}
4
+
5
+
6
+ <style>
7
+ #shopify-section-{{ section.id }} {
8
+ background: {{ section.settings.bg_color }};
9
+ display: flex;
10
+ flex-direction: column;
11
+ align-items: center;
12
+ padding-top: {{ section.settings.mobile_padding_top }}px;
13
+ padding-bottom: {{ section.settings.mobile_padding_bottom }}px;
14
+ }
15
+
16
+
17
+ #shopify-section-{{ section.id }} .pagination-item {
18
+ flex-shrink: 0;
19
+ overflow: hidden;
20
+ background-position: center;
21
+ background-repeat: no-repeat;
22
+ background-size: cover;
23
+ }
24
+
25
+ #shopify-section-{{ section.id }} .center-title-content {
26
+ top: 72px;
27
+ left: 20px;
28
+ right: 20px;
29
+ }
30
+
31
+ @media screen and (max-width: 1024px) {
32
+ #shopify-section-{{ section.id }} {
33
+ padding-left: 20px;
34
+ padding-right: 20px;
35
+ }
36
+
37
+ #shopify-section-{{ section.id }} .progress-div {
38
+ position: absolute;
39
+ bottom: 0px;
40
+ left: 6px;
41
+ right: 6px;
42
+ height: 1px;
43
+ }
44
+ #shopify-section-{{ section.id }} .progress-line {
45
+ height: 1px;
46
+ background-color: #FFFFFF;
47
+ transition: width 0.2s;
48
+ }
49
+
50
+ #shopify-section-{{ section.id }} .zz-swiper-gallery-center .swiper-slide .media-box {
51
+ width: 100% !important;
52
+ aspect-ratio: 375 / 690;
53
+ }
54
+ }
55
+
56
+ @media screen and (min-width: 1024px) {
57
+ #shopify-section-{{ section.id }} {
58
+ padding-top: {{ section.settings.pc_padding_top }}px;
59
+ padding-bottom: {{ section.settings.pc_padding_bottom }}px;
60
+ }
61
+ #shopify-section-{{ section.id }} .progress-div {
62
+ position: absolute;
63
+ bottom: 0px;
64
+ left: 8px;
65
+ right: 8px;
66
+ height: 2px;
67
+ z-index: 20;
68
+ }
69
+ #shopify-section-{{ section.id }} .progress-line {
70
+ height: 2px;
71
+ background-color: #FFFFFF;
72
+ transition: width 0.2s;
73
+ }
74
+ #shopify-section-{{ section.id }} .pagination-item-active .bullet-desc {
75
+ display: block !important;
76
+ }
77
+ #shopify-section-{{ section.id }} .pagination-item-active .pagination-item-bg {
78
+ background: #00000087;
79
+ }
80
+ #shopify-section-{{ section.id }} .center-title-content {
81
+ top: 85px;
82
+ }
83
+ #shopify-section-{{ section.id }} .pagination-item-active {
84
+ width: 556px !important;
85
+ }
86
+
87
+ }
88
+
89
+
90
+
91
+
92
+ </style>
93
+
94
+ <div
95
+ {% if section.settings.module_id != blank %}
96
+ data-track-zz-section="{{ section.settings.module_id }}"
97
+ {% endif %}
98
+ {% if section.settings.is_exposure != false %}
99
+ data-track-zz-exposure="true"
100
+ {% endif %}
101
+ {% if section.settings.module_name != blank %}data-zz-module-name="{{ section.settings.module_name }}"{% endif %}
102
+
103
+ >
104
+
105
+ <div class='zz-swiper-gallery-center tw-relative tw-w-screen zz-swiper-gallery-center-{{ section.id }}'>
106
+ <div class='swiper-wrapper'>
107
+ {% for banner in swiper_blocks %}
108
+ <div class='swiper-slide'>
109
+ <div class='media-box tw-w-screen lg:tw-h-screen lg:tw-min-h-[700px] tw-relative tw-overflow-hidden'>
110
+ {% if banner.settings.video_pc != blank or banner.settings.video_url_pc != blank %}
111
+ {% if banner.settings.video_url_pc != blank %}
112
+ {% assign pc_video = banner.settings.video_url_pc %}
113
+ {% else %}
114
+ {% assign pc_video = banner.settings.video_pc %}
115
+ {% endif %}
116
+ {% render 'zz-video',
117
+ pc_video: pc_video,
118
+ pc_poster: banner.settings.poster_pc,
119
+ mb_video: banner.settings.video_mb,
120
+ mb_poster: banner.settings.poster_mb,
121
+ lazy: true,
122
+ class: 'tw-w-full tw-h-full tw-object-cover',
123
+ autoplay: true,
124
+ loop: false,
125
+ muted: true
126
+ %}
127
+ {% else %}
128
+ {%- if banner.settings.poster_pc != blank -%}
129
+ {%
130
+ render 'zz-img',
131
+ pc_image: banner.settings.poster_pc,
132
+ mb_image: banner.settings.poster_mb | default: banner.settings.poster_pc,
133
+ image_alt: banner.settings.slide_title | escape,
134
+ %}
135
+ {% endif %}
136
+ {% endif %}
137
+ <div class="mb-desc tw-absolute tw-bottom-0 tw-h-[100px] lg:tw-hidden max-lg:tw-px-[20px] tw-overflow-y-auto
138
+ tw-leading-[1.2] tw-text-white">{{ banner.settings.slide_content }}</div>
139
+ </div>
140
+ </div>
141
+ {% endfor %}
142
+
143
+ </div>
144
+
145
+ <!-- Pagination indicator -->
146
+ <div class='custom-pagination zz-overflow-scrollbar-hidden tw-px-[20px] lg:tw-px-[80px] tw-z-10 tw-w-full tw-absolute tw-left-0 tw-bottom-[122px] lg:tw-bottom-[20px] tw-flex tw-flex-nowrap tw-overflow-x-auto tw-overflow-y-hidden tw-justify-start lg:tw-justify-center tw-gap-[15px]'>
147
+ {% for block in swiper_blocks %}
148
+ <div class="pagination-item tw-cursor-pointer tw-text-white tw-relative tw-rounded-[10px] tw-w-[196px] tw-h-[95px] lg:tw-w-[389px] lg:tw-h-[213px]"
149
+ {% if section.settings.module_id != blank %} data-track-zz-element="{{ section.settings.module_id }}_change" {% endif %}
150
+ style="background-image:url({{ block.settings.nav_image | image_url: width:800 }});">
151
+ <div class="pagination-item-bg tw-w-full tw-h-full max-lg:tw-p-[10px] lg:tw-py-[10px] lg:tw-px-[16px]">
152
+ <div class="bullet-title tw-text-left tw-text-[16px] tw-leading-[1.5] lg:tw-text-[21px] tw-font-bold tw-mb-[10px] lg:tw-mb-[16px] tw-flex">
153
+ {{ block.settings.slide_title }}
154
+ </div>
155
+ <div class="bullet-desc tw-hidden tw-text-left tw-text-[10px] lg:tw-text-[16px] tw-leading-[1.4]">{{ block.settings.slide_content }}</div>
156
+ <div class="progress-div">
157
+ <div class="progress-line tw-z-30">&nbsp;</div>
158
+ </div>
159
+ </div>
160
+
161
+
162
+ </div>
163
+ {% endfor %}
164
+
165
+ </div>
166
+
167
+ {% comment %} 居中对齐 {% endcomment %}
168
+ <div class="center-title-content tw-absolute tw-z-10">
169
+ {% render 'zz-h4',
170
+ title: section.settings.pc_title,
171
+ title_mb: section.settings.mb_title,
172
+ title_color: section.settings.text_color,
173
+ class_name: 'content-title tw-text-center tw-mb-[18px]',
174
+ %}
175
+
176
+ {% assign content_color = section.settings.text_color
177
+ | color_modify: 'alpha', 0.7
178
+ %}
179
+
180
+ {% render 'zz-content-text',
181
+ text: section.settings.description_pc,
182
+ mb_text: section.settings.description_mb,
183
+ text_color: content_color,
184
+ pc_font_size: 16,
185
+ mobile_font_size: 12,
186
+ pc_text_align: 'center',
187
+ mobile_text_align: 'center',
188
+ class_name: 'max-lg:!tw-text-white'
189
+ %}
190
+ </div>
191
+
192
+
193
+ </div>
194
+
195
+
196
+
197
+
198
+
199
+ </div>
200
+
201
+ <script>
202
+ document.addEventListener('DOMContentLoaded', function () {
203
+ const section = document.getElementById('shopify-section-{{section.id}}');
204
+
205
+ // 获取所有pagination-item 监听点击事件,点击后swiper切换到对应的slide
206
+ const paginationItems = section.querySelectorAll('.pagination-item');
207
+ paginationItems.forEach((item, index) => {
208
+ item.addEventListener('click', () => {
209
+ prevSwiper.slideToLoop(index);
210
+ });
211
+ });
212
+
213
+
214
+ const swiperClassName = '.zz-swiper-gallery-center-{{ section.id }}';
215
+ let isDesktop = window.innerWidth > 1023;
216
+ // 获取 class zz-accessories-swiper 的元素 且data-id 为 section.id 的元素
217
+ const zzAccessoriesSwiper = document.querySelector(swiperClassName);
218
+ const swiperWrapper = zzAccessoriesSwiper.querySelector('.swiper-wrapper');
219
+
220
+ let progressInterval = null;
221
+ let progressTimer = null;
222
+ const prevSwiper = new Swiper(swiperClassName, {
223
+ loop: true,
224
+ slidesPerView: 1,
225
+ centeredSlides: true,
226
+ spaceBetween: isDesktop ? 30 : 8,
227
+ effect: 'fade',
228
+ on: {
229
+ slideChangeTransitionEnd: function () {
230
+ handleVideoOnSlideChange();
231
+ },
232
+ slideChangeTransitionStart: function () {
233
+ if (progressInterval) {
234
+ clearInterval(progressInterval);
235
+ }
236
+ if (progressTimer) {
237
+ clearTimeout(progressTimer);
238
+ }
239
+ },
240
+ slideChange: function () {
241
+ updateNavActive(this.realIndex);
242
+ if (!isDesktop) {
243
+ scrollPaginationToCenter();
244
+ }
245
+ section
246
+ .querySelectorAll('.pagination-item')
247
+ .forEach((bullet) => {
248
+ const progressBar = bullet.querySelector('.progress-line');
249
+ progressBar.style.width = '0';
250
+ });
251
+ },
252
+ },
253
+ });
254
+
255
+ // 使用 Intersection Observer 监听 swiper-wrapper 的可见性
256
+
257
+ const observer = new IntersectionObserver(
258
+ (entries) => {
259
+ entries.forEach((entry) => {
260
+ if (entry.isIntersecting) {
261
+ // 当 swiper-wrapper 第一次出现在视口中时执行
262
+ setTimeout(() => {
263
+ handleVideoOnSlideChange();
264
+ }, 200);
265
+ // 执行一次后取消观察
266
+ observer.unobserve(entry.target);
267
+ }
268
+ });
269
+ },
270
+ {
271
+ threshold: 0.2, // 当 20% 的元素可见时触发
272
+ }
273
+ );
274
+
275
+ if (swiperWrapper) {
276
+ observer.observe(swiperWrapper);
277
+ }
278
+
279
+ const endedHandler = function () {
280
+
281
+ if (prevSwiper.isEnd) {
282
+ prevSwiper.slideTo(0);
283
+ } else {
284
+ prevSwiper.slideNext();
285
+ }
286
+ if (progressInterval) {
287
+ clearInterval(progressInterval);
288
+ }
289
+ this.removeEventListener('ended', endedHandler);
290
+ };
291
+
292
+ function scrollPaginationToCenter() {
293
+ // 获取当前section
294
+ const section = document.getElementById(
295
+ 'shopify-section-{{section.id}}'
296
+ );
297
+ const paginationContainer = section.querySelector('.custom-pagination');
298
+ const activeBullet = paginationContainer.querySelector('.pagination-item-active');
299
+
300
+ if (activeBullet) {
301
+ const containerWidth = paginationContainer.offsetWidth;
302
+ const bulletLeft = activeBullet.offsetLeft;
303
+ const bulletWidth = activeBullet.offsetWidth;
304
+
305
+ // 计算需要滚动的位置,使当前bullet居中
306
+ const scrollPosition = bulletLeft - (containerWidth / 2) + (bulletWidth / 2);
307
+
308
+ paginationContainer.scrollTo({
309
+ left: scrollPosition,
310
+ behavior: 'smooth'
311
+ });
312
+ }
313
+ }
314
+
315
+
316
+ function updataMediaProgress(type, videoDom) {
317
+ const activeBullet = section.querySelector(
318
+ '.pagination-item-active'
319
+ );
320
+ const progressBar = activeBullet.querySelector('.progress-line');
321
+ if (progressBar) {
322
+ if (progressInterval) {
323
+ clearInterval(progressInterval);
324
+ }
325
+ if (progressTimer) {
326
+ clearTimeout(progressTimer);
327
+ }
328
+ if (type === 'video') {
329
+ // 添加新的事件监听器
330
+
331
+ progressInterval = setInterval(() => {
332
+ const progress = videoDom.currentTime / videoDom.duration;
333
+ progressBar.style.width = `${progress * 100}%`;
334
+ }, 100);
335
+
336
+
337
+ videoDom.addEventListener('ended', endedHandler);
338
+ } else if (type === 'image') {
339
+
340
+ progressBar.style.transition = '10s linear';
341
+ progressBar.style.width = '100%';
342
+
343
+ progressTimer = setTimeout(() => {
344
+ if (prevSwiper.isEnd) {
345
+ prevSwiper.slideTo(0);
346
+ } else {
347
+ prevSwiper.slideNext();
348
+ }
349
+ }, 10000);
350
+ }
351
+ }
352
+ }
353
+
354
+ function handleVideoOnSlideChange() {
355
+
356
+ const allvideo = section.querySelectorAll('video');
357
+ // 播放当前 active slide 中的视频
358
+ const activeSlide = section.querySelector('.swiper-slide-active');
359
+ const media = getVisibleDisplayMedias(activeSlide);
360
+ // 重置进度条
361
+ const activeBullet = section.querySelector(
362
+ '.pagination-item-active'
363
+ );
364
+ const progressBar = activeBullet.querySelector('.progress-line');
365
+ if (progressBar) {
366
+ progressBar.style.width = '0';
367
+ }
368
+ if (media.type === 'video') {
369
+ allvideo.forEach((item) => {
370
+ if (item === media.video) {
371
+ media.video.currentTime = 0;
372
+ if (media.video.paused) {
373
+ media.video.play();
374
+ }
375
+
376
+ updataMediaProgress('video', media.video);
377
+ } else {
378
+ item.pause();
379
+ }
380
+ });
381
+ } else if (media.type === 'image') {
382
+ // 处理图片的情况
383
+ updataMediaProgress('image', media.image);
384
+ allvideo.forEach((item) => {
385
+ item.pause();
386
+ });
387
+ }
388
+ }
389
+
390
+
391
+ // 更新当前高亮的导航
392
+ function updateNavActive(activeIndex) {
393
+ paginationItems.forEach((item, i) => {
394
+ item.classList.toggle('pagination-item-active', i === activeIndex);
395
+ });
396
+ }
397
+
398
+
399
+ });
400
+ </script>
401
+
402
+ {% schema %}
403
+ {
404
+ "name": "Swiper Gallery Center",
405
+ "class": "zz-swiper-gallery-center",
406
+ "settings": [
407
+ {
408
+ "type": "color_background",
409
+ "id": "bg_color",
410
+ "default": "#ffffff",
411
+ "label": "背景色"
412
+ },
413
+ {
414
+ "type": "richtext",
415
+ "id": "pc_title",
416
+ "label": "PC标题内容 (PC Title Content)",
417
+ "default": "<p>这里是标题</p>"
418
+ },
419
+ {
420
+ "type": "richtext",
421
+ "id": "mb_title",
422
+ "label": "移动标题内容 (Mobile Title Content)"
423
+ },
424
+ {
425
+ "type": "color",
426
+ "id": "text_color",
427
+ "label": "文字颜色",
428
+ "default": "#000000"
429
+ },
430
+ {
431
+ "type": "richtext",
432
+ "id": "description_pc",
433
+ "label": "Description PC"
434
+ },
435
+ {
436
+ "type": "richtext",
437
+ "id": "description_mb",
438
+ "label": "Description Mobile"
439
+ },
440
+ {
441
+ "type": "number",
442
+ "id": "pc_padding_top",
443
+ "label": "PC上内边距 (Padding Top)",
444
+ "default": 80
445
+ },
446
+ {
447
+ "type": "number",
448
+ "id": "pc_padding_bottom",
449
+ "label": "PC下内边距 (Padding Bottom)",
450
+ "default": 80
451
+ },
452
+ {
453
+ "type": "number",
454
+ "id": "mobile_padding_top",
455
+ "label": "移动上内边距 (Padding Top)",
456
+
457
+ "default": 40
458
+ },
459
+ {
460
+ "type": "number",
461
+ "id": "mobile_padding_bottom",
462
+ "label": "移动下内边距 (Padding Bottom)",
463
+ "default": 40
464
+ },
465
+ {
466
+ "type": "header",
467
+ "content": "统计相关"
468
+ },
469
+ {
470
+ "type": "text",
471
+ "id": "module_id",
472
+ "label": "唯一业务id",
473
+ "info": "需要唯一不要与其他模块重复,用户定位、埋点统计、代码隐藏控制等"
474
+ },
475
+ {
476
+ "type": "checkbox",
477
+ "id": "is_exposure",
478
+ "label": "是否曝光统计",
479
+ "default": false,
480
+ "info": "勾选后会对section模块进行曝光统计埋点, 必须填写唯一业务id"
481
+ },
482
+ {
483
+ "type": "text",
484
+ "id": "module_name",
485
+ "label": "模块定位id",
486
+ "info": "模块定位"
487
+ }
488
+ ],
489
+ "blocks": [
490
+ {
491
+ "name": "Video",
492
+ "type": "video",
493
+ "settings": [
494
+ {
495
+ "type": "text",
496
+ "id": "slide_title",
497
+ "label": "标题"
498
+ },
499
+ {
500
+ "type": "text",
501
+ "id": "video_url_pc",
502
+ "label": "Video url PC",
503
+ "info": "使用cdn视频时,填写视频url。防止视频模糊"
504
+ },
505
+ {
506
+ "type": "video",
507
+ "id": "video_pc",
508
+ "label": "Video"
509
+ },
510
+ {
511
+ "type": "image_picker",
512
+ "id": "poster_pc",
513
+ "label": "视频封面 PC 没有视频时显示封面"
514
+ },
515
+ {
516
+ "type": "video",
517
+ "id": "video_mb",
518
+ "label": "Video Mobile"
519
+ },
520
+ {
521
+ "type": "image_picker",
522
+ "id": "poster_mb",
523
+ "label": "视频封面 Mobile"
524
+ },
525
+
526
+ {
527
+ "type": "image_picker",
528
+ "id": "nav_image",
529
+ "label": "导航小图",
530
+ "info": "分页导航小图,建议尺寸 556x213px,未选中时展示中间区域,PC端选中展示完整图片"
531
+ },
532
+
533
+ {
534
+ "type": "richtext",
535
+ "id": "slide_content",
536
+ "label": "内容"
537
+ }
538
+
539
+ ]
540
+ }
541
+ ],
542
+ "presets": [
543
+ {
544
+ "name": "Swiper Gallery Center",
545
+ "category": "Swiper Section"
546
+ },
547
+ ]
548
+ }
549
+ {% endschema %}