🛬 顶部横幅倒计时,改代码

{% comment %}
限时促销横幅:黄底、橙色顶标、双色主标题、蓝色倒计时胶囊 + 底部双层波浪(SVG 平移动画)
{% endcomment %}
{% liquid
assign pbc_uid = 'pbc-' | append: section.id
assign end_iso = section.settings.countdown_end | strip
assign pbc_wave_kf_back = 'pbc-wvb-' | append: section.id | replace: '--', '-' | replace: '__', '-' | handleize
assign pbc_wave_kf_front = 'pbc-wvf-' | append: section.id | replace: '--', '-' | replace: '__', '-' | handleize
assign pbc_pulse_kf = 'pbc-pulse-' | append: section.id | replace: '--', '-' | replace: '__', '-' | handleize
assign pbc_pulse_peak = section.settings.suffix_pulse_peak_pct | default: 112 | times: 0.01
%}
<div class="pbc-section color-{{ section.settings.color_scheme }}" style="
--pbc-bg: {{ section.settings.bg_color }};
--pbc-badge-orange: {{ section.settings.badge_orange }};
--pbc-badge-orange-text: {{ section.settings.badge_orange_text }};
--pbc-head-blue: {{ section.settings.headline_blue }};
--pbc-head-red: {{ section.settings.headline_red }};
--pbc-sub-color: {{ section.settings.subhead_color }};
--pbc-count-bg: {{ section.settings.countdown_pill_bg }};
--pbc-count-text: {{ section.settings.countdown_pill_text }};
--pbc-wave-back: {{ section.settings.wave_color_back }};
--pbc-wave-front: {{ section.settings.wave_color_front }};
">
<div class="pbc" id="{{ pbc_uid }}">
<div class="pbc__inner page-width">
{% if section.settings.top_badge_text != blank %}
<div class="pbc__pill pbc__pill--top">
<span class="pbc__pill-text">{{ section.settings.top_badge_text | escape }}</span>
</div>
{% endif %}
<h2 class="pbc__headline">
{% if section.settings.headline_prefix != blank %}
<span class="pbc__headline-part pbc__headline-part--blue">{{ section.settings.headline_prefix | escape }}</span>
{% endif %}
{% if section.settings.headline_suffix != blank %}
<span class="pbc__headline-part pbc__headline-part--red">{{ section.settings.headline_suffix | escape }}</span>
{% endif %}
</h2>
{% if section.settings.subheadline != blank %}
<p class="pbc__sub">{{ section.settings.subheadline | escape }}</p>
{% endif %}
<div
class="pbc__pill pbc__pill--countdown"
data-pbc-mode="{{ section.settings.countdown_mode | default: 'fixed_minutes' }}"
data-pbc-duration-min="{{ section.settings.countdown_minutes | default: 30 }}"
data-pbc-persist="{% if section.settings.countdown_persist %}true{% else %}false{% endif %}"
data-pbc-storage-key="pbc-end-{{ section.id }}"
data-pbc-end="{{ end_iso | escape }}"
>
<span class="pbc__count-label">{{ section.settings.countdown_prefix | escape }}</span>
<strong class="pbc__count-value" data-pbc-display aria-live="polite">——</strong>
</div>
</div>
<div class="pbc__waves" aria-hidden="true">
<div class="pbc__wave-layer pbc__wave-layer--back">
<svg class="pbc__wave-svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2400 96" preserveAspectRatio="none">
<path
fill="var(--pbc-wave-back)"
d="M0,56 C200,16 400,96 600,56 C800,16 1000,96 1200,56 L1200,96 L0,96 Z"
/>
<path
fill="var(--pbc-wave-back)"
transform="translate(1200, 0)"
d="M0,56 C200,16 400,96 600,56 C800,16 1000,96 1200,56 L1200,96 L0,96 Z"
/>
</svg>
</div>
<div class="pbc__wave-layer pbc__wave-layer--front">
<svg class="pbc__wave-svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2400 96" preserveAspectRatio="none">
<path
fill="var(--pbc-wave-front)"
d="M0,48 C240,8 480,88 720,48 C960,8 1200,88 1440,48 L1440,96 L0,96 Z"
/>
<path
fill="var(--pbc-wave-front)"
transform="translate(1440, 0)"
d="M0,48 C240,8 480,88 720,48 C960,8 1200,88 1440,48 L1440,96 L0,96 Z"
/>
</svg>
</div>
</div>
</div>
</div>
<style>
#shopify-section-{{ section.id }} .pbc__wave-layer--back {
animation: {{ pbc_wave_kf_back }} 18s linear infinite;
}
#shopify-section-{{ section.id }} .pbc__wave-layer--front {
animation: {{ pbc_wave_kf_front }} 11s linear infinite reverse;
}
@keyframes {{ pbc_wave_kf_back }} {
0% {
transform: translate3d(0, 0, 0);
}
100% {
transform: translate3d(-50%, 0, 0);
}
}
@keyframes {{ pbc_wave_kf_front }} {
0% {
transform: translate3d(0, 0, 0);
}
100% {
transform: translate3d(-50%, 0, 0);
}
}
@media (prefers-reduced-motion: reduce) {
{% if section.settings.force_wave_motion %}
#shopify-section-{{ section.id }} .pbc__wave-layer--back {
animation: {{ pbc_wave_kf_back }} 18s linear infinite !important;
}
#shopify-section-{{ section.id }} .pbc__wave-layer--front {
animation: {{ pbc_wave_kf_front }} 11s linear infinite reverse !important;
}
{% else %}
#shopify-section-{{ section.id }} .pbc__wave-layer--back,
#shopify-section-{{ section.id }} .pbc__wave-layer--front {
animation: none !important;
}
{% endif %}
}
{% if section.settings.enable_suffix_pulse %}
#shopify-section-{{ section.id }} .pbc__headline-part--red {
animation: {{ pbc_pulse_kf }} {{ section.settings.suffix_pulse_seconds }}s ease-in-out infinite;
transform-origin: center center;
will-change: transform;
}
@keyframes {{ pbc_pulse_kf }} {
0%,
100% {
transform: scale(1);
}
50% {
transform: scale({{ pbc_pulse_peak }});
}
}
@media (prefers-reduced-motion: reduce) {
{% if section.settings.force_suffix_pulse %}
#shopify-section-{{ section.id }} .pbc__headline-part--red {
animation: {{ pbc_pulse_kf }} {{ section.settings.suffix_pulse_seconds }}s ease-in-out infinite !important;
}
{% else %}
#shopify-section-{{ section.id }} .pbc__headline-part--red {
animation: none !important;
}
{% endif %}
}
{% endif %}
</style>
<script>
(function () {
var root = document.getElementById('{{ pbc_uid }}');
if (!root) return;
var pill = root.querySelector('.pbc__pill--countdown');
var el = root.querySelector('[data-pbc-display]');
if (!pill || !el) return;
var mode = pill.getAttribute('data-pbc-mode') || 'fixed_minutes';
var endAttr = pill.getAttribute('data-pbc-end');
var durationMin = parseInt(pill.getAttribute('data-pbc-duration-min'), 10);
var persist = pill.getAttribute('data-pbc-persist') === 'true';
var storageKey = pill.getAttribute('data-pbc-storage-key') || '';
if (!durationMin || durationMin < 1) durationMin = 30;
var endMs;
if (mode === 'end_datetime') {
endMs = endAttr ? Date.parse(endAttr) : NaN;
} else {
var msLeft = durationMin * 60 * 1000;
var now = Date.now();
if (persist && storageKey && typeof localStorage !== 'undefined') {
try {
var stored = localStorage.getItem(storageKey);
if (stored) {
var parsed = parseInt(stored, 10);
if (!isNaN(parsed) && parsed > now) {
endMs = parsed;
} else {
localStorage.removeItem(storageKey);
}
}
} catch (e) {}
}
if (typeof endMs !== 'number' || isNaN(endMs)) {
endMs = now + msLeft;
if (persist && storageKey && typeof localStorage !== 'undefined') {
try {
localStorage.setItem(storageKey, String(endMs));
} catch (e) {}
}
}
}
function pad(n) {
return (n < 10 ? '0' : '') + n;
}
function formatRemain(ms) {
if (ms <= 0) return '已结束';
var totalSec = Math.floor(ms / 1000);
var days = Math.floor(totalSec / 86400);
totalSec %= 86400;
var h = Math.floor(totalSec / 3600);
totalSec %= 3600;
var m = Math.floor(totalSec / 60);
var s = totalSec % 60;
/* 不足 1 小时(且当日小时数为 0)不显示 h;否则显示 h、m、s */
var msOnly = pad(m) + 'm ' + pad(s) + 's';
var hms = pad(h) + 'h ' + pad(m) + 'm ' + pad(s) + 's';
if (days > 0) {
return h > 0 ? days + 'd ' + hms : days + 'd ' + msOnly;
}
return h > 0 ? hms : msOnly;
}
function tick() {
if (mode === 'end_datetime') {
if (!endAttr || isNaN(endMs)) {
el.textContent = '请在设置中填写结束时间';
return;
}
}
var left = endMs - Date.now();
if (isNaN(left)) {
el.textContent = '时间格式有误';
return;
}
el.textContent = formatRemain(left);
if (left <= 0) return;
setTimeout(tick, 1000);
}
tick();
})();
</script>
{% stylesheet %}
.pbc-section {
position: relative;
overflow: hidden;
}
.pbc {
position: relative;
background: var(--pbc-bg, #fff176);
padding: clamp(1.25rem, 4vw, 2rem) 0 clamp(3.25rem, 8vw, 4.5rem);
font-family: Impact, Haettenschweiler, 'Arial Narrow Bold', 'Segoe UI', sans-serif;
}
.pbc__inner {
position: relative;
z-index: 2;
text-align: center;
max-width: 52rem;
margin: 0 auto;
padding-inline: 0.75rem;
}
.pbc__pill {
display: inline-flex;
align-items: center;
justify-content: center;
border-radius: 999px;
font-weight: 700;
font-style: italic;
letter-spacing: 0.02em;
}
.pbc__pill--top {
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
font-style: normal;
font-weight: 800;
background: var(--pbc-badge-orange, #c2410c);
color: var(--pbc-badge-orange-text, #ffffff);
padding: 0.5rem 1.25rem;
font-size: clamp(0.9rem, 3vw, 1.1rem);
margin-bottom: 0.65rem;
text-transform: uppercase;
letter-spacing: 0.06em;
line-height: 1.25;
border: 1px solid rgba(0, 0, 0, 0.2);
box-shadow: none;
text-shadow: none;
-webkit-font-smoothing: antialiased;
}
.pbc__pill-text {
white-space: nowrap;
}
.pbc__headline {
margin: 0 0 0.35rem;
font-size: clamp(2rem, 8vw, 3.75rem);
line-height: 1.05;
font-weight: 700;
font-style: italic;
letter-spacing: -0.02em;
}
.pbc__headline-part {
display: inline-block;
margin: 0 0.15em;
}
.pbc__headline-part--blue {
color: var(--pbc-head-blue, #1e3a8a);
text-shadow: none;
}
.pbc__headline-part--red {
color: var(--pbc-head-red, #b91c1c);
text-shadow: none;
}
.pbc__sub {
margin: 0 0 0.85rem;
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
font-size: clamp(0.78rem, 2.4vw, 1.05rem);
font-weight: 800;
font-style: normal;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--pbc-sub-color, #0f172a);
text-shadow: none;
line-height: 1.35;
}
.pbc__pill--countdown {
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
font-style: normal;
background: var(--pbc-count-bg, #172554);
color: var(--pbc-count-text, #ffffff);
padding: 0.65rem 1.3rem;
font-size: clamp(1rem, 3.2vw, 1.25rem);
gap: 0.45rem;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12);
flex-wrap: wrap;
justify-content: center;
max-width: 100%;
text-shadow: none;
line-height: 1.35;
border: 1px solid rgba(255, 255, 255, 0.12);
-webkit-font-smoothing: antialiased;
}
.pbc__count-label {
font-style: normal;
font-weight: 800;
letter-spacing: 0.04em;
flex-shrink: 0;
text-transform: uppercase;
opacity: 1;
}
.pbc__count-value {
font-style: normal;
font-weight: 800;
font-variant-numeric: tabular-nums;
word-break: keep-all;
letter-spacing: 0.02em;
}
.pbc__waves {
position: absolute;
left: 0;
right: 0;
bottom: 0;
height: clamp(52px, 12vw, 72px);
pointer-events: none;
z-index: 1;
}
.pbc__wave-layer {
position: absolute;
left: 0;
bottom: 0;
width: 200%;
height: 100%;
margin: 0;
will-change: transform;
backface-visibility: hidden;
}
.pbc__wave-layer--back {
opacity: 0.45;
z-index: 1;
}
.pbc__wave-layer--front {
opacity: 1;
z-index: 2;
}
.pbc__wave-svg {
display: block;
width: 100%;
height: clamp(52px, 12vw, 72px);
vertical-align: bottom;
}
{% endstylesheet %}
{% schema %}
{
"name": "促销横幅倒计时",
"tag": "section",
"class": "section-promo-banner-countdown",
"settings": [
{
"type": "color_scheme",
"id": "color_scheme",
"label": "配色方案",
"default": "scheme-1"
},
{
"type": "header",
"content": "背景与文字颜色"
},
{
"type": "color",
"id": "bg_color",
"label": "横幅背景",
"default": "#FFF176"
},
{
"type": "color",
"id": "badge_orange",
"label": "顶部橙色胶囊背景",
"info": "偏深的橙色配白字对比更清晰;可按品牌再调",
"default": "#C2410C"
},
{
"type": "color",
"id": "badge_orange_text",
"label": "顶部胶囊文字",
"default": "#FFFFFF"
},
{
"type": "color",
"id": "headline_blue",
"label": "主标题前半(蓝)",
"default": "#1E3A8A"
},
{
"type": "color",
"id": "headline_red",
"label": "主标题后半(红)",
"default": "#B91C1C"
},
{
"type": "color",
"id": "subhead_color",
"label": "副标题文字",
"default": "#0F172A"
},
{
"type": "color",
"id": "countdown_pill_bg",
"label": "倒计时胶囊背景",
"info": "默认略加深,「ENDS IN」等字与白字对比更明显",
"default": "#172554"
},
{
"type": "color",
"id": "countdown_pill_text",
"label": "倒计时胶囊文字",
"default": "#FFFFFF"
},
{
"type": "color",
"id": "wave_color_front",
"label": "波浪前景(深蓝)",
"default": "#1E3A8A"
},
{
"type": "color",
"id": "wave_color_back",
"label": "波浪后层(浅蓝叠在后方)",
"default": "#93C5FD"
},
{
"type": "checkbox",
"id": "force_wave_motion",
"label": "系统开启「减少动态效果」时仍播放波浪动画",
"info": "若波浪静止不动,可勾选此项(无障碍场景请按需关闭)",
"default": true
},
{
"type": "header",
"content": "主标题红色字动画(如 80% OFF)"
},
{
"type": "checkbox",
"id": "enable_suffix_pulse",
"label": "红色部分呼吸缩放(变大变小)",
"info": "默认关闭,避免动态缩放导致文字发糊、不易辨认",
"default": false
},
{
"type": "range",
"id": "suffix_pulse_seconds",
"min": 0.8,
"max": 2.5,
"step": 0.1,
"unit": "s",
"label": "呼吸周期(越大越慢)",
"default": 1.2
},
{
"type": "range",
"id": "suffix_pulse_peak_pct",
"min": 104,
"max": 125,
"step": 1,
"unit": "%",
"label": "最大放大比例(相对原始字号)",
"info": "112 表示峰值约为原来的 1.12 倍",
"default": 112
},
{
"type": "checkbox",
"id": "force_suffix_pulse",
"label": "系统开启「减少动态效果」时仍播放红色字缩放",
"default": true
},
{
"type": "header",
"content": "文案"
},
{
"type": "text",
"id": "top_badge_text",
"label": "顶部橙色胶囊文案",
"default": "⚡ LIMITED-TIME MEGA SALE ⚡"
},
{
"type": "text",
"id": "headline_prefix",
"label": "主标题前半(蓝色)",
"default": "UP TO"
},
{
"type": "text",
"id": "headline_suffix",
"label": "主标题后半(红色)",
"default": "80% OFF"
},
{
"type": "text",
"id": "subheadline",
"label": "副标题(大写)",
"default": "EVERYTHING MUST GO! LIMITED STOCK!"
},
{
"type": "text",
"id": "countdown_prefix",
"label": "倒计时前缀",
"default": "距离结束还剩"
},
{
"type": "header",
"content": "倒计时规则"
},
{
"type": "select",
"id": "countdown_mode",
"label": "倒计时方式",
"options": [
{
"value": "fixed_minutes",
"label": "固定多少分钟(从打开页面起算)"
},
{
"value": "end_datetime",
"label": "固定结束日期与时间(ISO)"
}
],
"default": "fixed_minutes"
},
{
"type": "range",
"id": "countdown_minutes",
"min": 5,
"max": 180,
"step": 5,
"unit": "min",
"label": "倒计时时长(分钟)",
"info": "仅在「固定多少分钟」模式下使用,例如 20、25、30",
"default": 30
},
{
"type": "checkbox",
"id": "countdown_persist",
"label": "刷新页面后倒计时继续(同一浏览器记住结束时刻)",
"info": "关闭则每次打开页面重新从设定分钟数开始",
"default": false
},
{
"type": "text",
"id": "countdown_end",
"label": "倒计时结束时间(ISO 8601)",
"info": "仅在「固定结束日期与时间」模式下使用。示例:2026-12-31T23:59:59 或 2026-12-31T23:59:59+08:00",
"default": "2026-12-31T23:59:59"
}
],
"presets": [
{
"name": "促销横幅倒计时"
}
]
}
{% endschema %}
发布时间: