diff --git a/index.html b/index.html index 25c12a7..cacdd8b 100644 --- a/index.html +++ b/index.html @@ -446,19 +446,10 @@ /* 移动端痛点沉浸式布局 */ @media (max-width: 900px) { - /* 逐屏吸附:整个页面每次滚动就是一屏 */ - body { - scroll-snap-type: y mandatory; - scroll-behavior: smooth; - } - .hero, #solutions, #approach, #geo, #contact, footer { - scroll-snap-align: start; - } /* 痛点半块单列全屏 */ #pain-points { overflow: visible; text-align: center; - scroll-snap-align: start; } #pain-points .section-label { margin-bottom: 40px; @@ -516,8 +507,6 @@ overflow: visible; position: relative; z-index: 1; - scroll-snap-align: start; - scroll-snap-stop: always; } .pain-item::before { display: none; } .pain-item:hover { background: transparent; } @@ -985,6 +974,14 @@ // 移动端:入场动画 + 视差滚动 if (window.innerWidth <= 900) { + // 移除 CSS scroll-snap(不可靠),用 JS 实现 + document.body.style.scrollSnapType = 'none'; + document.querySelectorAll('[style*="scroll-snap"]').forEach(el => { + el.style.scrollSnapType = 'none'; + el.style.scrollSnapAlign = 'none'; + el.style.scrollSnapStop = 'normal'; + }); + // 1. 入场动画(一次性) const mobileItems = document.querySelectorAll('#pain-points .pain-item, #approach .approach-item'); const mobileObserver = new IntersectionObserver((entries) => { @@ -1000,21 +997,11 @@ function updateParallax() { painNums.forEach(num => { - // 获取数字所在的父卡片(.pain-item) const card = num.parentElement; if (!card) return; - - // 计算卡片相对于视口顶部的距离 const rect = card.getBoundingClientRect(); - - // 核心视差逻辑: - // rect.top 越大(卡片在屏幕越下方),位移越大(向上推数字) - // 随着卡片向上滚动(rect.top 变小),数字向下回落 - // 这会产生数字比文字"慢半拍"的悬浮感 - // 速度因子 0.15 意味着视差幅度为卡片移动距离的 15% const speed = 0.15; const yOffset = -(rect.top * speed); - num.style.transform = `translateY(${yOffset}px)`; }); ticking = false; @@ -1026,9 +1013,67 @@ ticking = true; } }, { passive: true }); - - // 初始化一次 updateParallax(); + + // 3. JS 逐屏吸附 + // 收集所有吸附点 + const snapTargets = []; + // Hero + const hero = document.querySelector('.hero'); + if (hero) snapTargets.push(hero); + // 痛点每个卡片 + document.querySelectorAll('#pain-points .pain-item').forEach(item => snapTargets.push(item)); + // 其他 section + ['solutions', 'approach', 'geo', 'contact'].forEach(id => { + const el = document.getElementById(id); + if (el) snapTargets.push(el); + }); + + let scrollEndTimer = null; + let isSnapping = false; + let touchStartY = 0; + let lastScrollTop = 0; + + document.addEventListener('touchstart', (e) => { + touchStartY = e.touches[0].clientY; + }, { passive: true }); + + window.addEventListener('scroll', () => { + // 清除之前的定时器 + clearTimeout(scrollEndTimer); + // 300ms 无滚动视为停止 + scrollEndTimer = setTimeout(() => { + if (isSnapping) return; // 正在吸附中不触发 + snapToNearest(); + }, 200); + lastScrollTop = window.scrollY; + }, { passive: true }); + + function snapToNearest() { + const vh = window.innerHeight; + const center = window.scrollY + vh * 0.4; // 吸附点偏上 40% 位置 + + let nearest = null; + let minDist = Infinity; + + snapTargets.forEach(target => { + const rect = target.getBoundingClientRect(); + const top = rect.top + window.scrollY; + const dist = Math.abs(top - center); + if (dist < minDist) { + minDist = dist; + nearest = target; + } + }); + + if (nearest) { + isSnapping = true; + const targetY = nearest.getBoundingClientRect().top + window.scrollY; + window.scrollTo({ top: targetY, behavior: 'smooth' }); + // 吸附完成后解锁 + setTimeout(() => { isSnapping = false; }, 800); + } + } }