fix(site): adaptive stat font scaling, marquee gap and inject race / 统计四宫格数字自适应缩放防串行、跑马灯拼接缝隙统一、双注入路径竞态防护
This commit is contained in:
parent
90ba3016c0
commit
61609d3566
@ -1,4 +1,4 @@
|
||||
/**
|
||||
/**
|
||||
* 鍥哄畾鏉垮潡鍐呭娉ㄥ叆閫昏緫
|
||||
*/
|
||||
|
||||
@ -114,27 +114,102 @@ function splitSection2StatTitle(raw) {
|
||||
}
|
||||
|
||||
/** 銆屽叧浜庡崕闃炽€嶅睆鍙充晶鍥涘鏍硷細.e_loop-39 .p_list + .cbox-39.p_loopitem */
|
||||
/**
|
||||
* 统计四宫格数字自适应卡片宽度:
|
||||
* 基准取归档 CSS 当前断点计算字号(桌面 50px),强制单行不换行;
|
||||
* 文字超宽时等比缩小(下限 14px),仍放不下则省略号兜底,避免串行或硬裁切。
|
||||
*/
|
||||
export function fitStatNumFontSize(numSpan) {
|
||||
if (!numSpan) return
|
||||
numSpan.style.whiteSpace = 'nowrap'
|
||||
/* inline 元素 scrollWidth 不反映溢出宽度,改 inline-block 才能测出真实文本宽 */
|
||||
numSpan.style.display = 'inline-block'
|
||||
/* 极长文字缩到下限仍放不下时,省略号兜底(优于硬裁切) */
|
||||
numSpan.style.maxWidth = '100%'
|
||||
numSpan.style.overflow = 'hidden'
|
||||
numSpan.style.textOverflow = 'ellipsis'
|
||||
/* 断点下父级 line-height:1 小于字体实际渲染高,inline-block+overflow 会裁掉下缘(如字母 q 的下伸部),撑足行高(实测字体度量需 ≥1.34) */
|
||||
numSpan.style.lineHeight = '1.35'
|
||||
numSpan.style.fontSize = ''
|
||||
const box = numSpan.parentElement
|
||||
if (!box) return
|
||||
const cs = getComputedStyle(box)
|
||||
const avail = box.clientWidth - (parseFloat(cs.paddingLeft) || 0) - (parseFloat(cs.paddingRight) || 0)
|
||||
if (avail <= 0) return
|
||||
const w = numSpan.scrollWidth
|
||||
if (w > avail) {
|
||||
const base = parseFloat(getComputedStyle(numSpan).fontSize) || 50
|
||||
/* avail 再留 2px 余量,抵消 floor 取整与亚像素测量误差;下限 14px 保证可读 */
|
||||
numSpan.style.fontSize = Math.max(14, Math.floor((base * (avail - 2)) / w)) + 'px'
|
||||
}
|
||||
}
|
||||
|
||||
function refitAllStatNums() {
|
||||
document.querySelectorAll('.e_loop-39 .p_num').forEach(fitStatNumFontSize)
|
||||
}
|
||||
|
||||
let statNumFitResizeTimer = null
|
||||
/* 断点切换后归档字号变化(50/40/36/30px),防抖统一重算已注入数字 */
|
||||
function scheduleStatNumFitOnResize() {
|
||||
if (statNumFitResizeTimer) clearTimeout(statNumFitResizeTimer)
|
||||
statNumFitResizeTimer = setTimeout(refitAllStatNums, 200)
|
||||
}
|
||||
if (typeof window !== 'undefined') window.addEventListener('resize', scheduleStatNumFitOnResize)
|
||||
|
||||
let statNumFitHooksInstalled = false
|
||||
/*
|
||||
* 注入发生在首页 swiper 非激活屏时 clientWidth=0,fit 会跳过缩放;
|
||||
* 且注入可能早于 webfont 加载(fallback 字体测宽偏窄)。
|
||||
* 故在多个时机补算:首次滚入视口、字体就绪、延迟兜底(flag 保证只挂一次)。
|
||||
*/
|
||||
export function installStatNumFitHooks() {
|
||||
if (statNumFitHooksInstalled || typeof window === 'undefined') return
|
||||
statNumFitHooksInstalled = true
|
||||
if (document.fonts?.ready) document.fonts.ready.then(() => refitAllStatNums())
|
||||
setTimeout(refitAllStatNums, 600)
|
||||
if ('IntersectionObserver' in window) {
|
||||
const list = document.querySelector('.e_loop-39 .p_list')
|
||||
if (list) {
|
||||
const io = new IntersectionObserver((entries) => {
|
||||
if (entries.some((e) => e.isIntersecting)) {
|
||||
refitAllStatNums()
|
||||
io.disconnect()
|
||||
}
|
||||
})
|
||||
io.observe(list)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 单张统计卡内容写入:标题按「主数字|单位」拆分,描述/图标兜底,供四宫格两条注入路径(section2 子项 / home features)复用 */
|
||||
export function applyStatCardContent(cardEl, rawTitle, desc, iconUrl) {
|
||||
const { num, unit } = splitSection2StatTitle(rawTitle)
|
||||
const numSpan = cardEl.querySelector('.p_num')
|
||||
if (numSpan) {
|
||||
numSpan.textContent = num
|
||||
if (num) numSpan.setAttribute('data-num', num)
|
||||
}
|
||||
const plus = cardEl.querySelector('.p_plus')
|
||||
if (plus) plus.textContent = unit || ''
|
||||
const label = cardEl.querySelector('.e_text-41')
|
||||
if (label) label.textContent = desc || ''
|
||||
const img = cardEl.querySelector('.e_image-42 img')
|
||||
if (img && iconUrl) bindImageFallback(img, resolveImageUrl(iconUrl))
|
||||
}
|
||||
|
||||
function injectSection2AboutStatGrid(listEl, templateEl, items) {
|
||||
const clone = templateEl.cloneNode(true)
|
||||
listEl.innerHTML = ''
|
||||
items.forEach((item) => {
|
||||
const node = clone.cloneNode(true)
|
||||
node.classList.remove('swiper-slide-duplicate', 'swiper-slide-active', 'swiper-slide-next')
|
||||
const { num, unit } = splitSection2StatTitle(item.itemTitle)
|
||||
const numSpan = node.querySelector('.p_num')
|
||||
if (numSpan) {
|
||||
numSpan.textContent = num
|
||||
if (num) numSpan.setAttribute('data-num', num)
|
||||
}
|
||||
const plus = node.querySelector('.p_plus')
|
||||
if (plus) plus.textContent = unit || ''
|
||||
const label = node.querySelector('.e_text-41')
|
||||
if (label) label.textContent = item.itemDesc || ''
|
||||
const img = node.querySelector('.e_image-42 img')
|
||||
const imgUrl = firstImageUrl(item.itemImage)
|
||||
if (img && imgUrl) bindImageFallback(img, resolveImageUrl(imgUrl))
|
||||
applyStatCardContent(node, item.itemTitle, item.itemDesc, firstImageUrl(item.itemImage))
|
||||
listEl.appendChild(node)
|
||||
fitStatNumFontSize(node.querySelector('.p_num'))
|
||||
})
|
||||
/* section2 子项注入后打标:home features 路径见标记即跳过,避免并行竞态覆盖(子项优先) */
|
||||
if (items.length > 0) listEl.setAttribute('data-hy-stat-injected', '1')
|
||||
installStatNumFitHooks()
|
||||
}
|
||||
|
||||
function applySection2AboutMainContent(slideEl, localizedData, code) {
|
||||
|
||||
@ -1,7 +1,14 @@
|
||||
import { resolveImageUrl } from '../utils/asset'
|
||||
import { applyImageLoadPriority } from '../utils/imageLoading'
|
||||
import { remember } from '../utils/asyncCache'
|
||||
import { clearHomeAboutArchiveFallback, fetchFixedSection, injectAllFixedSections } from './fixedSection'
|
||||
import {
|
||||
clearHomeAboutArchiveFallback,
|
||||
fetchFixedSection,
|
||||
injectAllFixedSections,
|
||||
applyStatCardContent,
|
||||
fitStatNumFontSize,
|
||||
installStatNumFitHooks
|
||||
} from './fixedSection'
|
||||
import { fetchProductPublic } from './product'
|
||||
import { currentLang } from './language'
|
||||
import i18n from '../i18n'
|
||||
@ -319,25 +326,22 @@ export async function injectHomeContent(options = {}) {
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 特色模块注入 (e_loop-39)
|
||||
// 2. 特色模块注入 (e_loop-39):section2 子项优先(fixedSection 注入后带 data-hy-stat-injected 标记,此处跳过避免并行竞态覆盖)
|
||||
const featureListContainer = document.querySelector('.e_loop-39 .p_list')
|
||||
if (data && featureListContainer && data.features && data.features.length > 0) {
|
||||
if (data && featureListContainer && data.features && data.features.length > 0 && !featureListContainer.hasAttribute('data-hy-stat-injected')) {
|
||||
const originalItems = Array.from(featureListContainer.querySelectorAll('.p_loopitem'))
|
||||
featureListContainer.innerHTML = ''
|
||||
|
||||
|
||||
data.features.forEach((feature) => {
|
||||
const item = (originalItems[0] || document.createElement('div')).cloneNode(true)
|
||||
const titleEl = item.querySelector('.p_num')
|
||||
if (titleEl) titleEl.textContent = feature.featureTitle
|
||||
const descEl = item.querySelector('.e_text-41')
|
||||
if (descEl) descEl.textContent = feature.featureDesc
|
||||
item.classList.remove('swiper-slide-duplicate', 'swiper-slide-active', 'swiper-slide-next')
|
||||
applyStatCardContent(item, feature.featureTitle, feature.featureDesc, feature.featureIcon)
|
||||
const imgEl = item.querySelector('.e_image-42 img')
|
||||
if (imgEl) {
|
||||
applyImageLoadPriority(imgEl, 'lazy')
|
||||
imgEl.src = resolveImageUrl(feature.featureIcon)
|
||||
}
|
||||
if (imgEl) applyImageLoadPriority(imgEl, 'lazy')
|
||||
featureListContainer.appendChild(item)
|
||||
fitStatNumFontSize(item.querySelector('.p_num'))
|
||||
})
|
||||
installStatNumFitHooks()
|
||||
}
|
||||
|
||||
// 3. 首页产品 (#home-product-grid) — 仅 div + CSS:最多 4 条,双行轨道 translateX 向左无缝循环(无 Swiper、无轮播控件)
|
||||
|
||||
@ -58,6 +58,8 @@
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
gap: 24px 20px;
|
||||
/* 右侧留白=列间距:修复行尾卡与克隆行首卡之间 0px 间隙,保持无缝循环缝隙均匀 */
|
||||
padding-right: 20px;
|
||||
box-sizing: border-box;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
@ -834,13 +834,14 @@ body[data-pageid='about-honor-page'] .haiho-page #c_static_001_P_4578-1694141444
|
||||
}
|
||||
|
||||
/*
|
||||
* 首页「关于华阳」屏右侧四宫格统计数字(.p_num,红色):
|
||||
* 归档 Home_*.min.css 按断点写了 50/40/36/30px,统一覆盖为 35px;
|
||||
* 两个 slide id 均覆盖,避免不同断点字号跳变。
|
||||
* 统计四宫格(.cbox-39 为 flex:0 0 48% 且不可收缩的 flex 项):
|
||||
* fitSection2StatNumFontSize 给 .p_num 加了 nowrap,超长文字的 min-content
|
||||
* 会沿 min-width:auto 链路把 48% 卡片撑大,触发 p_list 的 flex-wrap 串行;
|
||||
* 解除卡片及其内部 flex 项的自动最小宽,让卡片保持 48%、长文字由 JS 缩放。
|
||||
*/
|
||||
.haiho-page #c_static_001_P_5212-1683353667670 .e_scrollNumber-40 .p_num,
|
||||
.haiho-page #c_static_001_P_5212-17155923594210 .e_scrollNumber-40 .p_num {
|
||||
font-size: 35px;
|
||||
.haiho-page #c_static_001_P_5212-1683353667670 .e_loop-39 .cbox-39,
|
||||
.haiho-page #c_static_001_P_5212-1683353667670 .e_loop-39 .e_container-47 > .p_item {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
Loading…
Reference in New Issue
Block a user