zz-shopify-components 0.32.1-beta.4 → 0.32.1-beta.5

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.
@@ -451,6 +451,25 @@ function getVisibleDisplayMedias(container) {
451
451
  // component 统一的初始化入口
452
452
  document.addEventListener('DOMContentLoaded', (event) => {
453
453
  const isDesktop = window.innerWidth > 1023;
454
+
455
+ // 处理所有组件可见逻辑处理
456
+ const compIntersectionObserver = new IntersectionObserver((entries) => {
457
+ entries.forEach((entry) => {
458
+ const el = entry.target;
459
+ if (entry.isIntersecting) {
460
+
461
+ } else {
462
+ if (el.classList.contains('zz-card-animate-list')) {
463
+ // 重置所有
464
+ el.querySelectorAll('.zz-card-inner').forEach(card => {
465
+ card.classList.remove('is-active');
466
+ });
467
+ }
468
+ }
469
+ });
470
+ });
471
+
472
+
454
473
  if (!isDesktop) {
455
474
  const switchCard = document.querySelectorAll('.product-switch-card');
456
475
  if (switchCard.length > 0) {
@@ -481,5 +500,28 @@ document.addEventListener('DOMContentLoaded', (event) => {
481
500
  });
482
501
 
483
502
 
484
- });
503
+ // zz-card-animate-text.liquid
504
+ const cardListGroup = document.querySelectorAll('.zz-card-animate-list');
505
+ cardListGroup.forEach((list) => {
506
+ if (!list) return;
507
+ if (isDesktop) return;
508
+ list.addEventListener('click', (e) => {
509
+ const item = e.target.closest('.zz-card-inner');
510
+ if (!item || !list.contains(item)) return;
511
+
512
+ console.log('click card item');
513
+
514
+ // 重置所有
515
+ list.querySelectorAll('.zz-card-inner').forEach(el => {
516
+ el.classList.remove('is-active');
517
+ });
518
+
519
+ // 当前设为 B 状态
520
+ item.classList.add('is-active');
521
+ });
522
+ compIntersectionObserver.observe(list);
523
+
524
+ });
525
+
485
526
 
527
+ });
@@ -0,0 +1,229 @@
1
+ {% schema %}
2
+ {
3
+ "name": "Card Animate Text",
4
+ "class": "zz-card-animate-text",
5
+ "settings": [
6
+ {
7
+ "type": "select",
8
+ "id": "bg_type",
9
+ "label": "背景类型",
10
+ "options": [
11
+ { "value": "image", "label": "图片" },
12
+ { "value": "video", "label": "视频" }
13
+ ],
14
+ "default": "image"
15
+ },
16
+ {
17
+ "type": "header",
18
+ "content": "PC端设置"
19
+ },
20
+ {
21
+ "type": "image_picker",
22
+ "id": "desktop_image",
23
+ "label": "PC端图片"
24
+ },
25
+
26
+ {
27
+ "type": "range",
28
+ "id": "pc_width",
29
+ "min": 0,
30
+ "max": 100,
31
+ "step": 1,
32
+ "unit": "%",
33
+ "label": "PC端占父容器的百分比,单位%",
34
+ "default": 50
35
+ },
36
+ {
37
+ "type": "number",
38
+ "id": "desktop_border_radius",
39
+ "label": "PC端圆角",
40
+ "default": 16,
41
+ "info": "单位:px"
42
+ },
43
+ {
44
+ "type": "header",
45
+ "content": "移动端设置"
46
+ },
47
+ {
48
+ "type": "image_picker",
49
+ "id": "mobile_image",
50
+ "label": "移动端图片"
51
+ },
52
+
53
+ {
54
+ "type": "range",
55
+ "id": "mobile_width",
56
+ "min": 0,
57
+ "max": 100,
58
+ "step": 1,
59
+ "unit": "%",
60
+ "label": "移动端占父容器的百分比,单位%",
61
+ "default": 100
62
+ },
63
+ {
64
+ "type": "number",
65
+ "id": "mobile_border_radius",
66
+ "label": "移动端圆角",
67
+ "default": 10,
68
+ "info": "单位:px"
69
+ },
70
+ {
71
+ "type": "html",
72
+ "id": "front_text",
73
+ "label": "正面文案",
74
+ "default": "<p>Enter your content here</p>"
75
+ },
76
+ {
77
+ "type": "select",
78
+ "id": "front_text_position",
79
+ "label": "正面文案位置",
80
+ "default": "left_top",
81
+ "options": [
82
+ { "value": "left_top", "label": "左上" },
83
+ { "value": "left_bottom", "label": "左下" }
84
+ ],
85
+ },
86
+ {
87
+ "type": "richtext",
88
+ "id": "hover_text",
89
+ "label": "hover文案"
90
+ },
91
+ {
92
+ "type": "video",
93
+ "id": "pc_background_video",
94
+ "label": "PC端背景视频",
95
+ "visible_if": "{{ block.settings.bg_type == 'video' }}"
96
+ },
97
+ {
98
+ "type": "video",
99
+ "id": "mobile_background_video",
100
+ "label": "移动端视频",
101
+ "visible_if": "{{ block.settings.bg_type == 'video' }}"
102
+ },
103
+ ],
104
+ "presets": [
105
+ {
106
+ "name": "Card Animate Text",
107
+ }
108
+ ]
109
+ }
110
+ {% endschema %}
111
+
112
+
113
+
114
+ <div class="zz-card-inner tw-relative">
115
+ <div class="main-face tw-w-full tw-h-full">
116
+ {% render 'zz-img',
117
+ pc_image: block.settings.desktop_image,
118
+ mb_image: block.settings.mobile_image,
119
+ image_alt: 'image',
120
+ class_name: "card-inner-image tw-w-full tw-h-auto",
121
+ %}
122
+
123
+ <div class="main-text tw-absolute tw-text-white">
124
+ {{ block.settings.front_text }}
125
+ </div>
126
+
127
+ <div class="add-btn tw-absolute tw-w-[21px] tw-h-[21px] lg:tw-w-[45px] lg:tw-h-[45px] tw-right-[7px] tw-bottom-[7px] lg:tw-right-[20px] lg:tw-bottom-[20px]">
128
+ <span class="tw-w-[6px] tw-h-[7px] lg:tw-w-[14px] lg:tw-h-[15px]">
129
+ <svg width="100%" height="100%" viewBox="0 0 14 15" fill="none" xmlns="http://www.w3.org/2000/svg">
130
+ <line y1="6.875" x2="13.75" y2="6.875" stroke="white" stroke-width="1.25"/>
131
+ <line x1="6.87524" y1="2.73196e-08" x2="6.87524" y2="15" stroke="white" stroke-width="1.25"/>
132
+ </svg>
133
+ </span>
134
+
135
+ </div>
136
+ </div>
137
+ <div class="cover-face tw-hidden tw-absolute tw-top-0 tw-left-0 tw-w-full tw-h-full tw-bg-black/50">
138
+ {% if block.settings.bg_type == 'video' %}
139
+
140
+ {% render 'zz-video',
141
+ pc_video: block.settings.pc_background_video,
142
+ pc_poster: block.settings.desktop_image,
143
+ mb_video: block.settings.mobile_background_video,
144
+ mb_poster: block.settings.mobile_image,
145
+ lazy: true,
146
+ class: 'card-inner-video tw-w-full tw-h-full tw-object-cover',
147
+ autoplay: true,
148
+ loop: true,
149
+ muted: true
150
+ %}
151
+
152
+ {% else %}
153
+ <div class="cover-text tw-flex tw-items-center tw-px-[10px] lg:tw-px-[25px] tw-h-full tw-w-full max-lg:tw-leading-[1.5] lg:tw-leading-[1.2]
154
+ tw-text-[14px] lg:tw-text-[24px] tw-font-bold tw-text-white">
155
+ {{ block.settings.hover_text }}
156
+ </div>
157
+ {% endif %}
158
+
159
+ </div>
160
+ </div>
161
+
162
+
163
+ <style>
164
+
165
+ #shopify-block-{{ block.id }} {
166
+ cursor: pointer;
167
+ border-radius: {{ block.settings.mobile_border_radius }}px;
168
+ width: {% if block.settings.mobile_width > 0 %}{{ block.settings.mobile_width }}%{% else %}100%{% endif %};
169
+ overflow: hidden;
170
+ }
171
+ #shopify-block-{{ block.id }} .main-text {
172
+ font-weight: 700;
173
+ font-size: 12px;
174
+ line-height: 120%;
175
+ right: 10px;
176
+ {% if block.settings.front_text_position == 'left_top' %}
177
+ top: 10px;
178
+ left: 10px;
179
+ {% else %}
180
+ bottom: 10px;
181
+ left: 10px;
182
+ {% endif %}
183
+ }
184
+ #shopify-block-{{ block.id }} .zz-card-inner:hover .cover-face {
185
+ display: block !important;
186
+ }
187
+ #shopify-block-{{ block.id }} .zz-card-inner:hover .main-text {
188
+ display: none !important;
189
+ }
190
+ #shopify-block-{{ block.id }} .zz-card-inner:hover .add-btn {
191
+ display: none !important;
192
+ }
193
+ #shopify-block-{{ block.id }} .zz-card-inner.is-active .cover-face {
194
+ display: block !important;
195
+ }
196
+ #shopify-block-{{ block.id }} .zz-card-inner.is-active .main-text {
197
+ display: none !important;
198
+ }
199
+ #shopify-block-{{ block.id }} .zz-card-inner.is-active .add-btn {
200
+ display: none !important;
201
+ }
202
+ #shopify-block-{{ block.id }} .add-btn {
203
+ background: rgba(255, 255, 255, 0.3);
204
+ backdrop-filter: blur(10px);
205
+ border-radius: 50%;
206
+ display: flex;
207
+ align-items: center;
208
+ justify-content: center;
209
+ }
210
+
211
+
212
+ @media screen and (min-width: 1024px) {
213
+ #shopify-block-{{ block.id }} {
214
+ border-radius: {{ block.settings.desktop_border_radius }}px;
215
+ width: {% if block.settings.pc_width > 0 %}{{ block.settings.pc_width }}%{% else %}100%{% endif %};
216
+ }
217
+ #shopify-block-{{ block.id }} .main-text {
218
+ font-size: 24px;
219
+ right: 20px;
220
+ {% if block.settings.front_text_position == 'left_top' %}
221
+ top: 20px;
222
+ left: 20px;
223
+ {% else %}
224
+ bottom: 20px;
225
+ left: 20px;
226
+ {% endif %}
227
+ }
228
+ }
229
+ </style>
@@ -683,7 +683,7 @@
683
683
  "id": "enable_long_image_scroll",
684
684
  "label": "启用超长图横向滚动模式",
685
685
  "info": "开启后,单张图片可横向滑动查看,常用于超宽全景图。此时请将可见数(Mobile/PC)设置为1。",
686
- "default": true
686
+ "default": false
687
687
  },
688
688
  {
689
689
  "type": "range",
@@ -0,0 +1,398 @@
1
+ {% comment %}
2
+ ZZ Swiper Marquee Row
3
+ 这是一个高性能、可复用的单行跑马灯组件。
4
+ 支持嵌套子 Block,支持匀速滚动、悬停暂停和手动拖拽。
5
+ {% endcomment %}
6
+
7
+ {% schema %}
8
+ {
9
+ "name": "ZZ Swiper Marquee Row",
10
+ "class": "zz-swiper-marquee-row",
11
+ "settings": [
12
+ {
13
+ "type": "header",
14
+ "content": "跑马灯配置 (Animation)"
15
+ },
16
+ {
17
+ "type": "select",
18
+ "id": "direction",
19
+ "label": "滚动方向",
20
+ "options": [
21
+ { "value": "ltr", "label": "从左向右 (Left-to-Right)" },
22
+ { "value": "rtl", "label": "从右向左 (Right-to-Left)" }
23
+ ],
24
+ "default": "rtl"
25
+ },
26
+ {
27
+ "type": "number",
28
+ "id": "marquee_speed",
29
+ "label": "滚动速度 (数值越小越快)",
30
+ "default": 8000,
31
+ "info": "这是完成一次完整循环所需的时间(ms)。建议值 4000-12000"
32
+ },
33
+ {
34
+ "type": "checkbox",
35
+ "id": "enable_autoplay_pc",
36
+ "label": "桌面端开启自动滚动",
37
+ "default": true
38
+ },
39
+ {
40
+ "type": "checkbox",
41
+ "id": "enable_autoplay_mobile",
42
+ "label": "移动端开启自动滚动",
43
+ "default": true
44
+ },
45
+ {
46
+ "type": "checkbox",
47
+ "id": "pause_on_hover",
48
+ "label": "鼠标悬停时暂停",
49
+ "default": true
50
+ },
51
+ {
52
+ "type": "checkbox",
53
+ "id": "enable_grab",
54
+ "label": "允许手动拖拽",
55
+ "default": false
56
+ },
57
+ {
58
+ "type": "header",
59
+ "content": "布局与尺寸 (Layout)"
60
+ },
61
+ {
62
+ "type": "number",
63
+ "id": "desktop_height",
64
+ "label": "桌面端高度 (px)",
65
+ "default": 400
66
+ },
67
+ {
68
+ "type": "number",
69
+ "id": "card_width_desktop",
70
+ "label": "桌面端卡片宽度 (px)",
71
+ "default": 300,
72
+ "info": "设置卡片的固定宽度,留空或0为自适应"
73
+ },
74
+ {
75
+ "type": "number",
76
+ "id": "mobile_height",
77
+ "label": "移动端高度 (px)",
78
+ "default": 250
79
+ },
80
+ {
81
+ "type": "number",
82
+ "id": "card_width_mobile",
83
+ "label": "移动端卡片宽度 (px)",
84
+ "default": 200,
85
+ "info": "设置卡片的固定宽度,留空或0为自适应"
86
+ },
87
+ {
88
+ "type": "range",
89
+ "id": "gap_desktop",
90
+ "label": "桌面端间距 (px)",
91
+ "default": 20,
92
+ "min": 0,
93
+ "max": 100,
94
+ "step": 2
95
+ },
96
+ {
97
+ "type": "range",
98
+ "id": "gap_mobile",
99
+ "label": "移动端间距 (px)",
100
+ "default": 12,
101
+ "min": 0,
102
+ "max": 50,
103
+ "step": 1
104
+ },
105
+ {
106
+ "type": "color",
107
+ "id": "bg_color",
108
+ "label": "背景底色",
109
+ "default": "transparent"
110
+ },
111
+ {
112
+ "type": "header",
113
+ "content": "媒体控制 (Media)"
114
+ },
115
+ {
116
+ "type": "checkbox",
117
+ "id": "video_autoplay",
118
+ "label": "视频自动播放",
119
+ "default": true,
120
+ "info": "开启后,内部视频块将默认静音自动播放"
121
+ }
122
+ ],
123
+ "blocks": [
124
+ {
125
+ "type": "@theme"
126
+ }
127
+ ],
128
+ "presets": [
129
+ {
130
+ "name": "ZZ Swiper Marquee Row",
131
+ "category": "ZZ Components",
132
+ "blocks": [
133
+ { "type": "zz-video-img-item" },
134
+ { "type": "zz-video-img-item" },
135
+ { "type": "zz-video-img-item" }
136
+ ]
137
+ }
138
+ ]
139
+ }
140
+ {% endschema %}
141
+
142
+ <style>
143
+ #shopify-block-{{ block.id }} {
144
+ --marquee-speed: {{ block.settings.marquee_speed | default: 8000 }}ms;
145
+ --marquee-height: {{ block.settings.desktop_height | default: 400 }}px;
146
+ --marquee-gap: {{ block.settings.gap_desktop | default: 20 }}px;
147
+ --marquee-bg: {{ block.settings.bg_color | default: 'transparent' }};
148
+
149
+ {% if block.settings.card_width_desktop > 0 %}
150
+ --card-width: {{ block.settings.card_width_desktop }}px;
151
+ --child-width: 100%;
152
+ {% else %}
153
+ --card-width: auto;
154
+ --child-width: auto;
155
+ {% endif %}
156
+
157
+ background: var(--marquee-bg);
158
+ width: 100%;
159
+ max-width: 100%;
160
+ overflow: hidden;
161
+ position: relative;
162
+ box-sizing: border-box;
163
+ display: block; /* 确保作为块级元素占满整行 */
164
+ }
165
+
166
+ #shopify-block-{{ block.id }} .active-box-content {
167
+ display: none;
168
+ margin: 0 !important;
169
+ }
170
+
171
+ #shopify-block-{{ block.id }} .media-box {
172
+ height: 100%;
173
+ }
174
+
175
+ @media (max-width: 749px) {
176
+ #shopify-block-{{ block.id }} {
177
+ --marquee-height: {{ block.settings.mobile_height | default: 250 }}px;
178
+ --marquee-gap: {{ block.settings.gap_mobile | default: 12 }}px;
179
+
180
+ {% if block.settings.card_width_mobile > 0 %}
181
+ --card-width: {{ block.settings.card_width_mobile }}px;
182
+ --child-width: 100%;
183
+ {% else %}
184
+ --card-width: auto;
185
+ --child-width: auto;
186
+ {% endif %}
187
+ }
188
+ }
189
+
190
+ /* 宽度设置:占满父容器 100% 宽度 */
191
+ #shopify-block-{{ block.id }} .zz-marquee-container {
192
+ width: 100%;
193
+ position: relative;
194
+ overflow: hidden;
195
+ }
196
+
197
+ #shopify-block-{{ block.id }} .zz-marquee-swiper {
198
+ height: var(--marquee-height);
199
+ width: 100%;
200
+ /* 移除 grab 手势,参考 comment-splide 使用默认或指针样式 */
201
+ cursor: default;
202
+ }
203
+
204
+ /* 核心核心:强制线性过渡曲线,消除 Swiper 的加速减速感 */
205
+ #shopify-block-{{ block.id }} .swiper-wrapper {
206
+ transition-timing-function: linear !important;
207
+ }
208
+
209
+ #shopify-block-{{ block.id }} .swiper-slide {
210
+ width: var(--card-width);
211
+ height: 100%;
212
+ display: flex;
213
+ align-items: center;
214
+ justify-content: center;
215
+ margin-right: var(--marquee-gap);
216
+ overflow: hidden; /* Fix: Ensure internal media is clipped to the card shape */
217
+ border-radius: 16px; /* Desktop default */
218
+ }
219
+
220
+ @media (max-width: 749px) {
221
+ #shopify-block-{{ block.id }} .swiper-slide {
222
+ border-radius: 12px; /* Mobile default */
223
+ }
224
+ }
225
+
226
+ #shopify-block-{{ block.id }} .swiper-slide > * {
227
+ height: 100% !important;
228
+ width: var(--child-width) !important;
229
+ max-width: var(--child-width) !important;
230
+ }
231
+
232
+ /* 强力覆盖子元素样式:针对 zz-video-img-item 等可能自带宽高的子组件 */
233
+ #shopify-block-{{ block.id }} .swiper-slide > [id^="shopify-block-"] {
234
+ width: var(--child-width) !important;
235
+ height: 100% !important;
236
+ margin: 0 !important;
237
+ transform: none !important;
238
+ }
239
+ /* 覆盖子组件内部媒体元素 */
240
+ #shopify-block-{{ block.id }} .swiper-slide .image-video-box,
241
+ #shopify-block-{{ block.id }} .swiper-slide video,
242
+ #shopify-block-{{ block.id }} .swiper-slide img {
243
+ width: var(--child-width) !important;
244
+ height: 100% !important;
245
+ object-fit: cover !important;
246
+ aspect-ratio: auto !important; /* 解除原有比例限制,听从 marquee 宽高 */
247
+ }
248
+ </style>
249
+
250
+ <div class="zz-marquee-container" id="zz-marquee-{{ block.id }}">
251
+ <div class="zz-marquee-swiper swiper">
252
+ <div class="swiper-wrapper">
253
+ {% comment %}
254
+ 最佳实践:智能循环复制
255
+ Swiper loop 模式要求内容宽度至少达到容器宽度的 2 倍。
256
+ 我们这里动态计算需要复制多少份,既保证了 Loop 稳定性,又避免了 DOM 过度膨胀。
257
+ 目标:确保 Slide 总数至少达到 10 个以上 (或者更多,视宽度而定)。
258
+ {% endcomment %}
259
+ {% assign min_slides_target = 12 %}
260
+ {% assign block_size = block.blocks.size | default: 1 %}
261
+ {% if block_size == 0 %}{% assign block_size = 1 %}{% endif %}
262
+
263
+ {% assign loop_count = min_slides_target | divided_by: block_size | ceil %}
264
+ {% if loop_count < 2 %}{% assign loop_count = 2 %}{% endif %}
265
+ {% if loop_count > 10 %}{% assign loop_count = 10 %}{% endif %}
266
+
267
+ {% for i in (1..loop_count) %}
268
+ {% for slide_block in block.blocks %}
269
+ <div class="swiper-slide">
270
+ {% render slide_block %}
271
+ </div>
272
+ {% endfor %}
273
+ {% endfor %}
274
+ </div>
275
+ </div>
276
+ </div>
277
+
278
+ <script>
279
+ (function() {
280
+ const initMarquee = () => {
281
+ const blockContainer = document.querySelector('#zz-marquee-{{ block.id }}');
282
+ const swiperContainer = blockContainer.querySelector('.zz-marquee-swiper');
283
+ if (!swiperContainer || swiperContainer.swiper) return;
284
+
285
+ const speed = parseInt({{ block.settings.marquee_speed | default: 8000 }});
286
+ const direction = '{{ block.settings.direction | default: "rtl" }}';
287
+ const pauseOnHover = {{ block.settings.pause_on_hover | default: true }};
288
+ const enableGrab = {{ block.settings.enable_grab | default: true }};
289
+ const videoAutoplay = {{ block.settings.video_autoplay | default: true }};
290
+
291
+ const enableAutoplayPc = {{ block.settings.enable_autoplay_pc | json }};
292
+ const enableAutoplayMobile = {{ block.settings.enable_autoplay_mobile| json }};
293
+ const isMobile = window.innerWidth < 750;
294
+ const shouldAutoplay = isMobile ? enableAutoplayMobile : enableAutoplayPc;
295
+
296
+ const swiper = new Swiper(swiperContainer, {
297
+ loop: true,
298
+ speed: speed,
299
+ freeMode: true,
300
+ slidesPerView: 'auto',
301
+ spaceBetween: 0,
302
+ centeredSlides: true,
303
+ allowTouchMove: enableGrab,
304
+ autoplay: shouldAutoplay ? {
305
+ delay: 0,
306
+ disableOnInteraction: false,
307
+ // pauseOnMouseEnter 在 linear marquee 中可能不够稳定,下面将使用手动监听
308
+ reverseDirection: direction === 'ltr'
309
+ } : false,
310
+ // 关键:确保有足够的 Slide 进行 Loop 计算
311
+ loopedSlides: 20,
312
+ observer: true,
313
+ observeParents: true,
314
+ });
315
+
316
+ // 暴力启动,防止某些情况下 linear 不自动开始
317
+ if (shouldAutoplay) {
318
+ swiper.autoplay.start();
319
+ }
320
+
321
+ let lastNeedSwiperSpeed = speed;
322
+
323
+ const handlePause = () => {
324
+ try {
325
+ if (!swiper.autoplay.running) return;
326
+ const currentTranslate = swiper.getTranslate();
327
+ const targetTranslate = swiper.translate;
328
+ const slides = swiper.slides;
329
+ const activeIndex = swiper.activeIndex;
330
+
331
+ if (slides && slides[activeIndex]) {
332
+ const currentSlideWidth = slides[activeIndex].offsetWidth;
333
+ const spaceBetween = swiper.params.spaceBetween || 0;
334
+ // 计算当前移动比例,从而得出恢复时需要的速度(剩余时间)
335
+ lastNeedSwiperSpeed = (Math.abs(Math.abs(currentTranslate) - Math.abs(targetTranslate)) / (currentSlideWidth + spaceBetween)) * speed;
336
+ }
337
+
338
+ swiper.setTranslate(currentTranslate);
339
+ swiper.autoplay.stop();
340
+ } catch (e) {
341
+ console.error('Marquee pause error:', e);
342
+ }
343
+ };
344
+
345
+ const handleResume = () => {
346
+ try {
347
+ // 使用计算出的剩余时间滑动到目标位置,然后重启 autoplay
348
+ swiper.slideTo(swiper.activeIndex, lastNeedSwiperSpeed);
349
+ setTimeout(() => {
350
+ if (shouldAutoplay) swiper.autoplay.start();
351
+ }, 300);
352
+ } catch (e) {
353
+ console.error('Marquee resume error:', e);
354
+ }
355
+ };
356
+
357
+ // 1. 手动处理悬停暂停 (参考 comment-splide / comments-swiper-contral.js 逻辑)
358
+ if (pauseOnHover && shouldAutoplay) {
359
+ swiperContainer.addEventListener('mouseenter', handlePause);
360
+ swiperContainer.addEventListener('mouseleave', handleResume);
361
+ }
362
+
363
+ // 2. 视频控制逻辑
364
+ const controlVideos = (play) => {
365
+ if (!videoAutoplay) return;
366
+ const videos = swiperContainer.querySelectorAll('video');
367
+ videos.forEach(v => {
368
+ if (play) {
369
+ v.play().catch(() => {});
370
+ } else {
371
+ v.pause();
372
+ }
373
+ });
374
+ };
375
+
376
+ // 3. 性能优化:仅在视口内播放
377
+ const observer = new IntersectionObserver((entries) => {
378
+ entries.forEach(entry => {
379
+ if (entry.isIntersecting) {
380
+ if (shouldAutoplay) swiper.autoplay.start();
381
+ controlVideos(true);
382
+ } else {
383
+ if (shouldAutoplay) swiper.autoplay.stop();
384
+ controlVideos(false);
385
+ }
386
+ });
387
+ }, { threshold: 0.1 });
388
+
389
+ observer.observe(swiperContainer);
390
+ };
391
+
392
+ if (document.readyState === 'loading') {
393
+ document.addEventListener('DOMContentLoaded', initMarquee);
394
+ } else {
395
+ initMarquee();
396
+ }
397
+ })();
398
+ </script>