zz-shopify-components 0.36.1-beta.5 → 0.36.1-beta.6
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.
package/package.json
CHANGED
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
{%- comment -%}
|
|
2
|
+
Products Specs comparison table.
|
|
3
|
+
|
|
4
|
+
Two block types:
|
|
5
|
+
- "product": a column header (product select + image + description).
|
|
6
|
+
- "specs": a row of spec values, one value per product.
|
|
7
|
+
|
|
8
|
+
Product columns render in block order. For each spec row, the value shown
|
|
9
|
+
under a product is chosen from the spec block's fields according to that
|
|
10
|
+
product's selected type (X1 / X1 PRO / X1 PROMAX / AQUA).
|
|
11
|
+
{%- endcomment -%}
|
|
12
|
+
|
|
13
|
+
{%- assign product_blocks = section.blocks | where: 'type', 'product' -%}
|
|
14
|
+
{%- assign spec_blocks = section.blocks | where: 'type', 'specs' -%}
|
|
15
|
+
{%- assign product_block_count = product_blocks.size | at_least: 1 -%}
|
|
16
|
+
{%- assign uid = section.id -%}
|
|
17
|
+
|
|
18
|
+
<div
|
|
19
|
+
id="ProductsSpecs-{{ uid }}" class="tw-box-border"
|
|
20
|
+
data-zz-module-name="compare-id"
|
|
21
|
+
>
|
|
22
|
+
<div class="tw-box-border tw-mx-auto tw-max-w-[1220px] lg:tw-py-[80px] max-lg:tw-pt-[48px] max-lg:tw-pb-[30px]">
|
|
23
|
+
{%- if section.settings.title != blank -%}
|
|
24
|
+
<div class="tw-mb-[3rem] lg:tw-mb-[100px] tw-text-center tw-font-bold tw-text-[#2d2d2d] tw-leading-[1] tw-text-[2.8rem] lg:tw-text-[40px]">
|
|
25
|
+
{{ section.settings.title | escape }}
|
|
26
|
+
</div>
|
|
27
|
+
{%- endif -%}
|
|
28
|
+
|
|
29
|
+
<div class="max-lg:tw-overflow-x-auto tw-px-[20px] lg:tw-px-[40px] tw-py-[30px] lg:tw-py-[60px] tw-rounded-[10px] lg:tw-rounded-[16px] tw-bg-[#fafafa] [-webkit-overflow-scrolling:touch]" data-specs-scroll>
|
|
30
|
+
<div class="products-specs-content" data-specs-content>
|
|
31
|
+
{%- comment -%} Header: empty first cell + one column per product {%- endcomment -%}
|
|
32
|
+
<div class="tw-flex tw-items-stretch lg:tw-pb-[40px] max-lg:tw-pb-[30px]" data-specs-header>
|
|
33
|
+
<div class="lg:tw-w-[220px] tw-shrink-0 max-lg:tw-hidden"> </div>
|
|
34
|
+
<div class="tw-flex tw-gap-[20px] lg:tw-gap-[30px] tw-flex-1" data-specs-header-row>
|
|
35
|
+
{%- for product in product_blocks -%}
|
|
36
|
+
<div
|
|
37
|
+
class="value-item tw-box-border tw-flex tw-flex-col tw-items-start tw-gap-[10px] lg:tw-gap-[12px]"
|
|
38
|
+
data-specs-product-col
|
|
39
|
+
data-specs-heading="{{ product.settings.heading | escape }}"
|
|
40
|
+
{{ product.shopify_attributes }}
|
|
41
|
+
>
|
|
42
|
+
{% assign product_all = all_products[product.settings.product] %}
|
|
43
|
+
{% assign variant_id = product.settings.variant_id | times: 1 %}
|
|
44
|
+
{% assign variant = product_all.variants | where: 'id', variant_id | first %}
|
|
45
|
+
{%- if product.settings.image != blank -%}
|
|
46
|
+
{{
|
|
47
|
+
product.settings.image
|
|
48
|
+
| image_url: width: 400
|
|
49
|
+
| image_tag:
|
|
50
|
+
class: 'product-image tw-w-[128px] lg:tw-w-[168px] tw-object-contain tw-object-center tw-aspect-[168/132]',
|
|
51
|
+
loading: 'lazy',
|
|
52
|
+
alt: product.settings.heading
|
|
53
|
+
}}
|
|
54
|
+
{%- endif -%}
|
|
55
|
+
<div class="tw-break-words tw-font-bold tw-leading-[1.5] tw-mt-[10px] lg:tw-mt-[18px] tw-text-[#2d2d2d] lg:tw-text-[28px] max-lg:tw-text-[1.8rem]">{{ product.settings.heading | escape }}</div>
|
|
56
|
+
|
|
57
|
+
{%- if product.settings.description != blank -%}
|
|
58
|
+
<div class="tw-break-words tw-text-[1.2rem] lg:tw-text-[14px] tw-font-bold tw-leading-[1.5] tw-text-[#2d2d2d]">{{ product.settings.description | escape | newline_to_br }}</div>
|
|
59
|
+
{%- endif -%}
|
|
60
|
+
<div class="tw-align-middle tw-text-center tw-text-[1.4rem] lg:tw-text-[20px] tw-leading-[1.5] tw-tracking-[0px] tw-text-[#2D2D2DCC]">{{ section.settings.price_prefix }} {{ variant.price | money_without_trailing_zeros }}</div>
|
|
61
|
+
</div>
|
|
62
|
+
{%- endfor -%}
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
|
|
66
|
+
{%- comment -%} Spec rows: name cell + value per product (ordered by header) {%- endcomment -%}
|
|
67
|
+
{%- for spec in spec_blocks -%}
|
|
68
|
+
<div class="tw-flex tw-items-stretch tw-border-b max-lg:tw-w-fit tw-border-solid tw-border-[rgba(45,45,45,0.1)] tw-py-[15px] lg:tw-py-[30px] max-lg:tw-flex-col max-lg:tw-items-start" {{ spec.shopify_attributes }}>
|
|
69
|
+
<div class="tw-break-words tw-font-bold tw-leading-[1.5] tw-text-[#2d2d2d] lg:tw-w-[220px] lg:tw-text-[16px] max-lg:tw-w-[14rem] max-lg:tw-mb-[10px] max-lg:tw-shrink-0 max-lg:tw-text-[12px]">{{ spec.settings.heading | escape | newline_to_br }}</div>
|
|
70
|
+
<div class="tw-flex tw-flex-1 tw-items-center tw-gap-[20px] lg:tw-gap-[30px]">
|
|
71
|
+
{%- for product in product_blocks -%}
|
|
72
|
+
{%- case product.settings.heading -%}
|
|
73
|
+
{%- when 'X1' -%}
|
|
74
|
+
{%- assign cell_value = spec.settings.x1_value -%}
|
|
75
|
+
{%- when 'X1 PRO' -%}
|
|
76
|
+
{%- assign cell_value = spec.settings.x1_pro_value -%}
|
|
77
|
+
{%- when 'X1 PROMAX' -%}
|
|
78
|
+
{%- assign cell_value = spec.settings.x1_promax_value -%}
|
|
79
|
+
{%- when 'AQUA' -%}
|
|
80
|
+
{%- assign cell_value = spec.settings.aqua_value -%}
|
|
81
|
+
{%- else -%}
|
|
82
|
+
{%- assign cell_value = '' -%}
|
|
83
|
+
{%- endcase -%}
|
|
84
|
+
<div class="value-item tw-break-words tw-text-[10px] lg:tw-text-[16px] tw-leading-[1.5] tw-text-[rgba(45,45,45,0.8)]"
|
|
85
|
+
>{{ cell_value | escape | newline_to_br }}</div>
|
|
86
|
+
{%- endfor -%}
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
89
|
+
{%- endfor -%}
|
|
90
|
+
</div>
|
|
91
|
+
</div>
|
|
92
|
+
</div>
|
|
93
|
+
|
|
94
|
+
<div class="specs-floating-head" data-specs-float aria-hidden="true">
|
|
95
|
+
<div class="specs-floating-head__inner" data-specs-float-inner>
|
|
96
|
+
<div class="specs-floating-head__spacer max-lg:tw-hidden" data-specs-float-spacer> </div>
|
|
97
|
+
<div class="specs-floating-head__row" data-specs-float-row>
|
|
98
|
+
{%- for product in product_blocks -%}
|
|
99
|
+
<div class="specs-floating-head__cell value-item tw-font-bold tw-leading-[1.5] tw-mt-[10px] lg:tw-mt-[18px] tw-text-[#2d2d2d] lg:tw-text-[28px] max-lg:tw-text-[1.8rem]" data-specs-float-cell>{{ product.settings.heading | escape }}</div>
|
|
100
|
+
{%- endfor -%}
|
|
101
|
+
</div>
|
|
102
|
+
</div>
|
|
103
|
+
</div>
|
|
104
|
+
</div>
|
|
105
|
+
|
|
106
|
+
<style>
|
|
107
|
+
@media screen and (max-width: 1023px) {
|
|
108
|
+
#shopify-section-{{section.id}} .value-item {
|
|
109
|
+
width: 14.5rem;
|
|
110
|
+
flex-shrink: 0;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
@media screen and (min-width: 1023px) {
|
|
114
|
+
#shopify-section-{{section.id}} .value-item {
|
|
115
|
+
flex-basis: calc(100% / {{ product_block_count }});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
{%- comment -%} Floating heading row: fixed to viewport, hidden by default {%- endcomment -%}
|
|
120
|
+
#shopify-section-{{ section.id }} .specs-floating-head {
|
|
121
|
+
position: fixed;
|
|
122
|
+
top: 0;
|
|
123
|
+
left: 0;
|
|
124
|
+
z-index: 30;
|
|
125
|
+
display: none;
|
|
126
|
+
overflow: hidden;
|
|
127
|
+
background: #fafafa;
|
|
128
|
+
box-shadow: 0 4px 12px rgba(45, 45, 45, 0.06);
|
|
129
|
+
pointer-events: none;
|
|
130
|
+
}
|
|
131
|
+
#shopify-section-{{ section.id }} .specs-floating-head.is-visible {
|
|
132
|
+
display: block;
|
|
133
|
+
}
|
|
134
|
+
#shopify-section-{{ section.id }} .specs-floating-head__inner {
|
|
135
|
+
display: flex;
|
|
136
|
+
align-items: stretch;
|
|
137
|
+
padding-top: 15px;
|
|
138
|
+
padding-bottom: 15px;
|
|
139
|
+
will-change: transform;
|
|
140
|
+
}
|
|
141
|
+
#shopify-section-{{ section.id }} .specs-floating-head__spacer {
|
|
142
|
+
flex-shrink: 0;
|
|
143
|
+
}
|
|
144
|
+
#shopify-section-{{ section.id }} .specs-floating-head__row {
|
|
145
|
+
display: flex;
|
|
146
|
+
flex: 1 1 auto;
|
|
147
|
+
gap: 20px;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
@media screen and (min-width: 1024px) {
|
|
151
|
+
#shopify-section-{{ section.id }} .specs-floating-head__inner {
|
|
152
|
+
padding-top: 18px;
|
|
153
|
+
padding-bottom: 18px;
|
|
154
|
+
}
|
|
155
|
+
#shopify-section-{{ section.id }} .specs-floating-head__row {
|
|
156
|
+
gap: 30px;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
}
|
|
160
|
+
</style>
|
|
161
|
+
|
|
162
|
+
<script>
|
|
163
|
+
(function () {
|
|
164
|
+
var root = document.getElementById('ProductsSpecs-{{ uid }}');
|
|
165
|
+
if (!root || root.dataset.specsFloatBound === 'true') { return; }
|
|
166
|
+
root.dataset.specsFloatBound = 'true';
|
|
167
|
+
|
|
168
|
+
var scroller = root.querySelector('[data-specs-scroll]');
|
|
169
|
+
var content = root.querySelector('[data-specs-content]');
|
|
170
|
+
var header = root.querySelector('[data-specs-header]');
|
|
171
|
+
var headerRow = root.querySelector('[data-specs-header-row]');
|
|
172
|
+
var floatEl = root.querySelector('[data-specs-float]');
|
|
173
|
+
var floatInner = root.querySelector('[data-specs-float-inner]');
|
|
174
|
+
var floatSpacer = root.querySelector('[data-specs-float-spacer]');
|
|
175
|
+
var floatRow = root.querySelector('[data-specs-float-row]');
|
|
176
|
+
var srcCols = Array.prototype.slice.call(root.querySelectorAll('[data-specs-product-col]'));
|
|
177
|
+
var floatCells = Array.prototype.slice.call(root.querySelectorAll('[data-specs-float-cell]'));
|
|
178
|
+
|
|
179
|
+
if (!scroller || !content || !header || !headerRow || !floatEl) { return; }
|
|
180
|
+
|
|
181
|
+
var isVisible = false;
|
|
182
|
+
var ticking = false;
|
|
183
|
+
|
|
184
|
+
{%- comment -%} Height of the visible content-nagivation bar (mobile vs pc) {%- endcomment -%}
|
|
185
|
+
function getNavHeight() {
|
|
186
|
+
var pc = document.querySelector('.content-nagivation-pc');
|
|
187
|
+
var mobile = document.querySelector('.content-nagivation-mobile');
|
|
188
|
+
{%- comment -%} the hidden one (wrong breakpoint) reports offsetHeight 0 {%- endcomment -%}
|
|
189
|
+
var pcH = pc ? pc.offsetHeight : 0;
|
|
190
|
+
var mobileH = mobile ? mobile.offsetHeight : 0;
|
|
191
|
+
return Math.max(pcH, mobileH, 0);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
{%- comment -%} Match float bar geometry to the scroller (width, left, padding, column sizing) {%- endcomment -%}
|
|
195
|
+
function syncLayout() {
|
|
196
|
+
var scRect = scroller.getBoundingClientRect();
|
|
197
|
+
var cs = window.getComputedStyle(scroller);
|
|
198
|
+
var padLeft = parseFloat(cs.paddingLeft) || 0;
|
|
199
|
+
var padRight = parseFloat(cs.paddingRight) || 0;
|
|
200
|
+
|
|
201
|
+
floatEl.style.left = scRect.left + 'px';
|
|
202
|
+
floatEl.style.top = getNavHeight() + 'px';
|
|
203
|
+
floatEl.style.width = scRect.width + 'px';
|
|
204
|
+
floatInner.style.paddingLeft = padLeft + 'px';
|
|
205
|
+
floatInner.style.paddingRight = padRight + 'px';
|
|
206
|
+
|
|
207
|
+
{%- comment -%} spacer = first empty label cell width (desktop only; hidden on mobile) {%- endcomment -%}
|
|
208
|
+
var firstEmpty = header.firstElementChild;
|
|
209
|
+
if (firstEmpty && floatSpacer) {
|
|
210
|
+
floatSpacer.style.width = firstEmpty.getBoundingClientRect().width + 'px';
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
{%- comment -%} match each float cell width to its source column {%- endcomment -%}
|
|
214
|
+
for (var i = 0; i < floatCells.length; i++) {
|
|
215
|
+
var src = srcCols[i];
|
|
216
|
+
if (!src) { continue; }
|
|
217
|
+
var w = src.getBoundingClientRect().width;
|
|
218
|
+
floatCells[i].style.flex = '0 0 ' + w + 'px';
|
|
219
|
+
floatCells[i].style.width = w + 'px';
|
|
220
|
+
}
|
|
221
|
+
{%- comment -%} match the header row gap so cells align 1:1 {%- endcomment -%}
|
|
222
|
+
var rowCs = window.getComputedStyle(headerRow);
|
|
223
|
+
floatRow.style.gap = rowCs.columnGap || rowCs.gap || '20px';
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
{%- comment -%} A方案核心: follow horizontal scroll of the table {%- endcomment -%}
|
|
227
|
+
function syncScrollX() {
|
|
228
|
+
floatInner.style.transform = 'translateX(' + (-scroller.scrollLeft) + 'px)';
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function update() {
|
|
232
|
+
ticking = false;
|
|
233
|
+
var navH = getNavHeight();
|
|
234
|
+
var headRect = header.getBoundingClientRect();
|
|
235
|
+
var contentRect = content.getBoundingClientRect();
|
|
236
|
+
var floatH = floatEl.offsetHeight || headRect.height;
|
|
237
|
+
|
|
238
|
+
{%- comment -%} show once product row scrolls under the nav, hide when content bottom passes {%- endcomment -%}
|
|
239
|
+
var shouldShow = headRect.bottom <= navH && contentRect.bottom > navH + floatH;
|
|
240
|
+
|
|
241
|
+
if (shouldShow && !isVisible) {
|
|
242
|
+
isVisible = true;
|
|
243
|
+
syncLayout();
|
|
244
|
+
syncScrollX();
|
|
245
|
+
floatEl.classList.add('is-visible');
|
|
246
|
+
floatEl.setAttribute('aria-hidden', 'false');
|
|
247
|
+
} else if (!shouldShow && isVisible) {
|
|
248
|
+
isVisible = false;
|
|
249
|
+
floatEl.classList.remove('is-visible');
|
|
250
|
+
floatEl.setAttribute('aria-hidden', 'true');
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function onScroll() {
|
|
255
|
+
if (!ticking) {
|
|
256
|
+
ticking = true;
|
|
257
|
+
window.requestAnimationFrame(update);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
window.addEventListener('scroll', onScroll, { passive: true });
|
|
262
|
+
window.addEventListener('resize', function () {
|
|
263
|
+
if (isVisible) { syncLayout(); syncScrollX(); }
|
|
264
|
+
onScroll();
|
|
265
|
+
}, { passive: true });
|
|
266
|
+
scroller.addEventListener('scroll', function () {
|
|
267
|
+
if (isVisible) { syncScrollX(); }
|
|
268
|
+
}, { passive: true });
|
|
269
|
+
|
|
270
|
+
{%- comment -%} initial run (defer to allow images/layout to settle) {%- endcomment -%}
|
|
271
|
+
if (document.readyState === 'complete') {
|
|
272
|
+
update();
|
|
273
|
+
} else {
|
|
274
|
+
window.addEventListener('load', update, { once: true });
|
|
275
|
+
}
|
|
276
|
+
update();
|
|
277
|
+
})();
|
|
278
|
+
</script>
|
|
279
|
+
|
|
280
|
+
{% schema %}
|
|
281
|
+
{
|
|
282
|
+
"name": "ZZ Products Specs",
|
|
283
|
+
"tag": "section",
|
|
284
|
+
"class": "products-specs-section",
|
|
285
|
+
"settings": [
|
|
286
|
+
{
|
|
287
|
+
"type": "text",
|
|
288
|
+
"id": "title",
|
|
289
|
+
"label": "Section title",
|
|
290
|
+
"default": "Specs"
|
|
291
|
+
},
|
|
292
|
+
{
|
|
293
|
+
"type": "text",
|
|
294
|
+
"id": "price_prefix",
|
|
295
|
+
"label": "Price prefix",
|
|
296
|
+
"default": "from"
|
|
297
|
+
}
|
|
298
|
+
],
|
|
299
|
+
"blocks": [
|
|
300
|
+
{
|
|
301
|
+
"type": "product",
|
|
302
|
+
"name": "Product",
|
|
303
|
+
"settings": [
|
|
304
|
+
{
|
|
305
|
+
"type": "select",
|
|
306
|
+
"id": "heading",
|
|
307
|
+
"label": "Product",
|
|
308
|
+
"options": [
|
|
309
|
+
{ "value": "X1", "label": "X1" },
|
|
310
|
+
{ "value": "X1 PRO", "label": "X1 PRO" },
|
|
311
|
+
{ "value": "X1 PROMAX", "label": "X1 PROMAX" },
|
|
312
|
+
{ "value": "AQUA", "label": "AQUA" }
|
|
313
|
+
],
|
|
314
|
+
"default": "X1"
|
|
315
|
+
},
|
|
316
|
+
{
|
|
317
|
+
"type": "product",
|
|
318
|
+
"id": "product",
|
|
319
|
+
"label": "Product",
|
|
320
|
+
"info": "选择产品,获取价格"
|
|
321
|
+
},
|
|
322
|
+
{
|
|
323
|
+
"type": "text",
|
|
324
|
+
"id": "variant_id",
|
|
325
|
+
"label": "Variant ID",
|
|
326
|
+
"placeholder": "变体ID",
|
|
327
|
+
"info": "输入变体ID,变体ID可以在产品编辑页面查看,获取价格"
|
|
328
|
+
},
|
|
329
|
+
{
|
|
330
|
+
"type": "image_picker",
|
|
331
|
+
"id": "image",
|
|
332
|
+
"label": "Product image"
|
|
333
|
+
},
|
|
334
|
+
{
|
|
335
|
+
"type": "textarea",
|
|
336
|
+
"id": "description",
|
|
337
|
+
"label": "Description"
|
|
338
|
+
}
|
|
339
|
+
]
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
"type": "specs",
|
|
343
|
+
"name": "Spec",
|
|
344
|
+
"settings": [
|
|
345
|
+
{
|
|
346
|
+
"type": "textarea",
|
|
347
|
+
"id": "heading",
|
|
348
|
+
"label": "Spec name"
|
|
349
|
+
},
|
|
350
|
+
{
|
|
351
|
+
"type": "textarea",
|
|
352
|
+
"id": "x1_value",
|
|
353
|
+
"label": "X1 value"
|
|
354
|
+
},
|
|
355
|
+
{
|
|
356
|
+
"type": "textarea",
|
|
357
|
+
"id": "x1_pro_value",
|
|
358
|
+
"label": "X1 PRO value"
|
|
359
|
+
},
|
|
360
|
+
{
|
|
361
|
+
"type": "textarea",
|
|
362
|
+
"id": "x1_promax_value",
|
|
363
|
+
"label": "X1 PROMAX value"
|
|
364
|
+
},
|
|
365
|
+
{
|
|
366
|
+
"type": "textarea",
|
|
367
|
+
"id": "aqua_value",
|
|
368
|
+
"label": "AQUA value"
|
|
369
|
+
}
|
|
370
|
+
]
|
|
371
|
+
}
|
|
372
|
+
],
|
|
373
|
+
"presets": [
|
|
374
|
+
{
|
|
375
|
+
"name": "ZZ Products Specs",
|
|
376
|
+
"blocks": [
|
|
377
|
+
{ "type": "product" },
|
|
378
|
+
{ "type": "product" },
|
|
379
|
+
{ "type": "product" },
|
|
380
|
+
{ "type": "product" },
|
|
381
|
+
{ "type": "specs" },
|
|
382
|
+
{ "type": "specs" },
|
|
383
|
+
{ "type": "specs" }
|
|
384
|
+
]
|
|
385
|
+
}
|
|
386
|
+
]
|
|
387
|
+
}
|
|
388
|
+
{% endschema %}
|
|
@@ -0,0 +1,558 @@
|
|
|
1
|
+
{%- liquid
|
|
2
|
+
assign video_blocks = section.blocks | where: 'type', 'video'
|
|
3
|
+
assign uid = section.id
|
|
4
|
+
-%}
|
|
5
|
+
|
|
6
|
+
<style>
|
|
7
|
+
#shopify-section-{{ section.id }} {
|
|
8
|
+
background: {{ section.settings.bg_color }};
|
|
9
|
+
}
|
|
10
|
+
.zz-vc-{{ uid }} {
|
|
11
|
+
padding-top: {{ section.settings.mobile_padding_top }}px;
|
|
12
|
+
padding-bottom: {{ section.settings.mobile_padding_bottom }}px;
|
|
13
|
+
overflow: hidden;
|
|
14
|
+
}
|
|
15
|
+
.zz-vc-{{ uid }} .zz-vc__title {
|
|
16
|
+
color: {{ section.settings.title_color }};
|
|
17
|
+
text-align: center;
|
|
18
|
+
font-weight: 700;
|
|
19
|
+
font-size: 28px;
|
|
20
|
+
line-height: normal;
|
|
21
|
+
padding: 0 2rem;
|
|
22
|
+
margin: 0;
|
|
23
|
+
}
|
|
24
|
+
.zz-vc-{{ uid }} .zz-vc__head {
|
|
25
|
+
margin-bottom: 30px;
|
|
26
|
+
}
|
|
27
|
+
.zz-vc-{{ uid }} .zz-vc__viewport {
|
|
28
|
+
position: relative;
|
|
29
|
+
width: 100%;
|
|
30
|
+
}
|
|
31
|
+
.zz-vc-{{ uid }} .zz-vc-swiper {
|
|
32
|
+
width: 100%;
|
|
33
|
+
overflow: hidden;
|
|
34
|
+
}
|
|
35
|
+
.zz-vc-{{ uid }} .swiper-wrapper {
|
|
36
|
+
align-items: flex-start;
|
|
37
|
+
}
|
|
38
|
+
.zz-vc-{{ uid }} .zz-vc__card {
|
|
39
|
+
position: relative;
|
|
40
|
+
overflow: hidden;
|
|
41
|
+
background: #fff;
|
|
42
|
+
}
|
|
43
|
+
.zz-vc-{{ uid }} .zz-vc__media {
|
|
44
|
+
position: absolute;
|
|
45
|
+
inset: 0;
|
|
46
|
+
width: 100%;
|
|
47
|
+
height: 100%;
|
|
48
|
+
}
|
|
49
|
+
.zz-vc-{{ uid }} .zz-vc__media img,
|
|
50
|
+
.zz-vc-{{ uid }} .zz-vc__media video {
|
|
51
|
+
width: 100% !important;
|
|
52
|
+
height: 100% !important;
|
|
53
|
+
object-fit: cover;
|
|
54
|
+
}
|
|
55
|
+
.zz-vc-{{ uid }} .zz-vc__caption {
|
|
56
|
+
position: absolute;
|
|
57
|
+
left: 0;
|
|
58
|
+
right: 0;
|
|
59
|
+
bottom: 0;
|
|
60
|
+
display: flex;
|
|
61
|
+
align-items: center;
|
|
62
|
+
padding: 20px;
|
|
63
|
+
font-weight: 700;
|
|
64
|
+
font-size: 18px;
|
|
65
|
+
line-height: normal;
|
|
66
|
+
word-break: break-word;
|
|
67
|
+
background: linear-gradient(to top, rgba(0, 0, 0, 0.68), rgba(0, 0, 0, 0));
|
|
68
|
+
pointer-events: none;
|
|
69
|
+
}
|
|
70
|
+
.zz-vc-{{ uid }} .zz-vc__nav {
|
|
71
|
+
display: none;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/* ------- Mobile: one card per swipe (offset handled by Swiper) ------- */
|
|
75
|
+
@media screen and (max-width: 1023.98px) {
|
|
76
|
+
.zz-vc-{{ uid }} .zz-vc__card {
|
|
77
|
+
/* 2rem left offset + ~40px peek of the next card on the right */
|
|
78
|
+
width: 30rem;
|
|
79
|
+
aspect-ratio: 3 / 4;
|
|
80
|
+
border-radius: 12px;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/* ------- PC: auto carousel + hover ------- */
|
|
85
|
+
@media screen and (min-width: 1024px) {
|
|
86
|
+
.zz-vc-{{ uid }} {
|
|
87
|
+
padding-top: {{ section.settings.pc_padding_top }}px;
|
|
88
|
+
padding-bottom: {{ section.settings.pc_padding_bottom }}px;
|
|
89
|
+
}
|
|
90
|
+
.zz-vc-{{ uid }} .zz-vc__title {
|
|
91
|
+
font-size: 40px;
|
|
92
|
+
padding: 0;
|
|
93
|
+
}
|
|
94
|
+
.zz-vc-{{ uid }} .zz-vc__head {
|
|
95
|
+
margin-bottom: 100px;
|
|
96
|
+
}
|
|
97
|
+
.zz-vc-{{ uid }} .zz-vc__card {
|
|
98
|
+
width: 318px;
|
|
99
|
+
height: 564px;
|
|
100
|
+
border-radius: 16px;
|
|
101
|
+
cursor: pointer;
|
|
102
|
+
}
|
|
103
|
+
.zz-vc-{{ uid }} .zz-vc__caption {
|
|
104
|
+
font-size: 20px;
|
|
105
|
+
}
|
|
106
|
+
.zz-vc-{{ uid }} .zz-vc__nav {
|
|
107
|
+
display: flex;
|
|
108
|
+
align-items: center;
|
|
109
|
+
justify-content: center;
|
|
110
|
+
position: absolute;
|
|
111
|
+
top: 50%;
|
|
112
|
+
transform: translateY(-50%);
|
|
113
|
+
width: 48px;
|
|
114
|
+
height: 48px;
|
|
115
|
+
border: none;
|
|
116
|
+
border-radius: 100px;
|
|
117
|
+
background: rgba(0, 0, 0, 0.3);
|
|
118
|
+
backdrop-filter: blur(5px);
|
|
119
|
+
-webkit-backdrop-filter: blur(5px);
|
|
120
|
+
cursor: pointer;
|
|
121
|
+
z-index: 2;
|
|
122
|
+
transition: opacity 0.3s;
|
|
123
|
+
}
|
|
124
|
+
.zz-vc-{{ uid }} .zz-vc__nav svg {
|
|
125
|
+
width: 24px;
|
|
126
|
+
height: 24px;
|
|
127
|
+
}
|
|
128
|
+
.zz-vc-{{ uid }} .zz-vc__nav.swiper-button-disabled {
|
|
129
|
+
opacity: 0;
|
|
130
|
+
pointer-events: none;
|
|
131
|
+
}
|
|
132
|
+
.zz-vc-{{ uid }} .zz-vc__prev {
|
|
133
|
+
left: 68px;
|
|
134
|
+
}
|
|
135
|
+
.zz-vc-{{ uid }} .zz-vc__prev svg {
|
|
136
|
+
transform: scaleX(-1);
|
|
137
|
+
}
|
|
138
|
+
.zz-vc-{{ uid }} .zz-vc__next {
|
|
139
|
+
right: 68px;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
</style>
|
|
143
|
+
|
|
144
|
+
<div
|
|
145
|
+
class="zz-vc-{{ uid }}"
|
|
146
|
+
data-zz-video-cards
|
|
147
|
+
{% if section.settings.module_id != blank %}
|
|
148
|
+
data-track-zz-section="{{ section.settings.module_id }}"
|
|
149
|
+
data-zz-module-name="{{ section.settings.module_id }}"
|
|
150
|
+
{% endif %}
|
|
151
|
+
>
|
|
152
|
+
{%- if section.settings.heading != blank -%}
|
|
153
|
+
<div class="zz-vc__head">
|
|
154
|
+
<h2 class="zz-vc__title">{{ section.settings.heading }}</h2>
|
|
155
|
+
</div>
|
|
156
|
+
{%- endif -%}
|
|
157
|
+
|
|
158
|
+
<div class="zz-vc__viewport max-lg:tw-pl-[2rem]">
|
|
159
|
+
<div class="swiper zz-vc-swiper zz-vc-swiper-{{ uid }}">
|
|
160
|
+
<div class="swiper-wrapper">
|
|
161
|
+
{%- for round in (1..2) -%}
|
|
162
|
+
{%- for block in video_blocks -%}
|
|
163
|
+
<div
|
|
164
|
+
class="swiper-slide zz-vc__card"
|
|
165
|
+
data-vc-index="{{ forloop.index0 }}"
|
|
166
|
+
{% if block.settings.video_pc == blank and block.settings.video_mb == blank %}
|
|
167
|
+
data-vc-image="true"
|
|
168
|
+
{% endif %}
|
|
169
|
+
{% if block.settings.block_module_id != blank %}
|
|
170
|
+
data-track-zz-element="{{ block.settings.block_module_id }}"
|
|
171
|
+
{% endif %}
|
|
172
|
+
{{ block.shopify_attributes }}
|
|
173
|
+
>
|
|
174
|
+
<div class="zz-vc__media">
|
|
175
|
+
{%- if block.settings.video_pc != blank or block.settings.video_mb != blank -%}
|
|
176
|
+
{% render 'zz-video',
|
|
177
|
+
pc_video: block.settings.video_pc,
|
|
178
|
+
pc_poster: block.settings.poster_pc,
|
|
179
|
+
mb_video: block.settings.video_mb,
|
|
180
|
+
mb_poster: block.settings.poster_mb,
|
|
181
|
+
lazy: true,
|
|
182
|
+
class: 'tw-w-full tw-h-full tw-object-cover',
|
|
183
|
+
autoplay: false,
|
|
184
|
+
loop: false,
|
|
185
|
+
muted: true,
|
|
186
|
+
module_id: block.settings.block_module_id
|
|
187
|
+
%}
|
|
188
|
+
{%- elsif block.settings.poster_pc != blank or block.settings.poster_mb != blank -%}
|
|
189
|
+
{% render 'zz-img',
|
|
190
|
+
pc_image: block.settings.poster_pc,
|
|
191
|
+
mb_image: block.settings.poster_mb,
|
|
192
|
+
image_alt: block.settings.heading,
|
|
193
|
+
id: block.settings.block_module_id
|
|
194
|
+
%}
|
|
195
|
+
{%- endif -%}
|
|
196
|
+
</div>
|
|
197
|
+
{%- if block.settings.heading != blank -%}
|
|
198
|
+
<div class="zz-vc__caption" style="color: {{ block.settings.title_color }};">
|
|
199
|
+
{{ block.settings.heading }}
|
|
200
|
+
</div>
|
|
201
|
+
{%- endif -%}
|
|
202
|
+
</div>
|
|
203
|
+
{%- endfor -%}
|
|
204
|
+
{%- endfor -%}
|
|
205
|
+
</div>
|
|
206
|
+
</div>
|
|
207
|
+
|
|
208
|
+
<button type="button" class="zz-vc__nav zz-vc__prev zz-vc__prev-{{ uid }}" aria-label="Previous">
|
|
209
|
+
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
210
|
+
<path d="M9 6l6 6-6 6" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
211
|
+
</svg>
|
|
212
|
+
</button>
|
|
213
|
+
<button type="button" class="zz-vc__nav zz-vc__next zz-vc__next-{{ uid }}" aria-label="Next">
|
|
214
|
+
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
215
|
+
<path d="M9 6l6 6-6 6" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
216
|
+
</svg>
|
|
217
|
+
</button>
|
|
218
|
+
</div>
|
|
219
|
+
</div>
|
|
220
|
+
<script>
|
|
221
|
+
document.addEventListener('DOMContentLoaded', function () {
|
|
222
|
+
var root = document.querySelector('#shopify-section-{{ section.id }} [data-zz-video-cards]');
|
|
223
|
+
if (!root) return;
|
|
224
|
+
var swiperEl = root.querySelector('.zz-vc-swiper-{{ uid }}');
|
|
225
|
+
var slides = Array.prototype.slice.call(root.querySelectorAll('.zz-vc__card'));
|
|
226
|
+
if (!swiperEl || !slides.length || typeof Swiper === 'undefined') return;
|
|
227
|
+
|
|
228
|
+
var IMAGE_DURATION = 10000;
|
|
229
|
+
var mq = window.matchMedia('(min-width: 1024px)');
|
|
230
|
+
var swiper = null;
|
|
231
|
+
var hovering = false;
|
|
232
|
+
var visible = false;
|
|
233
|
+
var imageTimer = null;
|
|
234
|
+
var armed = { video: null, fn: null };
|
|
235
|
+
|
|
236
|
+
function isDesktop() { return mq.matches; }
|
|
237
|
+
|
|
238
|
+
// zz-video renders up to two <video> tags:
|
|
239
|
+
// - PC video carries `max-lg:tw-hidden` (hidden below 1024px)
|
|
240
|
+
// - Mobile video carries `lg:tw-hidden` (hidden at/above 1024px)
|
|
241
|
+
// A single-video config has no hiding class. Match the one for the current
|
|
242
|
+
// breakpoint explicitly; never fall back to the wrong (hidden) one.
|
|
243
|
+
function cardVideo(card) {
|
|
244
|
+
var videos = card.querySelectorAll('video');
|
|
245
|
+
|
|
246
|
+
if (!videos.length) return null;
|
|
247
|
+
var pcVideo = null;
|
|
248
|
+
var mbVideo = null;
|
|
249
|
+
var plain = null;
|
|
250
|
+
for (var i = 0; i < videos.length; i++) {
|
|
251
|
+
var v = videos[i];
|
|
252
|
+
if (v.classList.contains('max-lg:tw-hidden')) pcVideo = v;
|
|
253
|
+
else if (v.classList.contains('lg:tw-hidden')) mbVideo = v;
|
|
254
|
+
else plain = v; // single-video config (no breakpoint class)
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (isDesktop()) return pcVideo || plain || null;
|
|
258
|
+
return mbVideo || plain || null;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Lazy <video> ships its url in <source data-src>; set it before first play.
|
|
262
|
+
function hydrate(video) {
|
|
263
|
+
if (!video || video.dataset.vcHydrated) return;
|
|
264
|
+
video.dataset.vcHydrated = '1';
|
|
265
|
+
Array.prototype.forEach.call(video.querySelectorAll('source'), function (s) {
|
|
266
|
+
if (!s.getAttribute('src') && s.dataset.src) s.setAttribute('src', s.dataset.src);
|
|
267
|
+
});
|
|
268
|
+
try { video.load(); } catch (e) {}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function clearImageTimer() {
|
|
272
|
+
if (imageTimer) { clearTimeout(imageTimer); imageTimer = null; }
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function disarm() {
|
|
276
|
+
if (armed.video && armed.fn) armed.video.removeEventListener('ended', armed.fn);
|
|
277
|
+
armed.video = null;
|
|
278
|
+
armed.fn = null;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Query videos live from the DOM so loop-mode clones are included too.
|
|
282
|
+
function pauseAllExcept(except) {
|
|
283
|
+
var videos = swiperEl.querySelectorAll('video');
|
|
284
|
+
Array.prototype.forEach.call(videos, function (v) {
|
|
285
|
+
if (v !== except) { try { v.pause(); } catch (e) {} }
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Advance one card; loop mode wraps back to the start automatically.
|
|
290
|
+
function goNext() {
|
|
291
|
+
if (!swiper) return;
|
|
292
|
+
swiper.slideNext();
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// The live active slide (may be a loop-mode clone, so read it from Swiper's DOM).
|
|
296
|
+
function activeCard() {
|
|
297
|
+
if (!swiper) return null;
|
|
298
|
+
return swiper.slides[swiper.activeIndex] || null;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Play the currently active card, then auto-advance when its media finishes.
|
|
302
|
+
function playActive() {
|
|
303
|
+
if (!swiper) return; // slideChange can fire during Swiper init
|
|
304
|
+
clearImageTimer();
|
|
305
|
+
disarm();
|
|
306
|
+
if (!visible) return; // section off screen -> don't start playback
|
|
307
|
+
if (hovering) return; // hover has taken over playback
|
|
308
|
+
var card = activeCard();
|
|
309
|
+
if (!card) return;
|
|
310
|
+
pauseAllExcept(null);
|
|
311
|
+
|
|
312
|
+
var video = cardVideo(card);
|
|
313
|
+
if (!video || card.dataset.vcImage === 'true') {
|
|
314
|
+
// image-only card -> hold 10s, then next
|
|
315
|
+
imageTimer = setTimeout(goNext, IMAGE_DURATION);
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
hydrate(video);
|
|
319
|
+
video.loop = false;
|
|
320
|
+
video.muted = true;
|
|
321
|
+
try { video.currentTime = 0; } catch (e) {}
|
|
322
|
+
var p = video.play();
|
|
323
|
+
if (p && p.catch) p.catch(function () {});
|
|
324
|
+
|
|
325
|
+
var onEnded = function () {
|
|
326
|
+
disarm();
|
|
327
|
+
if (hovering) return;
|
|
328
|
+
goNext();
|
|
329
|
+
};
|
|
330
|
+
armed.video = video;
|
|
331
|
+
armed.fn = onEnded;
|
|
332
|
+
video.addEventListener('ended', onEnded);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/* -------- PC hover: play hovered card on repeat, pause the auto one -------- */
|
|
336
|
+
function onEnter(card) {
|
|
337
|
+
if (!isDesktop()) return;
|
|
338
|
+
hovering = true;
|
|
339
|
+
clearImageTimer();
|
|
340
|
+
disarm();
|
|
341
|
+
var video = cardVideo(card);
|
|
342
|
+
pauseAllExcept(video);
|
|
343
|
+
if (!video) return; // hovered an image card
|
|
344
|
+
hydrate(video);
|
|
345
|
+
video.muted = true;
|
|
346
|
+
video.loop = true; // keep replaying while hovered
|
|
347
|
+
var p = video.play();
|
|
348
|
+
if (p && p.catch) p.catch(function () {});
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
function onLeave(card) {
|
|
352
|
+
if (!isDesktop()) return;
|
|
353
|
+
hovering = false;
|
|
354
|
+
var video = cardVideo(card);
|
|
355
|
+
if (video) video.loop = false;
|
|
356
|
+
playActive(); // resume the auto loop from the active card
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// Delegate hover so loop-mode clones react too.
|
|
360
|
+
swiperEl.addEventListener('mouseenter', function (e) {
|
|
361
|
+
var card = e.target.closest('.zz-vc__card');
|
|
362
|
+
if (card && swiperEl.contains(card)) onEnter(card);
|
|
363
|
+
}, true);
|
|
364
|
+
swiperEl.addEventListener('mouseleave', function (e) {
|
|
365
|
+
var card = e.target.closest('.zz-vc__card');
|
|
366
|
+
if (card && swiperEl.contains(card)) onLeave(card);
|
|
367
|
+
}, true);
|
|
368
|
+
|
|
369
|
+
/* -------- Swiper setup -------- */
|
|
370
|
+
function offsetBefore() {
|
|
371
|
+
if (!isDesktop()) return 0; // mobile: 2rem
|
|
372
|
+
var w = window.innerWidth;
|
|
373
|
+
if (w >= 1280) return Math.max(0, (w - 1200) / 2);
|
|
374
|
+
return w * 0.10; // 1024-1280: 10vw
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
swiper = new Swiper(swiperEl, {
|
|
378
|
+
slidesPerView: 'auto',
|
|
379
|
+
spaceBetween: isDesktop() ? 20 : 12,
|
|
380
|
+
slidesOffsetBefore: offsetBefore(),
|
|
381
|
+
slidesOffsetAfter: 40,
|
|
382
|
+
longSwipes: false, // one card per swipe gesture
|
|
383
|
+
watchOverflow: true,
|
|
384
|
+
loop: true,
|
|
385
|
+
navigation: {
|
|
386
|
+
nextEl: '.zz-vc__next-{{ uid }}',
|
|
387
|
+
prevEl: '.zz-vc__prev-{{ uid }}'
|
|
388
|
+
},
|
|
389
|
+
on: {
|
|
390
|
+
slideChange: function (sw) {
|
|
391
|
+
playActive();
|
|
392
|
+
updatePrevVisibility(sw);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
// Hide the left arrow on the first card: the blank space to its left is the
|
|
398
|
+
// section's aligned margin, so there is nothing to navigate "back" to.
|
|
399
|
+
function updatePrevVisibility(sw) {
|
|
400
|
+
sw = sw || swiper;
|
|
401
|
+
if (!sw) return;
|
|
402
|
+
var prev = root.querySelector('.zz-vc__prev-{{ uid }}');
|
|
403
|
+
if (!prev) return;
|
|
404
|
+
if (sw.realIndex === 0) prev.classList.add('swiper-button-disabled');
|
|
405
|
+
else prev.classList.remove('swiper-button-disabled');
|
|
406
|
+
}
|
|
407
|
+
updatePrevVisibility(swiper);
|
|
408
|
+
|
|
409
|
+
/* -------- Drive playback by section visibility -------- */
|
|
410
|
+
// Play/loop only while the section is on screen; pause everything when it
|
|
411
|
+
// scrolls away so we don't decode video or advance slides in the background.
|
|
412
|
+
function stopPlayback() {
|
|
413
|
+
clearImageTimer();
|
|
414
|
+
disarm();
|
|
415
|
+
pauseAllExcept(null);
|
|
416
|
+
}
|
|
417
|
+
var vis = new IntersectionObserver(function (entries) {
|
|
418
|
+
entries.forEach(function (entry) {
|
|
419
|
+
if (entry.isIntersecting && !visible) {
|
|
420
|
+
visible = true;
|
|
421
|
+
if (!hovering) playActive();
|
|
422
|
+
} else if (!entry.isIntersecting && visible) {
|
|
423
|
+
visible = false;
|
|
424
|
+
stopPlayback();
|
|
425
|
+
}
|
|
426
|
+
});
|
|
427
|
+
}, { threshold: 0.2 });
|
|
428
|
+
vis.observe(root);
|
|
429
|
+
|
|
430
|
+
/* -------- Keep offset / spacing correct across breakpoints -------- */
|
|
431
|
+
var resizeRAF = null;
|
|
432
|
+
window.addEventListener('resize', function () {
|
|
433
|
+
if (resizeRAF) cancelAnimationFrame(resizeRAF);
|
|
434
|
+
resizeRAF = requestAnimationFrame(function () {
|
|
435
|
+
if (!swiper) return;
|
|
436
|
+
swiper.params.spaceBetween = isDesktop() ? 20 : 12;
|
|
437
|
+
swiper.params.slidesOffsetBefore = offsetBefore();
|
|
438
|
+
swiper.update();
|
|
439
|
+
});
|
|
440
|
+
});
|
|
441
|
+
});
|
|
442
|
+
</script>
|
|
443
|
+
|
|
444
|
+
{% schema %}
|
|
445
|
+
{
|
|
446
|
+
"name": "ZZ Video Cards",
|
|
447
|
+
"class": "zz-video-cards-section",
|
|
448
|
+
"settings": [
|
|
449
|
+
{
|
|
450
|
+
"type": "text",
|
|
451
|
+
"id": "heading",
|
|
452
|
+
"label": "标题"
|
|
453
|
+
},
|
|
454
|
+
{
|
|
455
|
+
"type": "color",
|
|
456
|
+
"id": "title_color",
|
|
457
|
+
"default": "#2D2D2D",
|
|
458
|
+
"label": "标题颜色"
|
|
459
|
+
},
|
|
460
|
+
{
|
|
461
|
+
"type": "color_background",
|
|
462
|
+
"id": "bg_color",
|
|
463
|
+
"default": "#ffffff",
|
|
464
|
+
"label": "背景色"
|
|
465
|
+
},
|
|
466
|
+
{
|
|
467
|
+
"type": "number",
|
|
468
|
+
"id": "pc_padding_top",
|
|
469
|
+
"label": "PC上内边距 (Padding Top)",
|
|
470
|
+
"default": 80
|
|
471
|
+
},
|
|
472
|
+
{
|
|
473
|
+
"type": "number",
|
|
474
|
+
"id": "pc_padding_bottom",
|
|
475
|
+
"label": "PC下内边距 (Padding Bottom)",
|
|
476
|
+
"default": 80
|
|
477
|
+
},
|
|
478
|
+
{
|
|
479
|
+
"type": "number",
|
|
480
|
+
"id": "mobile_padding_top",
|
|
481
|
+
"label": "移动上内边距 (Padding Top)",
|
|
482
|
+
"default": 48
|
|
483
|
+
},
|
|
484
|
+
{
|
|
485
|
+
"type": "number",
|
|
486
|
+
"id": "mobile_padding_bottom",
|
|
487
|
+
"label": "移动下内边距 (Padding Bottom)",
|
|
488
|
+
"default": 48
|
|
489
|
+
},
|
|
490
|
+
{
|
|
491
|
+
"type": "header",
|
|
492
|
+
"content": "统计相关"
|
|
493
|
+
},
|
|
494
|
+
{
|
|
495
|
+
"type": "text",
|
|
496
|
+
"id": "module_id",
|
|
497
|
+
"label": "唯一业务id",
|
|
498
|
+
"info": "需要唯一,埋点统计"
|
|
499
|
+
}
|
|
500
|
+
],
|
|
501
|
+
"blocks": [
|
|
502
|
+
{
|
|
503
|
+
"type": "video",
|
|
504
|
+
"name": "Video",
|
|
505
|
+
"settings": [
|
|
506
|
+
{
|
|
507
|
+
"type": "video",
|
|
508
|
+
"id": "video_pc",
|
|
509
|
+
"label": "Video"
|
|
510
|
+
},
|
|
511
|
+
{
|
|
512
|
+
"type": "image_picker",
|
|
513
|
+
"id": "poster_pc",
|
|
514
|
+
"label": "Poster"
|
|
515
|
+
},
|
|
516
|
+
{
|
|
517
|
+
"type": "video",
|
|
518
|
+
"id": "video_mb",
|
|
519
|
+
"label": "Video(Mobile)"
|
|
520
|
+
},
|
|
521
|
+
{
|
|
522
|
+
"type": "image_picker",
|
|
523
|
+
"id": "poster_mb",
|
|
524
|
+
"label": "Poster(Mobile)"
|
|
525
|
+
},
|
|
526
|
+
{
|
|
527
|
+
"type": "text",
|
|
528
|
+
"id": "heading",
|
|
529
|
+
"label": "标题"
|
|
530
|
+
},
|
|
531
|
+
{
|
|
532
|
+
"type": "color",
|
|
533
|
+
"id": "title_color",
|
|
534
|
+
"default": "#FFFFFF",
|
|
535
|
+
"label": "标题颜色"
|
|
536
|
+
},
|
|
537
|
+
{
|
|
538
|
+
"type": "text",
|
|
539
|
+
"id": "block_module_id",
|
|
540
|
+
"label": "唯一业务id",
|
|
541
|
+
"info": "需要唯一,埋点统计"
|
|
542
|
+
}
|
|
543
|
+
]
|
|
544
|
+
}
|
|
545
|
+
],
|
|
546
|
+
"presets": [
|
|
547
|
+
{
|
|
548
|
+
"name": "ZZ Video Cards",
|
|
549
|
+
"blocks": [
|
|
550
|
+
{ "type": "video" },
|
|
551
|
+
{ "type": "video" },
|
|
552
|
+
{ "type": "video" },
|
|
553
|
+
{ "type": "video" }
|
|
554
|
+
]
|
|
555
|
+
}
|
|
556
|
+
]
|
|
557
|
+
}
|
|
558
|
+
{% endschema %}
|