zz-shopify-components 0.16.2 → 0.16.3-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.
@@ -165,6 +165,103 @@ if (!customElements.get('zz-video-button')) {
165
165
  customElements.define('zz-video-button', ZZVideoBtn);
166
166
  }
167
167
 
168
+ if (!customElements.get('zz-video-popup')) {
169
+ class ZZVideoPopup extends HTMLElement {
170
+ constructor() {
171
+ super();
172
+ this.togglePopup = this.togglePopup.bind(this);
173
+ this.popup = null;
174
+ }
175
+
176
+
177
+ connectedCallback() {
178
+ this.querySelectorAll('.togglePopup').forEach((el) => {
179
+ el.addEventListener('click', (event) => {
180
+ console.log('click');
181
+ if (event.target.tagName !== 'VIDEO') {
182
+ this.togglePopup();
183
+ }
184
+ });
185
+ });
186
+ this.popup = this.querySelector('.popup');
187
+
188
+
189
+ }
190
+
191
+ disconnectedCallback() {
192
+ if (this.popup && document.body.contains(this.popup)) {
193
+ document.body.removeChild(this.popup); // 清理 popup 元素
194
+ }
195
+ }
196
+
197
+ togglePopup() {
198
+ if (!this.popup) return;
199
+ if (this.popup) {
200
+ // 将 popup 移动到 body
201
+ document.body.appendChild(this.popup);
202
+ } else {
203
+ console.error('Popup element not found.');
204
+ }
205
+
206
+ const isHidden = this.popup.classList.contains('!tw-hidden');
207
+
208
+ if (isHidden) {
209
+ this.showPopup();
210
+ } else {
211
+ this.hidePopup();
212
+ }
213
+ }
214
+
215
+ showPopup() {
216
+ if (!this.popup) return;
217
+
218
+ this.popup.classList.remove('!tw-hidden');
219
+ gsap.fromTo(
220
+ this.popup,
221
+ { opacity: 0 },
222
+ {
223
+ opacity: 1,
224
+ duration: 0.3,
225
+ ease: 'linear',
226
+ backdropFilter: 'blur(30px)',
227
+ onComplete: () => {
228
+ const videos = this.popup.querySelectorAll('video');
229
+ videos.forEach(video => {
230
+ if (window.getComputedStyle(video).display !== 'none') {
231
+ video.play();
232
+ }
233
+ });
234
+ },
235
+ }
236
+ );
237
+ }
238
+
239
+ hidePopup() {
240
+ if (!this.popup) return;
241
+
242
+ gsap.to(this.popup, {
243
+ opacity: 0,
244
+ duration: 0.3,
245
+ ease: 'linear',
246
+ onComplete: () => {
247
+ this.popup.classList.add('!tw-hidden');
248
+ const videos = this.popup.querySelectorAll('video');
249
+ videos.forEach(video => {
250
+ if (video) {
251
+ video.pause();
252
+ }
253
+ });
254
+ },
255
+ });
256
+ if (this.popup && document.body.contains(this.popup)) {
257
+ document.body.removeChild(this.popup); // 清理 popup 元素
258
+ }
259
+ }
260
+ }
261
+ customElements.define('zz-video-popup', ZZVideoPopup);
262
+ }
263
+
264
+
168
265
  /**
169
266
  * Toast 组件
170
267
  */
@@ -7,14 +7,78 @@
7
7
  box-sizing: border-box;
8
8
  overflow: hidden;
9
9
  position: relative;
10
+ --zz-autoplay-delay: {{ block.settings.autoplay_delay | default: 4000 }}ms;
11
+ --zz-pagination-offset-y: {{ block.settings.pagination_offset_y | default: 0 }}px;
10
12
  }
11
13
  #shopify-block-{{block.id}} .zz-normal-swiper {
12
- width: 100%;
13
14
  box-sizing: border-box;
14
15
  overflow: hidden;
15
16
  position: relative;
16
17
  letter-spacing: 0;
18
+ padding-bottom: 48px;
17
19
  }
20
+ {% if block.settings.pc_full_width == false %}
21
+
22
+ /* PC下固定宽度居中 */
23
+ @media (min-width: 1024px) {
24
+ #shopify-block-{{block.id}} .zz-normal-swiper {
25
+ width: {{ block.settings.pc_lg_width }}vw;
26
+ margin: 0 auto;
27
+ transition: all 0.6s ease;
28
+ position: relative;
29
+ }
30
+ }
31
+
32
+ @media (min-width: 1280px) {
33
+ #shopify-block-{{block.id}} .zz-normal-swiper {
34
+ width: {{ block.settings.pc_xl_width }}px;
35
+ margin: 0 auto;
36
+ transition: all 0.6s ease;
37
+ position: relative;
38
+ }
39
+ }
40
+
41
+ /* 👉 当启用 right_to_edge 时,将右侧拉到屏幕边缘 */
42
+ {% if block.settings.right_to_edge == true %}
43
+ @media (min-width: 1280px) {
44
+ #shopify-block-{{block.id}} .zz-normal-swiper {
45
+ margin-left: calc((100% - {{ block.settings.pc_xl_width }}px) / 2);
46
+ margin-right: 0;
47
+ width: calc({{ block.settings.pc_xl_width }}px + (100% - {{ block.settings.pc_xl_width }}px) / 2);
48
+ }
49
+ #shopify-block-{{block.id}} .zz-normal-swiper .swiper-pagination {
50
+ left: 50%;
51
+ transform: translateX(-50%);
52
+ width: {{ block.settings.pc_xl_width }}px;
53
+ }
54
+ }
55
+
56
+ @media (min-width: 1024px) and (max-width: 1279px) {
57
+ #shopify-block-{{block.id}} .zz-normal-swiper {
58
+ margin-left: calc((100% - ({{ block.settings.pc_lg_width }}vw)) / 2);
59
+ margin-right: 0;
60
+ width: calc({{ block.settings.pc_lg_width }}vw + (100% - ({{ block.settings.pc_lg_width }}vw)) / 2);
61
+ }
62
+ #shopify-block-{{block.id}} .zz-normal-swiper .swiper-pagination {
63
+ left: 50%;
64
+ transform: translateX(-50%);
65
+ width: {{ block.settings.pc_lg_width }}vw;
66
+ }
67
+ }
68
+ @media (max-width: 1024px) {
69
+ #shopify-block-{{block.id}} .zz-swiper-wrapper {
70
+ padding-left: 20px;
71
+ }
72
+ }
73
+ {% endif %}
74
+
75
+ {% else %}
76
+ #shopify-block-{{block.id}} {
77
+ width: 100%;
78
+ }
79
+ {% endif %}
80
+
81
+
18
82
 
19
83
  #shopify-block-{{block.id}} .swiper-button-next:after,
20
84
  #shopify-block-{{block.id}} .swiper-button-prev:after {
@@ -22,7 +86,57 @@
22
86
  }
23
87
 
24
88
  #shopify-block-{{block.id}} .zz-normal-swiper .swiper-pagination-bullets {
25
- bottom: 16px;
89
+ bottom: calc(16px + var(--zz-pagination-offset-y));
90
+ }
91
+ {% if block.settings.pagination_type == 'segments' %}
92
+ /* Segments pagination (mobile defaults) */
93
+ #shopify-block-{{block.id}} .zz-normal-swiper .zz-pagination-segments {
94
+ bottom: calc(16px + var(--zz-pagination-offset-y));
95
+ --seg-width: 24px;
96
+ --seg-active-width: 60px;
97
+ --seg-height: 4px;
98
+ --seg-gap: 2px;
99
+ --seg-bg: {{ block.settings.mb_pagination_color | color_modify: 'alpha', 0.3 }};
100
+ }
101
+ #shopify-block-{{block.id}} .zz-normal-swiper .zz-pagination-segments .swiper-pagination-bullet {
102
+ margin: 0 var(--seg-gap);
103
+ width: var(--seg-width);
104
+ height: var(--seg-height);
105
+ border-radius: var(--seg-height);
106
+ background: var(--seg-bg);
107
+ opacity: 1;
108
+ transition: width .3s ease, background-color .3s ease;
109
+ }
110
+ #shopify-block-{{block.id}} .zz-normal-swiper .zz-pagination-segments .swiper-pagination-bullet-active {
111
+ width: var(--seg-active-width);
112
+ position: relative;
113
+ overflow: hidden;
114
+ }
115
+ #shopify-block-{{block.id}} .zz-normal-swiper .zz-pagination-segments .swiper-pagination-bullet-active::after {
116
+ content: '';
117
+ position: absolute;
118
+ left: 0; top: 0; bottom: 0;
119
+ width: 0%;
120
+ background: #000;
121
+ {% if block.settings.autoplay %}
122
+ animation: zz-pb-{{block.id}} var(--zz-autoplay-delay) linear forwards;
123
+ {% endif %}
124
+ }
125
+ @keyframes zz-pb-{{block.id}} {
126
+ from { width: 0%; }
127
+ to { width: 100%; }
128
+ }
129
+ {% endif %}
130
+ /* Ensure progressbar pagination is visible and colored */
131
+ #shopify-block-{{block.id}} .zz-normal-swiper .swiper-pagination-progressbar,
132
+ #shopify-block-{{block.id}} .zz-normal-swiper .swiper-pagination-progressbar .swiper-pagination-progressbar-fill {
133
+ height: 4px;
134
+ }
135
+ #shopify-block-{{block.id}} .zz-normal-swiper .swiper-pagination-progressbar {
136
+ background: {{ block.settings.mb_pagination_color | color_modify: 'alpha', 0.15 }};
137
+ }
138
+ #shopify-block-{{block.id}} .zz-normal-swiper .swiper-pagination-progressbar .swiper-pagination-progressbar-fill {
139
+ background: {{ block.settings.mb_pagination_color }};
26
140
  }
27
141
  #shopify-block-{{block.id}} .zz-normal-swiper .swiper-pagination-bullet {
28
142
  margin: 0 4px;
@@ -39,7 +153,24 @@
39
153
 
40
154
  @media screen and (min-width: 1024px) {
41
155
  #shopify-block-{{block.id}} .zz-normal-swiper .swiper-pagination-bullets {
42
- bottom: 24px;
156
+ bottom: calc(24px + var(--zz-pagination-offset-y));
157
+ }
158
+ {% if block.settings.pagination_type == 'segments' %}
159
+ /* Segments pagination (desktop overrides) */
160
+ #shopify-block-{{block.id}} .zz-normal-swiper .zz-pagination-segments {
161
+ bottom: calc(24px + var(--zz-pagination-offset-y));
162
+ --seg-width: 48px;
163
+ --seg-active-width: 120px;
164
+ --seg-height: 6px;
165
+ --seg-gap: 4px;
166
+ --seg-bg: {{ block.settings.pagination_color | color_modify: 'alpha', 0.3 }};
167
+ }
168
+ {% endif %}
169
+ #shopify-block-{{block.id}} .zz-normal-swiper .swiper-pagination-progressbar {
170
+ background: {{ block.settings.pagination_color | color_modify: 'alpha', 0.15 }};
171
+ }
172
+ #shopify-block-{{block.id}} .zz-normal-swiper .swiper-pagination-progressbar .swiper-pagination-progressbar-fill {
173
+ background: {{ block.settings.pagination_color }};
43
174
  }
44
175
  #shopify-block-{{block.id}} .zz-normal-swiper .swiper-pagination-bullet {
45
176
  margin: 0 6px;
@@ -61,6 +192,10 @@
61
192
  #shopify-block-{{block.id}} .swiper-button-prev { left: 16px; }
62
193
  #shopify-block-{{block.id}} .swiper-button-next { right: 16px; }
63
194
  }
195
+ /* Reset segments progress animation when slide changes */
196
+ #shopify-block-{{block.id}} .zz-normal-swiper .zz-pagination-segments .swiper-pagination-bullet::after {
197
+ width: 0%;
198
+ }
64
199
 
65
200
  @media screen and (max-width: 1023px) {
66
201
  {% if block.settings.show_navigation_mobile == false %}
@@ -97,7 +232,7 @@
97
232
  {% endif %}
98
233
 
99
234
  {% if block.settings.pagination_type != 'none' %}
100
- <div class='swiper-pagination'></div>
235
+ <div class='swiper-pagination {% if block.settings.pagination_type == "segments" %} zz-pagination-segments {% endif %}'></div>
101
236
  {% endif %}
102
237
  </div>
103
238
 
@@ -124,7 +259,7 @@
124
259
  pagination: {
125
260
  el: '#shopify-block-{{block.id}} .swiper-pagination',
126
261
  clickable: true,
127
- type: '{{ block.settings.pagination_type }}',
262
+ type: '{% if block.settings.pagination_type == "segments" %}bullets{% else %}{{ block.settings.pagination_type }}{% endif %}',
128
263
  },
129
264
  {% endif %}
130
265
  {% if block.settings.show_navigation %}
@@ -220,6 +355,36 @@
220
355
  "label": "自适应高度",
221
356
  "default": false
222
357
  },
358
+ {
359
+ "type": "checkbox",
360
+ "id": "pc_full_width",
361
+ "label": "内容区域是否全屏展示",
362
+ "default": false
363
+ },
364
+ {
365
+ "type": "number",
366
+ "id": "pc_xl_width",
367
+ "label": "XL屏幕内容区域宽度 (px)",
368
+ "default": 1200,
369
+ "info": "非全屏展示时有效",
370
+ "visible_if": "{{ block.settings.pc_full_width == false }}"
371
+ },
372
+ {
373
+ "type": "number",
374
+ "id": "pc_lg_width",
375
+ "label": "LG屏幕内容区域宽度 (vw)",
376
+ "default": 90,
377
+ "info": "非全屏展示时有效",
378
+ "visible_if": "{{ block.settings.pc_full_width == false }}"
379
+ },
380
+
381
+ {
382
+ "type": "checkbox",
383
+ "id": "right_to_edge",
384
+ "label": "内容区域是否靠右对齐到边缘",
385
+ "visible_if": "{{ block.settings.pc_full_width == false }}",
386
+ "default": true
387
+ },
223
388
 
224
389
  {
225
390
  "type": "checkbox",
@@ -246,10 +411,17 @@
246
411
  { "value": "none", "label": "隐藏" },
247
412
  { "value": "bullets", "label": "圆点" },
248
413
  { "value": "fraction", "label": "分式" },
249
- { "value": "progressbar", "label": "进度条" }
414
+ { "value": "progressbar", "label": "进度条" },
415
+ { "value": "segments", "label": "分段进度" }
250
416
  ],
251
417
  "default": "bullets"
252
418
  },
419
+ {
420
+ "type": "number",
421
+ "id": "pagination_offset_y",
422
+ "label": "分页器垂直偏移(px)",
423
+ "default": 0
424
+ },
253
425
  {
254
426
  "type": "color",
255
427
  "id": "pagination_color",
@@ -1,7 +1,7 @@
1
1
  {% schema %}
2
2
  {
3
3
  "name": "Ratio Video",
4
- "class": "ratio-video",
4
+ "class": "ratio-video-block",
5
5
  "settings": [
6
6
  {
7
7
  "type": "header",
@@ -66,6 +66,12 @@
66
66
  "default": 10,
67
67
  "info": "单位:px"
68
68
  },
69
+ {
70
+ "type": "checkbox",
71
+ "id": "if_click_play",
72
+ "label": "是否点击播放",
73
+ "default": false
74
+ },
69
75
 
70
76
  ],
71
77
  "presets": [
@@ -80,6 +86,11 @@
80
86
  }
81
87
  {% endschema %}
82
88
 
89
+ {% assign autoplay = true %}
90
+ {% if block.settings.if_click_play %}
91
+ {% assign autoplay = false %}
92
+ {% endif %}
93
+
83
94
 
84
95
  {% render 'zz-video',
85
96
  pc_video: block.settings.video_pc,
@@ -88,10 +99,21 @@
88
99
  mb_poster: block.settings.poster_mb,
89
100
  lazy: true,
90
101
  class: 'ratio-video tw-w-full tw-h-auto',
91
- autoplay: true,
102
+ autoplay: autoplay,
92
103
  loop: true,
93
104
  muted: true
94
105
  %}
106
+ {% if block.settings.if_click_play %}
107
+ <div class="video-play-btn tw-absolute tw-top-[50%] tw-left-[50%] tw-translate-x-[-50%] tw-translate-y-[-50%]">
108
+ <svg class="max-lg:tw-hidden" width="80" height="81" viewBox="0 0 80 81" fill="none" xmlns="http://www.w3.org/2000/svg">
109
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M64.0712 34.896C67.939 37.2219 67.939 42.8289 64.0712 45.1548L34.0973 63.1798C30.1079 65.5788 25.0272 62.7055 25.0272 58.0504L25.0272 22.0004C25.0272 17.3453 30.1079 14.472 34.0973 16.871L64.0712 34.896Z" fill="white"/>
110
+ </svg>
111
+ <svg class="lg:tw-hidden" width="60" height="60" viewBox="0 0 60 60" fill="none" xmlns="http://www.w3.org/2000/svg">
112
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M48.3232 26.1313C51.2404 27.8856 51.2404 32.1144 48.3232 33.8687L25.7164 47.4634C22.7076 49.2727 18.8757 47.1056 18.8757 43.5947L18.8757 16.4053C18.8757 12.8944 22.7076 10.7273 25.7164 12.5366L48.3232 26.1313Z" fill="white"/>
113
+ </svg>
114
+ </div>
115
+
116
+ {% endif %}
95
117
 
96
118
  <style>
97
119
 
@@ -101,6 +123,11 @@
101
123
  overflow: hidden;
102
124
  box-sizing: border-box;
103
125
  overflow: hidden;
126
+ {% if block.settings.if_click_play %}
127
+ cursor: pointer;
128
+ position: relative;
129
+
130
+ {% endif %}
104
131
  }
105
132
 
106
133
 
@@ -111,5 +138,41 @@
111
138
  }
112
139
  }
113
140
 
114
-
115
141
  </style>
142
+
143
+ {% if block.settings.if_click_play %}
144
+ <script>
145
+ (function () {
146
+ const block = document.getElementById('shopify-block-{{block.id}}');
147
+ const videoPlayBtn = block.querySelector('.video-play-btn');
148
+ const videoPlayAndPause = block.querySelectorAll('.ratio-video');
149
+ videoPlayBtn.addEventListener('click', function (e) {
150
+ e.stopPropagation();
151
+ videoPlayAndPause.forEach((video) => {
152
+ if (video.classList.contains('tw-hidden')) {
153
+ return;
154
+ }
155
+ if (video.paused) {
156
+ video.play();
157
+ videoPlayBtn.classList.add('tw-hidden');
158
+ } else {
159
+ video.pause();
160
+ videoPlayBtn.classList.remove('tw-hidden');
161
+ }
162
+ });
163
+
164
+ });
165
+ videoPlayAndPause.forEach((video) => {
166
+ video.addEventListener('click', function () {
167
+ if (this.paused) {
168
+ this.play();
169
+ videoPlayBtn.classList.add('tw-hidden');
170
+ } else {
171
+ this.pause();
172
+ videoPlayBtn.classList.remove('tw-hidden');
173
+ }
174
+ });
175
+ });
176
+ })();
177
+ </script>
178
+ {% endif %}
@@ -0,0 +1,163 @@
1
+ <style>
2
+ @media screen and (max-width: 1024px) {
3
+ #shopify-block-{{block.id}} {
4
+ width: calc(100% - 72px);
5
+ box-sizing: border-box;
6
+ transform: translateX(-24px);
7
+ }
8
+ #shopify-block-{{block.id}} .image-video-box {
9
+ width: 100%;
10
+ aspect-ratio: 3 / 4;
11
+ object-fit: cover;
12
+ }
13
+ }
14
+ @media screen and (min-width: 1024px) {
15
+ #shopify-block-{{block.id}} {
16
+ width: 800px;
17
+ }
18
+ #shopify-block-{{block.id}} .image-video-box {
19
+ width: 100%;
20
+ aspect-ratio: 16 / 9;
21
+ object-fit: cover;
22
+ }
23
+ }
24
+ @media screen and (min-width: 1280px) {
25
+ #shopify-block-{{block.id}} {
26
+ width: 960px;
27
+ }
28
+ }
29
+
30
+
31
+
32
+ </style>
33
+
34
+ {% assign media_class = 'zz-link-' | append: block.id %}
35
+
36
+ <div class='media-box banner-item {{ media_class }} max-md:tw-rounded-[12px] md:tw-rounded-[16px] tw-relative'>
37
+ {% if block.settings.video_pc != blank or block.settings.video_url_pc != blank %}
38
+ {% if block.settings.video_url_pc != blank %}
39
+ {% assign pc_video = block.settings.video_url_pc %}
40
+ {% else %}
41
+ {% assign pc_video = block.settings.video_pc %}
42
+ {% endif %}
43
+ {% render 'zz-video',
44
+ pc_video: pc_video,
45
+ pc_poster: block.settings.poster_pc,
46
+ mb_video: block.settings.video_mb,
47
+ mb_poster: block.settings.poster_mb,
48
+ lazy: true,
49
+ class: 'image-video-box tw-w-full tw-h-auto max-md:tw-rounded-[12px] md:tw-rounded-[16px]',
50
+ autoplay: true,
51
+ muted: true,
52
+ loop: block.settings.video_loop
53
+ %}
54
+
55
+ {% render 'zz-video-popup',
56
+ pc_video: pc_video,
57
+ pc_poster: block.settings.poster_pc,
58
+ mb_video: block.settings.video_mb,
59
+ mb_poster: block.settings.poster_mb,
60
+
61
+ %}
62
+ {% else %}
63
+ {%
64
+ render 'zz-img',
65
+ pc_image: block.settings.poster_pc,
66
+ mb_image: block.settings.poster_mb,
67
+ image_alt: block.settings.heading | escape,
68
+ class_name: 'image-video-box max-md:tw-rounded-[12px] md:tw-rounded-[16px]',
69
+ %}
70
+ {% endif %}
71
+ {% if block.settings.heading != blank %}
72
+ <div class='heading-box tw-font-bold tw-text-white tw-absolute lg:tw-text-[28px] max-lg:tw-text-[18px] max-lg:tw-left-[20px] max-lg:tw-top-[20px] lg:tw-bottom-[40px] lg:tw-left-[48px]'>
73
+ {{ block.settings.heading }}
74
+ </div>
75
+ {% endif %}
76
+
77
+ {% if block.settings.btn_text != blank %}
78
+ <div class='show-video-btn tw-cursor-pointer tw-flex tw-items-center tw-gap-[4px] tw-text-white tw-absolute max-lg:tw-right-[20px] max-lg:tw-bottom-[20px] lg:tw-right-[40px] lg:tw-bottom-[40px]'>
79
+ {{ block.settings.btn_text }}
80
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
81
+ <path d="M11.0005 16.9497L15.9503 12L11.0005 7.05025" stroke="white" stroke-opacity="0.7" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
82
+ </svg>
83
+
84
+ </div>
85
+ {% endif %}
86
+
87
+ </div>
88
+
89
+
90
+ {% schema %}
91
+ {
92
+ "name": "ZZ Video Img JP Item",
93
+ "class": "zz-video-img-jp-item",
94
+ "settings": [
95
+ {
96
+ "type": "text",
97
+ "id": "video_url_pc",
98
+ "label": "Video url PC",
99
+ "info": "使用cdn视频时,填写视频url。防止大屏幕视频模糊"
100
+ },
101
+ {
102
+ "type": "video",
103
+ "id": "video_pc",
104
+ "label": "Video"
105
+ },
106
+ {
107
+ "type": "checkbox",
108
+ "id": "video_loop",
109
+ "label": "视频循环播放",
110
+ "default": false
111
+ },
112
+ {
113
+ "type": "image_picker",
114
+ "id": "poster_pc",
115
+ "label": "Poster"
116
+ },
117
+ {
118
+ "type": "video",
119
+ "id": "video_mb",
120
+ "label": "Video(Mobile)"
121
+ },
122
+ {
123
+ "type": "image_picker",
124
+ "id": "poster_mb",
125
+ "label": "Poster(Mobile)"
126
+ },
127
+
128
+ {
129
+ "type": "text",
130
+ "id": "heading",
131
+ "label": "标题",
132
+ },
133
+
134
+ {
135
+ "type": "text",
136
+ "id": "btn_text",
137
+ "label": "按钮文案",
138
+ },
139
+
140
+ ],
141
+ "presets": [
142
+ {
143
+ "name": "ZZ Video Img JP Item"
144
+ },
145
+ ]
146
+ }
147
+ {% endschema %}
148
+
149
+
150
+
151
+ <script>
152
+ (function () {
153
+ const block = document.getElementById(
154
+ 'shopify-block-{{block.id}}'
155
+ );
156
+ const videoPopup = block.querySelector('zz-video-popup');
157
+ const showBtn = block.querySelector('.show-video-btn');
158
+ showBtn.addEventListener('click', function () {
159
+ videoPopup.togglePopup();
160
+ });
161
+
162
+ })();
163
+ </script>
@@ -0,0 +1,489 @@
1
+ {% assign block_size = block.blocks.size %}
2
+ {% assign bullets_width_half = block_size
3
+ | minus: 1
4
+ | times: 14
5
+ | plus: 6
6
+ | divided_by: 2.0
7
+ | ceil
8
+ %}
9
+ {% assign btn_offset = bullets_width_half | plus: 14 | plus: 36 %}
10
+
11
+ <style>
12
+ #shopify-block-{{block.id}} {
13
+ background: {{ block.settings.bg_color }};
14
+ width: 100%;
15
+ box-sizing: border-box;
16
+ overflow: hidden;
17
+ position: relative;
18
+ }
19
+ #shopify-block-{{block.id}} .zz-video-swiper-preview {
20
+ width: 100%;
21
+ box-sizing: border-box;
22
+ overflow: hidden;
23
+ height: fit-content;
24
+ position: relative;
25
+ letter-spacing: 0;
26
+ }
27
+
28
+ #shopify-block-{{block.id}} .swiper-button-next,
29
+ #shopify-block-{{block.id}} .swiper-button-prev {
30
+ top: 0;
31
+ width: 48px;
32
+ height: 48px;
33
+ }
34
+ #shopify-block-{{block.id}} .swiper-button-prev {
35
+ left: unset;
36
+ right: 50%;
37
+ }
38
+ #shopify-block-{{block.id}} .swiper-button-next {
39
+ right: unset;
40
+ left: 50%;
41
+ }
42
+ #shopify-block-{{block.id}} .swiper-button-next:after,
43
+ #shopify-block-{{block.id}} .swiper-button-prev:after {
44
+ display: none;
45
+ }
46
+ #shopify-block-{{block.id}} .swiper-button-next.swiper-button-disabled, #shopify-block-{{block.id}} .swiper-button-prev.swiper-button-disabled {
47
+ opacity: 0.5;
48
+ }
49
+
50
+ #shopify-block-{{block.id}} .zz-video-swiper-preview .swiper-pagination-bullets .swiper-pagination-bullet {
51
+ margin: 0 8px;
52
+ width: 48px;
53
+ height: 4px;
54
+ border-radius: 3px;
55
+ background: {{ block.settings.pagination_color | color_modify: 'alpha', 0.2 }};
56
+ opacity: 1;
57
+ transition: 0.3s;
58
+ }
59
+ #shopify-block-{{block.id}} .zz-video-swiper-preview .swiper-pagination-bullets .swiper-pagination-bullet-active {
60
+ position: relative;
61
+ width: 120px;
62
+ {% if block.settings.if_autoplay == false %}
63
+ background-color: {{ block.settings.pagination_color }};
64
+ {% endif %}
65
+ }
66
+ #shopify-block-{{block.id}} .zz-video-swiper-preview .swiper-pagination-bullets .swiper-pagination-bullet-active .progress-line {
67
+ display: block;
68
+ position: absolute;
69
+ width:0;
70
+ height: 4px;
71
+ left: 0;
72
+ top: 0;
73
+ background-color: {{ block.settings.pagination_color }};
74
+ border-radius: 3px;
75
+ }
76
+ #shopify-block-{{block.id}} .swiper-slide-active .active-box-content {
77
+ display: block !important;
78
+ }
79
+
80
+ @media screen and (max-width: 1023px) {
81
+ #shopify-block-{{block.id}} .zz-video-swiper-preview {
82
+ padding-bottom: 68px;
83
+ }
84
+
85
+ #shopify-block-{{block.id}} .zz-video-swiper-preview .swiper-slide .banner-item::after {
86
+ display: none;
87
+ }
88
+ #shopify-block-{{block.id}} .swiper-button-next,
89
+ #shopify-block-{{block.id}} .swiper-button-prev {
90
+ z-index: 99;
91
+ top: unset;
92
+ bottom: 0;
93
+ width: 36px;
94
+ height: 36px;
95
+ }
96
+ #shopify-block-{{block.id}} .swiper-button-prev {
97
+ left: 50%;
98
+ transform: translate(-{{ btn_offset }}px, 0px);
99
+ }
100
+ #shopify-block-{{block.id}} .swiper-button-next {
101
+ left: unset;
102
+ right: 50%;
103
+ transform: translate({{ btn_offset }}px, 0px);
104
+ }
105
+ #shopify-block-{{block.id}} .zz-video-swiper-preview .swiper-pagination-bullets .swiper-pagination-bullet {
106
+ margin: 0 4px;
107
+ width: 6px;
108
+ height: 6px;
109
+ background-color: {{ block.settings.mb_pagination_color | color_modify: 'alpha', 0.3 }};
110
+ }
111
+ #shopify-block-{{block.id}} .zz-video-swiper-preview .swiper-pagination-bullets .swiper-pagination-bullet.swiper-pagination-bullet-active {
112
+ width: 6px;
113
+ background-color: {{ block.settings.mb_pagination_color }};
114
+ }
115
+ }
116
+ @media screen and (min-width: 1024px) {
117
+ #shopify-block-{{block.id}} .zz-video-swiper-preview {
118
+ padding-bottom: 68px;
119
+ }
120
+ #shopify-block-{{block.id}} .zz-video-swiper-preview .swiper-pagination-bullets {
121
+ bottom: 21px;
122
+ }
123
+ #shopify-block-{{block.id}} .swiper-button-prev {
124
+ transform: translate(-500px, 220px);
125
+ }
126
+ #shopify-block-{{block.id}} .swiper-button-next {
127
+ transform: translate(500px, 220px);
128
+ }
129
+
130
+
131
+
132
+
133
+ }
134
+ @media screen and (min-width: 1280px) {
135
+
136
+ #shopify-block-{{block.id}} .swiper-button-prev {
137
+ transform: translate(-594px, 260px);
138
+ }
139
+ #shopify-block-{{block.id}} .swiper-button-next {
140
+ transform: translate(594px, 260px);
141
+ }
142
+
143
+ }
144
+ </style>
145
+
146
+ <div
147
+ class='zz-video-swiper-preview zz-video-swiper-preview-{{ block.id }}'
148
+ >
149
+ <div class='swiper-wrapper'>
150
+ {% content_for 'blocks' %}
151
+ </div>
152
+
153
+ <!-- If we need navigation buttons -->
154
+ <div class='swiper-button-prev {% if block_size <= 1 %} tw-hidden {% endif %}'>
155
+ <span class='lg:tw-hidden'>
156
+ {% if block.settings.prev_icon != blank %}
157
+ {{
158
+ block.settings.prev_icon
159
+ | image_url: width: 48
160
+ | image_tag:
161
+ alt: 'prev',
162
+ class: 'tw-w-full tw-h-full tw-object-contain'
163
+ }}
164
+ {% else %}
165
+ {% render 'zz-prev-next-btn',
166
+ type: 'prev',
167
+ color_type: block.settings.prev_next_type
168
+ %}
169
+ {% endif %}
170
+
171
+ </span>
172
+ <span class='max-lg:tw-hidden'>
173
+ {% render 'zz-prev-next-blur-icon', type: 'prev', color_type: 'light' %}
174
+ </span>
175
+ </div>
176
+ <div class='swiper-button-next {% if block_size <= 1 %} tw-hidden {% endif %}'>
177
+ <span class='lg:tw-hidden'>
178
+ {% if block.settings.next_icon != blank %}
179
+ {{
180
+ block.settings.next_icon
181
+ | image_url: width: 48
182
+ | image_tag:
183
+ alt: 'prev',
184
+ class: 'tw-w-full tw-h-full tw-object-contain'
185
+ }}
186
+ {% else %}
187
+ {% render 'zz-prev-next-btn',
188
+ type: 'next',
189
+ color_type: block.settings.prev_next_type
190
+ %}
191
+ {% endif %}
192
+
193
+ </span>
194
+ <span class='max-lg:tw-hidden'>
195
+ {% render 'zz-prev-next-blur-icon', type: 'next', color_type: 'light' %}
196
+ </span>
197
+ </div>
198
+
199
+ <!-- Pagination indicator -->
200
+ <div class='swiper-pagination'></div>
201
+ </div>
202
+
203
+ <script>
204
+ document.addEventListener('DOMContentLoaded', function () {
205
+ const swiperClassName = '.zz-video-swiper-preview-{{ block.id }}';
206
+ let isDesktop = window.innerWidth > 1023;
207
+ // 获取 class zz-accessories-swiper 的元素 且data-id 为 block.id 的元素
208
+ const zzAccessoriesSwiper = document.querySelector(swiperClassName);
209
+ const swiperWrapper = zzAccessoriesSwiper.querySelector('.swiper-wrapper');
210
+ // 获取当前 swiper-wrapper 下面 所有 第一层 div
211
+ const swiperSlides = swiperWrapper.querySelectorAll(':scope > div');
212
+ // 给每个 swiper-slide 添加 class swiper-slide
213
+ swiperSlides.forEach((slide) => {
214
+ slide.classList.add('swiper-slide');
215
+ });
216
+ let progressInterval = null;
217
+ let progressTimer = null;
218
+
219
+ const prevSwiper = new Swiper(swiperClassName, {
220
+ {% if block.settings.if_loop == true %}
221
+ loop: true,
222
+ {% endif %}
223
+ slidesPerView: 'auto',
224
+ initialSlide: isDesktop ? 1 : 0,
225
+ centeredSlides: true,
226
+ spaceBetween: isDesktop ? 24 : 12,
227
+ navigation: {
228
+ nextEl: '.swiper-button-next',
229
+ prevEl: '.swiper-button-prev',
230
+ },
231
+ pagination: {
232
+ el: '.swiper-pagination',
233
+ clickable: true,
234
+ renderBullet: function (index, className) {
235
+ return `
236
+ <span class="${className}">
237
+ <span class="progress-line"></span>
238
+ </span>`;
239
+ },
240
+ },
241
+ effect: 'slide',
242
+ on: {
243
+ slideChangeTransitionEnd: function () {
244
+ handleVideoOnSlideChange();
245
+ },
246
+ slideChangeTransitionStart: function () {
247
+ if (progressInterval) {
248
+ clearInterval(progressInterval);
249
+ }
250
+ if (progressTimer) {
251
+ clearTimeout(progressTimer);
252
+ }
253
+ },
254
+ slideChange: function () {
255
+ if (isDesktop) {
256
+ const block = document.getElementById('shopify-block-{{block.id}}');
257
+ block
258
+ .querySelectorAll('.swiper-pagination-bullet')
259
+ .forEach((bullet) => {
260
+ const progressBar = bullet.querySelector('.progress-line');
261
+ progressBar.style.transition = 'none';
262
+ progressBar.style.width = '0';
263
+ });
264
+ }
265
+ },
266
+ },
267
+ });
268
+
269
+ // 使用 Intersection Observer 监听 swiper-wrapper 的可见性
270
+
271
+ const observer = new IntersectionObserver(
272
+ (entries) => {
273
+ entries.forEach((entry) => {
274
+ if (entry.isIntersecting) {
275
+ // 当 swiper-wrapper 第一次出现在视口中时执行
276
+ setTimeout(() => {
277
+ handleVideoOnSlideChange();
278
+ }, 200);
279
+ // 执行一次后取消观察
280
+ observer.unobserve(entry.target);
281
+ }
282
+ });
283
+ },
284
+ {
285
+ threshold: 0.2, // 当 20% 的元素可见时触发
286
+ }
287
+ );
288
+
289
+ if (swiperWrapper) {
290
+ observer.observe(swiperWrapper);
291
+ }
292
+
293
+ function getVisibleDisplayMedias(container) {
294
+ const videos = container.querySelectorAll('video');
295
+ const video = Array.from(videos).find((video) => {
296
+ const style = window.getComputedStyle(video);
297
+ return style.display !== 'none';
298
+ });
299
+ if (video) {
300
+ return {
301
+ video,
302
+ type: 'video',
303
+ };
304
+ } else {
305
+ const images = container.querySelectorAll('img');
306
+ const image = Array.from(images).find((image) => {
307
+ const style = window.getComputedStyle(image);
308
+ return style.display !== 'none';
309
+ });
310
+ if (image) {
311
+ return {
312
+ image,
313
+ type: 'image',
314
+ };
315
+ }
316
+ }
317
+ return null;
318
+ }
319
+ const endedHandler = function () {
320
+
321
+ if (prevSwiper.isEnd) {
322
+ prevSwiper.slideTo(0);
323
+ } else {
324
+ prevSwiper.slideNext();
325
+ }
326
+ if (progressInterval) {
327
+ clearInterval(progressInterval);
328
+ }
329
+ this.removeEventListener('ended', endedHandler);
330
+ };
331
+
332
+ function updataMediaProgress(type, videoDom) {
333
+ const block = document.getElementById('shopify-block-{{block.id}}');
334
+ const activeBullet = block.querySelector(
335
+ '.swiper-pagination-bullet-active'
336
+ );
337
+ const progressBar = activeBullet.querySelector('.progress-line');
338
+ if (progressBar) {
339
+ if (progressInterval) {
340
+ clearInterval(progressInterval);
341
+ }
342
+ if (progressTimer) {
343
+ clearTimeout(progressTimer);
344
+ }
345
+ if (type === 'video') {
346
+ // 添加新的事件监听器
347
+ if (isDesktop) {
348
+ progressInterval = setInterval(() => {
349
+ const progress = videoDom.currentTime / videoDom.duration;
350
+ progressBar.style.transition = '0.2s';
351
+ progressBar.style.width = `${progress * 100}%`;
352
+ }, 100);
353
+ }
354
+
355
+ videoDom.addEventListener('ended', endedHandler);
356
+ } else if (type === 'image') {
357
+ if (isDesktop) {
358
+ progressBar.style.transition = '10s linear';
359
+ progressBar.style.width = '100%';
360
+ }
361
+ progressTimer = setTimeout(() => {
362
+ if (prevSwiper.isEnd) {
363
+ prevSwiper.slideTo(0);
364
+ } else {
365
+ prevSwiper.slideNext();
366
+ }
367
+ }, 10000);
368
+ }
369
+ }
370
+ }
371
+
372
+ function handleVideoOnSlideChange() {
373
+ // 暂停所有视频
374
+ const block = document.getElementById('shopify-block-{{block.id}}');
375
+ const allvideo = block.querySelectorAll('video');
376
+ // 播放当前 active slide 中的视频
377
+ const activeSlide = block.querySelector('.swiper-slide-active');
378
+ const media = getVisibleDisplayMedias(activeSlide);
379
+ // 重置进度条
380
+ if (isDesktop) {
381
+ const activeBullet = block.querySelector(
382
+ '.swiper-pagination-bullet-active'
383
+ );
384
+ if (!activeBullet) {
385
+ return;
386
+ }
387
+ const progressBar = activeBullet.querySelector('.progress-line');
388
+ if (progressBar) {
389
+ progressBar.style.transition = 'none';
390
+ progressBar.style.width = '0';
391
+ }
392
+ }
393
+ if (media.type === 'video') {
394
+ allvideo.forEach((item) => {
395
+ if (item === media.video) {
396
+ media.video.currentTime = 0;
397
+ if (media.video.paused) {
398
+ media.video.play();
399
+ }
400
+
401
+ if('{{ block.settings.if_autoplay }}' == 'true') {
402
+ updataMediaProgress('video', media.video);
403
+ }
404
+ } else {
405
+ item.pause();
406
+ }
407
+ });
408
+ } else if (media.type === 'image') {
409
+ // 处理图片的情况
410
+ if('{{ block.settings.if_autoplay }}' == 'true') {
411
+ updataMediaProgress('image', media.image);
412
+ }
413
+ allvideo.forEach((item) => {
414
+ item.pause();
415
+ });
416
+ }
417
+ }
418
+ });
419
+ </script>
420
+
421
+ {% schema %}
422
+ {
423
+ "name": "Video Swiper JP Preview",
424
+ "class": "zz-video-jp-preview-block",
425
+ "settings": [
426
+ {
427
+ "type": "color",
428
+ "id": "bg_color",
429
+ "default": "#ffffff",
430
+ "label": "背景色"
431
+ },
432
+
433
+ {
434
+ "type": "color",
435
+ "id": "pagination_color",
436
+ "default": "#ffffff",
437
+ "label": "轮播导航圆圈颜色"
438
+ },
439
+ {
440
+ "type": "color",
441
+ "id": "mb_pagination_color",
442
+ "default": "#000000",
443
+ "label": "移动端轮播导航圆圈颜色"
444
+ },
445
+ {
446
+ "type": "checkbox",
447
+ "id": "if_loop",
448
+ "label": "是否循环播放",
449
+ "default": false
450
+ },
451
+ {
452
+ "type": "checkbox",
453
+ "id": "if_autoplay",
454
+ "label": "是否自动轮播",
455
+ "default": false
456
+ },
457
+ {
458
+ "type": "select",
459
+ "id": "prev_next_type",
460
+ "label": "prev next 颜色",
461
+ "options": [
462
+ { "value": "dark", "label": "黑色" },
463
+ { "value": "light", "label": "白色" },
464
+ ],
465
+ "default": "dark"
466
+ },
467
+ {
468
+ "type": "image_picker",
469
+ "id": "prev_icon",
470
+ "label": "轮播上一张图标 Mobile"
471
+ },
472
+ {
473
+ "type": "image_picker",
474
+ "id": "next_icon",
475
+ "label": "轮播下一张图标 Mobile"
476
+ },
477
+ ],
478
+ "blocks": [
479
+ {
480
+ "type": "zz-video-swiper-jp-item"
481
+ }
482
+ ],
483
+ "presets": [
484
+ {
485
+ "name": "Video Swiper JP Preview"
486
+ },
487
+ ]
488
+ }
489
+ {% endschema %}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zz-shopify-components",
3
- "version": "0.16.2",
3
+ "version": "0.16.3-beta.1",
4
4
  "description": "Reusable Shopify components for theme projects",
5
5
  "keywords": [
6
6
  "shopify",
@@ -0,0 +1,33 @@
1
+ {% comment %}
2
+ pc_video: 视频
3
+ pc_poster: 封面
4
+ mb_video: 移动端视频
5
+ mb_poster: 移动端封面
6
+
7
+
8
+ - class_name:自定义类名
9
+ {% endcomment %}
10
+
11
+ <zz-video-popup>
12
+
13
+ <div
14
+ class='togglePopup popup tw-flex !tw-hidden tw-bg-[rgba(0,0,0,0.9)] tw-items-center tw-justify-center tw-fixed tw-top-0 tw-left-0 tw-right-0 tw-bottom-0 tw-z-[999] tw-overflow-hidden {{ class_name }}'
15
+ >
16
+ <div class="tw-max-w-[960px]">
17
+ {% render 'zz-video',
18
+ pc_video: pc_video,
19
+ pc_poster: pc_poster,
20
+ mb_video: mb_video,
21
+ mb_poster: mb_poster,
22
+ controls: true,
23
+ lazy: false,
24
+ class: 'tw-w-full tw-h-auto',
25
+ autoplay: false,
26
+ loop: true,
27
+ %}
28
+
29
+ </div>
30
+
31
+ </div>
32
+ </zz-video-popup>
33
+