fix: replace CSS scroll-snap with JS-based snap for reliable mobile behavior
This commit is contained in:
parent
e38cfed764
commit
7a5430d40a
91
index.html
91
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
|
|
|||
Loading…
Reference in New Issue