diff --git a/assets/css/frontend/header-components/base.css b/assets/css/frontend/header-components/base.css index ce9807f7..c79ce848 100644 --- a/assets/css/frontend/header-components/base.css +++ b/assets/css/frontend/header-components/base.css @@ -175,9 +175,20 @@ --scale-height: 40%; --pc-height: 100%; --m-height: 100%; - padding-bottom: var(--scale-height); width: 100%; - height: 0; + height: auto; + /* Prefer aspect-ratio so global border-box cannot collapse the box. */ + aspect-ratio: 5 / 2; + padding-bottom: 0; +} + +@supports not (aspect-ratio: 1 / 1) { + .scale-height { + height: 0; + padding-bottom: var(--scale-height); + box-sizing: content-box; + aspect-ratio: auto; + } } .scale-height .swiper-wrapper, diff --git a/assets/css/frontend/header-components/search-keywords-row.css b/assets/css/frontend/header-components/search-keywords-row.css index f416673d..ae3f76c5 100644 --- a/assets/css/frontend/header-components/search-keywords-row.css +++ b/assets/css/frontend/header-components/search-keywords-row.css @@ -330,24 +330,41 @@ background: var(--main-bg-color, var(--surface-1)); } +/* + * Aspect-ratio card height. + * Global reset uses box-sizing:border-box, so the classic + * height:0 + padding-bottom trick collapses to 0 unless we + * force content-box (or use aspect-ratio). + */ .banner-module__swiper { position: relative; width: 100%; - height: 0; - padding-bottom: 40%; + height: auto; + aspect-ratio: 5 / 2; /* 40% */ + overflow: hidden; +} + +@supports not (aspect-ratio: 1 / 1) { + .banner-module__swiper { + height: 0; + padding-bottom: 40%; + box-sizing: content-box; + } } .banner-module__swiper .swiper-wrapper { position: absolute; inset: 0; - height: 100%; + width: 100%; + height: 100% !important; } .banner-module__swiper .swiper-slide { position: relative; overflow: hidden; cursor: pointer; - height: 100%; + width: 100%; + height: 100% !important; } .banner-module__slide-bg { @@ -359,8 +376,10 @@ .banner-module__slide-bg img, .banner-module__slide-bg .fit-cover { + display: block; width: 100%; height: 100%; + max-width: none; object-fit: cover; border-radius: 0; } @@ -453,8 +472,16 @@ } .banner-module__swiper { - padding-bottom: 50%; - height: 0; + aspect-ratio: 2 / 1; /* 50% */ + height: auto; + } + + @supports not (aspect-ratio: 1 / 1) { + .banner-module__swiper { + height: 0; + padding-bottom: 50%; + box-sizing: content-box; + } } .banner-module__content { diff --git a/assets/js/core/slider.js b/assets/js/core/slider.js index b8a353f4..6b2519e6 100644 --- a/assets/js/core/slider.js +++ b/assets/js/core/slider.js @@ -75,24 +75,111 @@ }); // ===== Type C: .new-swiper ===== + // Prefer data-swiper-config JSON (used by hero/banner templates), then data-* attrs. document.querySelectorAll(".new-swiper:not(.swiper-initialized)").forEach(function (el, index) { + var jsonConfig = null; + if (el.dataset.swiperConfig) { + try { + jsonConfig = JSON.parse(el.dataset.swiperConfig); + } catch (e) { + jsonConfig = null; + } + } + if (!jsonConfig || typeof jsonConfig !== "object") { + jsonConfig = {}; + } + var delay = parseInt(el.dataset.interval || "6000", 10); - var autoplay = el.dataset.autoplay !== undefined ? { - delay: delay, - disableOnInteraction: false, - } : false; + if (jsonConfig.autoplay && typeof jsonConfig.autoplay === "object" && jsonConfig.autoplay.delay) { + delay = parseInt(jsonConfig.autoplay.delay, 10) || delay; + } + + var autoplay; + if (Object.prototype.hasOwnProperty.call(jsonConfig, "autoplay")) { + autoplay = jsonConfig.autoplay; + } else if (el.dataset.autoplay !== undefined) { + autoplay = { + delay: delay, + disableOnInteraction: false, + }; + } else { + autoplay = false; + } var speed = parseInt(el.dataset.speed || "0", 10) || Math.floor((el.offsetWidth + 1000) / 22) * 10; + if (jsonConfig.speed) { + speed = parseInt(jsonConfig.speed, 10) || speed; + } speed = Math.max(400, speed); if (!autoplay) speed = Math.floor(speed / 1.6); - var loop = !!el.dataset.loop; + var loop = Object.prototype.hasOwnProperty.call(jsonConfig, "loop") + ? !!jsonConfig.loop + : !!el.dataset.loop; var parallax = !!el.querySelector("[data-swiper-parallax],[data-swiper-parallax-scale]"); - var effect = el.dataset.effect || "slide"; - var direction = el.dataset.direction || "horizontal"; - var spaceBetween = parseInt(el.dataset.spaceBetween || "0", 10); + var effect = jsonConfig.effect || el.dataset.effect || "slide"; + var direction = jsonConfig.direction || el.dataset.direction || "horizontal"; + var spaceBetween = Object.prototype.hasOwnProperty.call(jsonConfig, "spaceBetween") + ? parseInt(jsonConfig.spaceBetween, 10) || 0 + : parseInt(el.dataset.spaceBetween || "0", 10); var paginationType = el.dataset.paginationType || "bullets"; - var autoHeight = el.hasAttribute("auto-height"); + var autoHeight = Object.prototype.hasOwnProperty.call(jsonConfig, "autoHeight") + ? !!jsonConfig.autoHeight + : el.hasAttribute("auto-height"); + + // Fixed-ratio modules (banner card) must not use autoHeight — it collapses absolute slides. + if (el.classList.contains("banner-module__swiper") || el.classList.contains("scale-height")) { + autoHeight = false; + } + + var paginationEl = el.querySelector(".swiper-pagination, .header-components__pagination"); + var prevBtn = el.querySelector(".swiper-button-prev, .banner-module__prev"); + var nextBtn = el.querySelector(".swiper-button-next, .banner-module__next"); + + var pagination = false; + if (Object.prototype.hasOwnProperty.call(jsonConfig, "pagination")) { + if (jsonConfig.pagination === false) { + pagination = false; + } else if (jsonConfig.pagination && typeof jsonConfig.pagination === "object") { + pagination = Object.assign( + { + el: paginationEl, + clickable: true, + }, + jsonConfig.pagination + ); + if (!pagination.el) pagination.el = paginationEl; + if (!pagination.el) pagination = false; + } else if (jsonConfig.pagination) { + pagination = paginationEl + ? { el: paginationEl, type: paginationType, clickable: true } + : false; + } + } else { + pagination = paginationEl + ? { el: paginationEl, type: paginationType, clickable: true } + : false; + } + + var navigation = false; + if (Object.prototype.hasOwnProperty.call(jsonConfig, "navigation")) { + if (jsonConfig.navigation === false) { + navigation = false; + } else if (jsonConfig.navigation === true || jsonConfig.navigation) { + navigation = prevBtn && nextBtn + ? { nextEl: nextBtn, prevEl: prevBtn } + : false; + } + } else { + navigation = { + nextEl: nextBtn, + prevEl: prevBtn, + }; + } + + var keyboard = Object.prototype.hasOwnProperty.call(jsonConfig, "keyboard") + ? jsonConfig.keyboard + : { enabled: true }; el.classList.add("swiper-new-eq-" + index); el.setAttribute("swiper-new-index", String(index)); @@ -107,16 +194,9 @@ lazy: { loadPrevNext: true }, autoplay: autoplay, speed: speed, - pagination: { - el: el.querySelector(".swiper-pagination"), - type: paginationType, - clickable: true, - }, - navigation: { - nextEl: el.querySelector(".swiper-button-next"), - prevEl: el.querySelector(".swiper-button-prev"), - }, - keyboard: { enabled: true }, + pagination: pagination, + navigation: navigation, + keyboard: keyboard, a11y: { prevSlideMessage: "上一张", nextSlideMessage: "下一张" }, on: { lazyImageReady: function (slideEl, imageEl) { @@ -139,7 +219,16 @@ }, }; - if (paginationType === "thumbnail") { + if (effect === "fade") { + swiperArgs.fadeEffect = Object.assign( + { crossFade: true }, + (jsonConfig.fadeEffect && typeof jsonConfig.fadeEffect === "object") + ? jsonConfig.fadeEffect + : {} + ); + } + + if (pagination && paginationType === "thumbnail") { swiperArgs.pagination.type = "bullets"; swiperArgs.pagination.bulletClass = "pagination-thumbnail-item"; swiperArgs.pagination.bulletActiveClass = "pagination-thumbnail-item-active"; @@ -159,8 +248,6 @@ window.AksrtSwipers.new[index] = swiperInstance; if (el.classList.contains("banner-module__swiper")) { - var prevBtn = el.querySelector(".swiper-button-prev"); - var nextBtn = el.querySelector(".swiper-button-next"); if (prevBtn) { prevBtn.addEventListener("click", function (e) { e.preventDefault(); e.stopPropagation(); e.stopImmediatePropagation();