咨询点击屏幕底部上拖切换屏幕效果的名称及实现方法
Hey there! Let's break this down clearly for you:
What's this interaction called?
This effect is commonly referred to as Mouse-Drag Page Navigation (or sometimes Desktop Swipe Navigation, since it adapts mobile swipe gestures for mouse input). It lets users navigate between full-page sections by clicking, holding, and dragging upward (or other directions) instead of using scrollbars or buttons.
How to implement it
I'll cover two common scenarios: vanilla JavaScript (no frameworks) and Vue 3 (since it's widely used for interactive UIs).
Vanilla JavaScript Implementation
This example assumes you have full-height sections (<section> elements with height: 100vh) for each "screen":
- Set up your HTML structure
<div class="page-container"> <section class="screen">Screen 1</section> <section class="screen">Screen 2</section> <section class="screen">Screen 3</section> </div>
- Add basic CSS
body, html { margin: 0; padding: 0; overflow: hidden; } .screen { height: 100vh; display: flex; align-items: center; justify-content: center; font-size: 2rem; } .screen:nth-child(1) { background: #f0f0f0; } .screen:nth-child(2) { background: #e0e0e0; } .screen:nth-child(3) { background: #d0d0d0; }
- Add the JavaScript logic
const container = document.querySelector('.page-container'); const screens = document.querySelectorAll('.screen'); let isDragging = false; let startY = 0; const dragThreshold = 50; // Minimum distance to trigger navigation const bottomZoneHeight = window.innerHeight * 0.2; // 20% of viewport height for the trigger zone // Track mouse down event (only in bottom zone) document.addEventListener('mousedown', (e) => { if (e.clientY > window.innerHeight - bottomZoneHeight) { isDragging = true; startY = e.clientY; // Prevent text selection during drag document.body.style.userSelect = 'none'; } }); // Track mouse movement to detect drag direction document.addEventListener('mousemove', (e) => { if (!isDragging) return; const dragDistance = startY - e.clientY; // If user drags upward past threshold, switch to next screen if (dragDistance > dragThreshold) { const currentScreenIndex = Array.from(screens).findIndex(screen => screen.getBoundingClientRect().top <= 0); if (currentScreenIndex < screens.length - 1) { container.scrollTo({ top: (currentScreenIndex + 1) * window.innerHeight, behavior: 'smooth' }); } isDragging = false; document.body.style.userSelect = ''; } }); // End drag on mouse up document.addEventListener('mouseup', () => { isDragging = false; document.body.style.userSelect = ''; });
Vue 3 Implementation (Composition API)
If you're using Vue, you can clean up the logic with reactive state:
- Template
<template> <div class="page-container" ref="container"> <section class="screen" v-for="(screen, idx) in 3" :key="idx"> Screen {{ idx + 1 }} </section> </div> </template>
- Script
<script setup> import { ref, onMounted, onUnmounted } from 'vue'; const container = ref(null); let isDragging = ref(false); let startY = ref(0); const dragThreshold = 50; const bottomZoneHeight = window.innerHeight * 0.2; const handleMouseDown = (e) => { if (e.clientY > window.innerHeight - bottomZoneHeight) { isDragging.value = true; startY.value = e.clientY; document.body.style.userSelect = 'none'; } }; const handleMouseMove = (e) => { if (!isDragging.value) return; const dragDistance = startY.value - e.clientY; if (dragDistance > dragThreshold) { const currentScrollTop = container.value.scrollTop; const screenHeight = window.innerHeight; const nextScreenTop = Math.ceil(currentScrollTop / screenHeight) * screenHeight + screenHeight; if (nextScreenTop <= container.value.scrollHeight) { container.value.scrollTo({ top: nextScreenTop, behavior: 'smooth' }); } isDragging.value = false; document.body.style.userSelect = ''; } }; const handleMouseUp = () => { isDragging.value = false; document.body.style.userSelect = ''; }; onMounted(() => { document.addEventListener('mousedown', handleMouseDown); document.addEventListener('mousemove', handleMouseMove); document.addEventListener('mouseup', handleMouseUp); }); onUnmounted(() => { document.removeEventListener('mousedown', handleMouseDown); document.removeEventListener('mousemove', handleMouseMove); document.removeEventListener('mouseup', handleMouseUp); }); </script> <style scoped> /* Same CSS as vanilla example, add scoped tag */ body, html { margin: 0; padding: 0; overflow: hidden; } .screen { height: 100vh; display: flex; align-items: center; justify-content: center; font-size: 2rem; } .screen:nth-child(1) { background: #f0f0f0; } .screen:nth-child(2) { background: #e0e0e0; } .screen:nth-child(3) { background: #d0d0d0; } </style>
Key Notes
- Adjust
bottomZoneHeightto change how much of the bottom screen counts as the trigger area - Tweak
dragThresholdto make the navigation more or less sensitive - Add visual feedback (like a cursor change or section preview) to make the interaction more intuitive
内容的提问来源于stack exchange,提问作者Jeremy Clarkson

