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

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.
@@ -0,0 +1,625 @@
1
+
2
+ {% assign non_collapsible_blocks = section.blocks
3
+ | reject: 'type', 'video'
4
+ %}
5
+ {% assign swiper_blocks = section.blocks | where: 'type', 'video' %}
6
+
7
+
8
+ <style>
9
+ #shopify-section-{{ section.id }} {
10
+ background: {{ section.settings.bg_color }};
11
+ display: flex;
12
+ flex-direction: column;
13
+ align-items: center;
14
+ padding-top: {{ section.settings.mobile_padding_top }}px;
15
+ padding-bottom: {{ section.settings.mobile_padding_bottom }}px;
16
+ }
17
+
18
+ #shopify-section-{{ section.id }} .bullet-title , #shopify-section-{{ section.id }} .bullet-desc {
19
+ text-align: left;
20
+ font-weight: 400 !important;
21
+ color: #FFFFFF80;
22
+
23
+ }
24
+ #shopify-section-{{ section.id }} .zz-video-progress-swiper .swiper-pagination-bullets {
25
+ display: flex;
26
+ gap: 16px;
27
+ justify-content: space-between;
28
+ top: 0px;
29
+ left: 0px;
30
+ right:0px;
31
+ bottom: unset;
32
+ height: fit-content;
33
+ }
34
+
35
+
36
+ #shopify-section-{{ section.id }} .zz-video-progress-swiper .swiper-pagination-bullets .swiper-pagination-bullet {
37
+ width: fit-content;
38
+ box-sizing: border-box;
39
+ margin: 0;
40
+ height: fit-content;
41
+ position: relative;
42
+ border-bottom: 2px solid #FFFFFF4F !important;
43
+ padding-bottom: 23px;
44
+ border-radius: unset;
45
+ flex: 1;
46
+ opacity: 1;
47
+ }
48
+ #shopify-section-{{ section.id }} .swiper-pagination-bullet .bullet-title {
49
+ white-space: nowrap;
50
+ }
51
+ #shopify-section-{{ section.id }} .swiper-pagination-bullet .progress-line {
52
+ position: absolute;
53
+ bottom: -2px;
54
+ left: 0;
55
+ height: 2px;
56
+ width: 0;
57
+ background-color: #FFFFFF;
58
+ transition: width 0.2s;
59
+ }
60
+ #shopify-section-{{ section.id }} .bullet-normal-icon {
61
+ display: inline-block;
62
+ }
63
+ #shopify-section-{{ section.id }} .swiper-pagination-bullets .swiper-pagination-bullet {
64
+ background: none;
65
+ }
66
+ #shopify-section-{{ section.id }} .swiper-pagination-bullet-active .bullet-normal-icon {
67
+ display: none !important;
68
+ }
69
+ #shopify-section-{{ section.id }} .swiper-pagination-bullet-active .bullet-active-icon {
70
+ display: inline-block !important;
71
+ }
72
+ #shopify-section-{{ section.id }} .swiper-pagination-bullet-active .bullet-title {
73
+ color: #FFFFFF;
74
+ font-weight: 700 !important;
75
+ }
76
+ #shopify-section-{{ section.id }} .swiper-pagination-bullet-active .bullet-desc{
77
+ color: #FFFFFF;
78
+ }
79
+
80
+
81
+ @media screen and (min-width: 1024px) {
82
+ #shopify-section-{{ section.id }} {
83
+ padding-top: {{ section.settings.pc_padding_top }}px;
84
+ padding-bottom: {{ section.settings.pc_padding_bottom }}px;
85
+ }
86
+ #shopify-section-{{ section.id }} .zz-video-progress-swiper .swiper-pagination-bullets .swiper-pagination-bullet {
87
+ padding-bottom: 30px;
88
+ }
89
+ }
90
+ @media screen and (max-width: 1024px) {
91
+ #shopify-section-{{ section.id }} {
92
+ padding-left: 20px;
93
+ padding-right: 20px;
94
+ }
95
+
96
+ #shopify-section-{{ section.id }} .content-title {
97
+ margin-bottom: var(--zz-video-progress-title-mb-mb);
98
+ }
99
+
100
+ #shopify-section-{{ section.id }} .zz-video-progress-swiper {
101
+ width: 100%;
102
+
103
+ }
104
+ #shopify-section-{{ section.id }} .zz-video-progress-swiper .swiper-slide {
105
+ width: 100% !important;
106
+ }
107
+ #shopify-section-{{ section.id }} .zz-video-progress-swiper .swiper-slide .media-box {
108
+ position: relative;
109
+ width: 100%;
110
+ aspect-ratio: 335 / 252;
111
+ }
112
+ #shopify-section-{{ section.id }} .zz-video-progress-swiper .swiper-pagination-bullets {
113
+ overflow-y: hidden;
114
+ top: 0px;
115
+ left: 0px;
116
+ }
117
+ #shopify-section-{{ section.id }} .swiper-pagination-bullet {
118
+ min-width:190px;
119
+ }
120
+ #shopify-section-{{ section.id }} .swiper-pagination-horizontal {
121
+ overflow-x: auto; /* 启用横向滚动 */
122
+ -webkit-overflow-scrolling: touch; /* 移动端流畅滚动(可选) */
123
+ scrollbar-width: none; /* Firefox */
124
+ -ms-overflow-style: none; /* IE 和 Edge */
125
+ }
126
+ #shopify-section-{{ section.id }} .swiper-pagination-horizontal::-webkit-scrollbar {
127
+ display: none; /* Chrome, Safari */
128
+ }
129
+ }
130
+ @media screen and (min-width: 1024px) {
131
+ #shopify-section-{{ section.id }} .content-title {
132
+ margin-bottom: var(--zz-video-progress-title-pc-mb);
133
+ }
134
+ #shopify-section-{{ section.id }} .zz-video-progress-swiper {
135
+ width: 90vw;
136
+ }
137
+ #shopify-section-{{ section.id }} .zz-video-progress-swiper .swiper-slide {
138
+ width: 90vw;
139
+ }
140
+ #shopify-section-{{ section.id }} .zz-video-progress-swiper .swiper-slide .media-box {
141
+ position: relative;
142
+ aspect-ratio: 16 / 9;
143
+ }
144
+ #shopify-section-{{ section.id }} .zz-video-progress-swiper .swiper-pagination-bullets {
145
+ top: 0px;
146
+ left: 0px;
147
+ width: 100%;
148
+
149
+ }
150
+ #shopify-section-{{ section.id }} .zz-video-progress-swiper .swiper-pagination-bullets .swiper-pagination-bullet {
151
+ box-sizing: border-box;
152
+ margin: 0;
153
+
154
+ }
155
+ #shopify-section-{{ section.id }} .zz-video-progress-swiper .swiper-pagination-bullets .swiper-pagination-bullet-active {
156
+ font-weight: 700;
157
+ }
158
+
159
+ }
160
+ @media screen and (min-width: 1440px) {
161
+ #shopify-section-{{ section.id }} .zz-video-progress-swiper , #shopify-section-{{ section.id }} .zz-video-progress-swiper .swiper-slide {
162
+ width: 1220px;
163
+ }
164
+ #shopify-section-{{ section.id }} .zz-video-progress-swiper .swiper-slide .media-box {
165
+ position: relative;
166
+ width: 1220px;
167
+ aspect-ratio: 16 / 9;
168
+
169
+ }
170
+ }
171
+
172
+
173
+
174
+
175
+ </style>
176
+
177
+ <div
178
+ {% if section.settings.module_id != blank %}
179
+ data-track-zz-section="{{ section.settings.module_id }}"
180
+ {% endif %}
181
+ {% if section.settings.is_exposure != false %}
182
+ data-track-zz-exposure="true"
183
+ {% endif %}
184
+ {% if section.settings.module_name != blank %}data-zz-module-name="{{ section.settings.module_name }}"{% endif %}
185
+
186
+ >
187
+ <div class="video-progress-title-desc fade-in-box tw-flex tw-flex-col tw-justify-center">
188
+ {% for block in non_collapsible_blocks %}
189
+ {% case block.type %}
190
+ {% when 'title' %}
191
+ <div style="--zz-video-progress-title-mb-mb: {{ block.settings.mobile_margin_bottom }}px;
192
+ --zz-video-progress-title-pc-mb: {{ block.settings.pc_margin_bottom }}px;">
193
+ {% render 'zz-h2',
194
+ title: block.settings.pc_title,
195
+ title_mb: block.settings.mb_title,
196
+ title_color: block.settings.text_color,
197
+ class_name: 'content-title tw-text-center',
198
+ %}
199
+
200
+ </div>
201
+
202
+ {% endcase %}
203
+ {% endfor %}
204
+ </div>
205
+ <div class='zz-video-progress-swiper lg:tw-pt-[166px] tw-pt-[146px] tw-relative zz-video-progress-swiper-{{ section.id }}'>
206
+ <div class='swiper-wrapper'>
207
+ {% for banner in swiper_blocks %}
208
+ <div class='swiper-slide'>
209
+ <div class='media-box tw-rounded-[10px] lg:tw-rounded-[16px] tw-overflow-hidden'>
210
+ {% if banner.settings.video_pc != blank or banner.settings.video_url_pc != blank %}
211
+ {% if banner.settings.video_url_pc != blank %}
212
+ {% assign pc_video = banner.settings.video_url_pc %}
213
+ {% else %}
214
+ {% assign pc_video = banner.settings.video_pc %}
215
+ {% endif %}
216
+ {% render 'zz-video',
217
+ pc_video: pc_video,
218
+ pc_poster: banner.settings.poster_pc,
219
+ mb_video: banner.settings.video_mb,
220
+ mb_poster: banner.settings.poster_mb,
221
+ lazy: true,
222
+ class: 'tw-w-full tw-h-full tw-object-cover',
223
+ autoplay: true,
224
+ loop: false,
225
+ muted: true
226
+ %}
227
+ {% else %}
228
+ {%- if banner.settings.poster_pc != blank -%}
229
+ {%
230
+ render 'zz-img',
231
+ pc_image: banner.settings.poster_pc,
232
+ mb_image: banner.settings.poster_mb | default: banner.settings.poster_pc,
233
+ image_alt: banner.settings.slide_title | escape,
234
+ %}
235
+ {% endif %}
236
+ {% endif %}
237
+ </div>
238
+ </div>
239
+ {% endfor %}
240
+
241
+ </div>
242
+
243
+ <!-- Pagination indicator -->
244
+ <div class='swiper-pagination'></div>
245
+
246
+
247
+ </div>
248
+ </div>
249
+
250
+ <script>
251
+ document.addEventListener('DOMContentLoaded', function () {
252
+ const swiperClassName = '.zz-video-progress-swiper-{{ section.id }}';
253
+ let isDesktop = window.innerWidth > 1023;
254
+ // 获取 class zz-accessories-swiper 的元素 且data-id 为 section.id 的元素
255
+ const zzAccessoriesSwiper = document.querySelector(swiperClassName);
256
+ const swiperWrapper = zzAccessoriesSwiper.querySelector('.swiper-wrapper');
257
+
258
+ let progressInterval = null;
259
+ let progressTimer = null;
260
+
261
+ const slideTitles = [
262
+ {% for block in swiper_blocks %}
263
+ "{{ block.settings.slide_title }}",
264
+ {% endfor %}
265
+ ];
266
+
267
+ const slideDescs = [
268
+ {% for block in swiper_blocks %}
269
+ "{{ block.settings.slide_content | strip_html | escape }}",
270
+ {% endfor %}
271
+ ];
272
+
273
+ const prevSwiper = new Swiper(swiperClassName, {
274
+ loop: false,
275
+ slidesPerView: 1,
276
+ centeredSlides: true,
277
+ spaceBetween: isDesktop ? 30 : 8,
278
+ centeredSlides: isDesktop ? false: true,
279
+ pagination: {
280
+ el: '.swiper-pagination',
281
+ clickable: true,
282
+ renderBullet: function (index, className) {
283
+ const title = slideTitles[index % slideTitles.length];
284
+ const desc = slideDescs[index % slideDescs.length];
285
+ return `<div class="${className}" {% if section.settings.module_id != blank %} data-track-zz-element="{{ section.settings.module_id }}_change" {% endif %}>
286
+ <div class="bullet-title tw-text-left tw-text-[14px] lg:tw-text-[24px] tw-mb-[10px] lg:tw-mb-[16px] tw-flex tw-items-center tw-gap-[10px]">
287
+ <span class="bullet-active-icon tw-hidden tw-w-[17px] tw-h-[17px] lg:tw-w-[32px] lg:tw-h-[32px]">
288
+ <img class="tw-w-full tw-h-full tw-object-contain" src="{{ section.settings.active_icon | image_url: width: 40 }}" alt="Specs Header" width="40" height="auto">
289
+
290
+ </span>
291
+ <span class="bullet-normal-icon tw-w-[10px] tw-h-[17px] lg:tw-w-[14px] lg:tw-h-[32px]">
292
+ <svg class="tw-w-full tw-h-full" viewBox="0 0 14 32" fill="none" xmlns="http://www.w3.org/2000/svg">
293
+ <path d="M13.572 15.1499L1.81194 6.23102C1.47494 5.97525 1.01596 5.92788 0.630426 6.10883C0.244728 6.28975 0 6.66731 0 7.08114V24.9189C0 25.3327 0.244728 25.7103 0.630426 25.8912C0.785849 25.9642 0.953352 26 1.11986 26C1.36622 26 1.61081 25.9216 1.81194 25.769L13.5719 16.8502C13.8422 16.6451 14 16.3318 14 16C14 15.6683 13.8422 15.355 13.572 15.1499ZM2.24003 22.6946V9.30547L11.0671 16L2.24003 22.6946Z" fill="white" fill-opacity="0.3"/>
294
+ </svg>
295
+
296
+ </span>
297
+ ${title}
298
+ </div>
299
+ <div class="bullet-desc tw-text-left tw-text-[10px] lg:tw-text-[16px] tw-leading-[1.5]">${desc}</div>
300
+ <span class="progress-line"></span>
301
+ </div>`;
302
+ }
303
+ },
304
+ effect: isDesktop ? 'fade' : 'slide',
305
+ on: {
306
+ slideChangeTransitionEnd: function () {
307
+ handleVideoOnSlideChange();
308
+ },
309
+ slideChangeTransitionStart: function () {
310
+ if (progressInterval) {
311
+ clearInterval(progressInterval);
312
+ }
313
+ if (progressTimer) {
314
+ clearTimeout(progressTimer);
315
+ }
316
+ },
317
+ slideChange: function () {
318
+ if (!isDesktop) {
319
+ scrollPaginationToCenter();
320
+ }
321
+ const section = document.getElementById('shopify-section-{{section.id}}');
322
+ section
323
+ .querySelectorAll('.swiper-pagination-bullet')
324
+ .forEach((bullet) => {
325
+ const progressBar = bullet.querySelector('.progress-line');
326
+ progressBar.style.transition = 'none';
327
+ progressBar.style.width = '0';
328
+ });
329
+ },
330
+ },
331
+ });
332
+
333
+ // 使用 Intersection Observer 监听 swiper-wrapper 的可见性
334
+
335
+ const observer = new IntersectionObserver(
336
+ (entries) => {
337
+ entries.forEach((entry) => {
338
+ if (entry.isIntersecting) {
339
+ // 当 swiper-wrapper 第一次出现在视口中时执行
340
+ setTimeout(() => {
341
+ handleVideoOnSlideChange();
342
+ }, 200);
343
+ // 执行一次后取消观察
344
+ observer.unobserve(entry.target);
345
+ }
346
+ });
347
+ },
348
+ {
349
+ threshold: 0.2, // 当 20% 的元素可见时触发
350
+ }
351
+ );
352
+
353
+ if (swiperWrapper) {
354
+ observer.observe(swiperWrapper);
355
+ }
356
+
357
+ const endedHandler = function () {
358
+
359
+ if (prevSwiper.isEnd) {
360
+ prevSwiper.slideTo(0);
361
+ } else {
362
+ prevSwiper.slideNext();
363
+ }
364
+ if (progressInterval) {
365
+ clearInterval(progressInterval);
366
+ }
367
+ this.removeEventListener('ended', endedHandler);
368
+ };
369
+
370
+ function scrollPaginationToCenter() {
371
+ // 获取当前section
372
+ const section = document.getElementById(
373
+ 'shopify-section-{{section.id}}'
374
+ );
375
+ const paginationContainer = section.querySelector('.swiper-pagination');
376
+ const activeBullet = paginationContainer.querySelector('.swiper-pagination-bullet-active');
377
+
378
+ if (activeBullet) {
379
+ const containerWidth = paginationContainer.offsetWidth;
380
+ const bulletLeft = activeBullet.offsetLeft;
381
+ const bulletWidth = activeBullet.offsetWidth;
382
+
383
+ // 计算需要滚动的位置,使当前bullet居中
384
+ const scrollPosition = bulletLeft - (containerWidth / 2) + (bulletWidth / 2);
385
+
386
+ paginationContainer.scrollTo({
387
+ left: scrollPosition,
388
+ behavior: 'smooth'
389
+ });
390
+ }
391
+ }
392
+
393
+
394
+ function updataMediaProgress(type, videoDom) {
395
+ const section = document.getElementById('shopify-section-{{section.id}}');
396
+ const activeBullet = section.querySelector(
397
+ '.swiper-pagination-bullet-active'
398
+ );
399
+ const progressBar = activeBullet.querySelector('.progress-line');
400
+ if (progressBar) {
401
+ if (progressInterval) {
402
+ clearInterval(progressInterval);
403
+ }
404
+ if (progressTimer) {
405
+ clearTimeout(progressTimer);
406
+ }
407
+ if (type === 'video') {
408
+ // 添加新的事件监听器
409
+ progressInterval = setInterval(() => {
410
+ const progress = videoDom.currentTime / videoDom.duration;
411
+ progressBar.style.transition = '0.2s';
412
+ progressBar.style.width = `${progress * 100}%`;
413
+ }, 100);
414
+ videoDom.addEventListener('ended', endedHandler);
415
+ } else if (type === 'image') {
416
+
417
+ progressBar.style.transition = '10s linear';
418
+ progressBar.style.width = '100%';
419
+
420
+ progressTimer = setTimeout(() => {
421
+ if (prevSwiper.isEnd) {
422
+ prevSwiper.slideTo(0);
423
+ } else {
424
+ prevSwiper.slideNext();
425
+ }
426
+ }, 10000);
427
+ }
428
+ }
429
+ }
430
+
431
+ function handleVideoOnSlideChange() {
432
+ // 暂停所有视频
433
+ const section = document.getElementById('shopify-section-{{section.id}}');
434
+ const allvideo = section.querySelectorAll('video');
435
+ // 播放当前 active slide 中的视频
436
+ const activeSlide = section.querySelector('.swiper-slide-active');
437
+ const media = getVisibleDisplayMedias(activeSlide);
438
+ // 重置进度条
439
+ const activeBullet = section.querySelector(
440
+ '.swiper-pagination-bullet-active'
441
+ );
442
+ const progressBar = activeBullet.querySelector('.progress-line');
443
+ if (progressBar) {
444
+ progressBar.style.transition = 'none';
445
+ progressBar.style.width = '0';
446
+ }
447
+ if (media.type === 'video') {
448
+ allvideo.forEach((item) => {
449
+ if (item === media.video) {
450
+ media.video.currentTime = 0;
451
+ if (media.video.paused) {
452
+ media.video.play();
453
+ }
454
+
455
+ updataMediaProgress('video', media.video);
456
+ } else {
457
+ item.pause();
458
+ }
459
+ });
460
+ } else if (media.type === 'image') {
461
+ // 处理图片的情况
462
+ updataMediaProgress('image', media.image);
463
+ allvideo.forEach((item) => {
464
+ item.pause();
465
+ });
466
+ }
467
+ }
468
+ });
469
+ </script>
470
+
471
+ {% schema %}
472
+ {
473
+ "name": "Video Progress Swiper",
474
+ "class": "zz-video-progress-swiper-section",
475
+ "settings": [
476
+ {
477
+ "type": "color_background",
478
+ "id": "bg_color",
479
+ "default": "#ffffff",
480
+ "label": "背景色"
481
+ },
482
+ {
483
+ "type": "number",
484
+ "id": "pc_padding_top",
485
+ "label": "PC上内边距 (Padding Top)",
486
+ "default": 80
487
+ },
488
+ {
489
+ "type": "number",
490
+ "id": "pc_padding_bottom",
491
+ "label": "PC下内边距 (Padding Bottom)",
492
+ "default": 80
493
+ },
494
+ {
495
+ "type": "number",
496
+ "id": "mobile_padding_top",
497
+ "label": "移动上内边距 (Padding Top)",
498
+
499
+ "default": 40
500
+ },
501
+ {
502
+ "type": "number",
503
+ "id": "mobile_padding_bottom",
504
+ "label": "移动下内边距 (Padding Bottom)",
505
+ "default": 40
506
+ },
507
+ {
508
+ "type": "image_picker",
509
+ "id": "active_icon",
510
+ "label": "播放图标",
511
+ "info": "分页指示器激活状态下的图标"
512
+ },
513
+ {
514
+ "type": "header",
515
+ "content": "统计相关"
516
+ },
517
+ {
518
+ "type": "text",
519
+ "id": "module_id",
520
+ "label": "唯一业务id",
521
+ "info": "需要唯一不要与其他模块重复,用户定位、埋点统计、代码隐藏控制等"
522
+ },
523
+ {
524
+ "type": "checkbox",
525
+ "id": "is_exposure",
526
+ "label": "是否曝光统计",
527
+ "default": false,
528
+ "info": "勾选后会对section模块进行曝光统计埋点, 必须填写唯一业务id"
529
+ },
530
+ {
531
+ "type": "text",
532
+ "id": "module_name",
533
+ "label": "模块定位id",
534
+ "info": "模块定位"
535
+ }
536
+ ],
537
+ "blocks": [
538
+ {
539
+ "type": "title",
540
+ "name": "标题",
541
+ "settings": [
542
+ {
543
+ "type": "richtext",
544
+ "id": "pc_title",
545
+ "label": "PC标题内容 (PC Title Content)",
546
+ "default": "<p>这里是标题</p>"
547
+ },
548
+ {
549
+ "type": "richtext",
550
+ "id": "mb_title",
551
+ "label": "移动标题内容 (Mobile Title Content)"
552
+ },
553
+ {
554
+ "type": "color",
555
+ "id": "text_color",
556
+ "label": "文字颜色",
557
+ "default": "#000000"
558
+ },
559
+ {
560
+ "type": "number",
561
+ "id": "pc_margin_bottom",
562
+ "label": "PC下外边距 (Margin Bottom)",
563
+ "default": 40
564
+ },
565
+ {
566
+ "type": "number",
567
+ "id": "mobile_margin_bottom",
568
+ "label": "移动下外边距 (Margin Bottom)",
569
+ "default": 20
570
+ }
571
+ ]
572
+ },
573
+ {
574
+ "name": "Video",
575
+ "type": "video",
576
+ "settings": [
577
+ {
578
+ "type": "text",
579
+ "id": "slide_title",
580
+ "label": "标题"
581
+ },
582
+ {
583
+ "type": "text",
584
+ "id": "video_url_pc",
585
+ "label": "Video url PC",
586
+ "info": "使用cdn视频时,填写视频url。防止视频模糊"
587
+ },
588
+ {
589
+ "type": "video",
590
+ "id": "video_pc",
591
+ "label": "Video"
592
+ },
593
+ {
594
+ "type": "image_picker",
595
+ "id": "poster_pc",
596
+ "label": "视频封面 PC 没有视频时显示封面"
597
+ },
598
+ {
599
+ "type": "video",
600
+ "id": "video_mb",
601
+ "label": "Video Mobile"
602
+ },
603
+ {
604
+ "type": "image_picker",
605
+ "id": "poster_mb",
606
+ "label": "视频封面 Mobile"
607
+ },
608
+
609
+ {
610
+ "type": "richtext",
611
+ "id": "slide_content",
612
+ "label": "内容"
613
+ }
614
+
615
+ ]
616
+ }
617
+ ],
618
+ "presets": [
619
+ {
620
+ "name": "Video Progress Swiper",
621
+ "category": "Swiper Section"
622
+ },
623
+ ]
624
+ }
625
+ {% endschema %}