event.preventDefault():在 wheel、touchmove、keydown (方向鍵/空格/PageUp/Down) 事件中調用,阻止瀏覽器默認滾動。window.scrollTo({ top, left, behavior }) / element.scrollTop:程式化控制滾動位置。requestAnimationFrame:實現平滑動畫幀。const STEP = 100; // 自定義滾動距離
let isScrolling = false;
window.addEventListener('wheel', e => {
e.preventDefault(); // 關鍵:禁用默認滾動
if (isScrolling) return; // 簡易節流,防止動畫疊加
isScrolling = true;
const direction = e.deltaY > 0 ? 1 : -1;
const target = window.scrollY + direction * STEP;
// 平滑動畫
smoothScrollTo(target, 200).then(() => isScrolling = false);
}, { passive: false }); // passive:false 才能調用 preventDefault()
function smoothScrollTo(targetY, duration) {
return new Promise(resolve => {
const startY = window.scrollY;
const distance = targetY - startY;
const startTime = performance.now();
function animate(now) {
const elapsed = now - startTime;
const progress = Math.min(elapsed / duration, 1);
// easeOutQuad 緩動
const ease = 1 - (1 - progress) * (1 - progress);
window.scrollTo(0, startY + distance * ease);
if (progress < 1) requestAnimationFrame(animate);
else resolve();
}
requestAnimationFrame(animate);
});
}
window.addEventListener('keydown', e => {
const keyMap = {
'ArrowDown': 1, 'ArrowUp': -1,
'PageDown': 10, 'PageUp': -10,
'Space': 10, 'Shift+Space': -10
};
const steps = keyMap[e.code] || keyMap[e.key];
if (steps) {
e.preventDefault();
smoothScrollTo(window.scrollY + steps * STEP, 150);
}
});
移動端通常**不建議**全局禁用 touchmove(會破壞原生滾動體驗、輸入框聚焦、下拉刷新)。若確有需要(如全屏 Canvas 遊戲),可在特定容器上:
container.addEventListener('touchmove', e => e.preventDefault(), { passive: false });
wheel/touchmove 為 passive: true(不可阻止),必須顯式聲明。wheel 事件需節流/防抖,避免啟動過多動畫任務。html { scroll-behavior: smooth; } 或 scroll-snap,無需 JS。掌握 preventDefault() + scrollTo + rAF 組合,即可構建任意自定義滾動體驗(全屏翻頁、視差滾動、無限滾動加載等)。