store/scroll1

819 lines
11 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html>
<head>
<style>
:root {
--transition-duration: 0.8s;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html {
scroll-behavior: smooth;
scroll-snap-type: y mandatory;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
overflow-y: scroll;
scroll-snap-type: y mandatory;
}
/* 第一屏强制snap */
.first-screen-section {
height: 100vh;
scroll-snap-align: start;
scroll-snap-stop: always;
position: relative;
}
/* 第一屏内容 */
#hero {
background: linear-gradient(135deg, #1a2980 0%, #26d0ce 100%);
color: white;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
padding: 20px;
}
.hero-content {
max-width: 800px;
}
.hero-title {
font-size: clamp(2.5rem, 5vw, 4rem);
margin-bottom: 1rem;
opacity: 0;
transform: translateY(30px);
animation: fadeInUp 1s ease forwards 0.3s;
}
.hero-subtitle {
font-size: clamp(1rem, 2vw, 1.3rem);
opacity: 0.9;
margin-bottom: 2rem;
opacity: 0;
transform: translateY(30px);
animation: fadeInUp 1s ease forwards 0.6s;
}
.scroll-prompt {
position: absolute;
bottom: 40px;
display: flex;
flex-direction: column;
align-items: center;
gap: 12px;
opacity: 0;
animation: fadeIn 1s ease forwards 1s;
}
.chevron {
width: 24px;
height: 24px;
border-right: 3px solid rgba(255, 255, 255, 0.7);
border-bottom: 3px solid rgba(255, 255, 255, 0.7);
transform: rotate(45deg);
animation: bounce 2s infinite;
}
@keyframes fadeInUp {
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes fadeIn {
to {
opacity: 1;
}
}
@keyframes bounce {
0%, 100% {
transform: rotate(45deg) translateY(0);
}
50% {
transform: rotate(45deg) translateY(10px);
}
}
/* 第二屏开始正常滚动 */
.content-section {
min-height: 100vh;
padding: 80px 20px;
scroll-snap-align: none; /* 取消snap */
}
.section-content {
max-width: 1200px;
margin: 0 auto;
}
.content-title {
font-size: 2.5rem;
margin-bottom: 2rem;
color: #1a2980;
}
.content-text {
font-size: 1.1rem;
line-height: 1.8;
color: #333;
margin-bottom: 1.5rem;
}
/* 卡片样式 */
.feature-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 30px;
margin-top: 40px;
}
.feature-card {
background: white;
padding: 30px;
border-radius: 16px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08);
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.feature-card:hover {
transform: translateY(-5px);
box-shadow: 0 15px 40px rgba(0, 0, 0, 0.12);
}
.feature-icon {
font-size: 2.5rem;
margin-bottom: 1rem;
color: #26d0ce;
}
.feature-title {
font-size: 1.5rem;
margin-bottom: 1rem;
color: #1a2980;
}
/* 动态类 */
.snap-enabled {
scroll-snap-type: y mandatory;
}
.snap-disabled {
scroll-snap-type: none;
}
/* 进度指示器 */
.progress-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 4px;
background: rgba(0, 0, 0, 0.05);
z-index: 1000;
}
.progress-bar {
height: 100%;
background: linear-gradient(to right, #1a2980, #26d0ce);
width: 0%;
transition: width 0.3s ease;
}
</style>
</head>
<body>
<!-- 滚动进度条 -->
<div class="progress-container">
<div class="progress-bar" id="progressBar"></div>
</div>
<!-- 第一屏 -->
<section id="hero" class="first-screen-section">
<div class="hero-content">
<h1 class="hero-title">Start Your Journey</h1>
<p class="hero-subtitle">Experience seamless scrolling from first screen to second</p>
</div>
<div class="scroll-prompt" id="scrollPrompt">
<div class="chevron"></div>
<span style="font-size: 0.9rem; opacity: 0.8;">Scroll to Continue</span>
</div>
</section>
<!-- 正常内容区域 -->
<main id="content">
<section class="content-section">
<div class="section-content">
<h2 class="content-title">Welcome to Normal Scrolling</h2>
<p class="content-text">From here, you can scroll normally. The first screen was a special full-screen scroll, but now you have control.</p>
<div class="feature-grid">
<div class="feature-card">
<div class="feature-icon">🎯</div>
<h3 class="feature-title">Precision</h3>
<p>Accurate and responsive scrolling experience tailored to your needs.</p>
</div>
<div class="feature-card">
<div class="feature-icon">⚡</div>
<h3 class="feature-title">Performance</h3>
<p>Optimized scrolling with smooth transitions and minimal lag.</p>
</div>
<div class="feature-card">
<div class="feature-icon">🎨</div>
<h3 class="feature-title">Design</h3>
<p>Beautiful visual feedback and intuitive user interactions.</p>
</div>
</div>
</div>
</section>
<section class="content-section" style="background: #f8fafc;">
<div class="section-content">
<h2 class="content-title">More Content</h2>
<p class="content-text">This section demonstrates normal scrolling behavior. You can scroll freely without any forced snapping.</p>
<div style="height: 800px; display: flex; flex-direction: column; gap: 20px; padding: 20px; background: white; border-radius: 12px; margin-top: 40px;">
<div style="height: 200px; background: linear-gradient(90deg, #f0f0f0, #ffffff); border-radius: 8px;"></div>
<div style="height: 200px; background: linear-gradient(90deg, #ffffff, #f0f0f0); border-radius: 8px;"></div>
<div style="height: 200px; background: linear-gradient(90deg, #f0f0f0, #ffffff); border-radius: 8px;"></div>
</div>
</div>
</section>
<section class="content-section">
<div class="section-content">
<h2 class="content-title">Final Section</h2>
<p class="content-text">You've reached the end of the example. The transition from first screen to normal scrolling should have been seamless.</p>
</div>
</section>
</main>
<script>
class SmartScrollController {
constructor() {
this.firstScreen = document.getElementById('hero');
this.scrollPrompt = document.getElementById('scrollPrompt');
this.progressBar = document.getElementById('progressBar');
this.body = document.body;
this.isFirstScreenActive = true;
this.isAnimating = false;
this.lastScrollY = 0;
this.init();
}
init() {
// 初始启用snap
this.enableSnap();
// 监听滚动事件
this.setupScrollListeners();
// 监听滚动提示点击
this.setupClickListeners();
// 监听键盘
this.setupKeyboardListeners();
// 更新进度条
this.updateProgressBar();
}
setupScrollListeners() {
let ticking = false;
window.addEventListener('scroll', () => {
if (!ticking) {
requestAnimationFrame(() => {
this.handleScroll();
ticking = false;
});
ticking = true;
}
// 更新进度条
this.updateProgressBar();
});
// 检测第一屏是否离开
const observer = new IntersectionObserver(
(entries) => {
entries.forEach(entry => {
if (!entry.isIntersecting && this.isFirstScreenActive) {
// 第一屏离开视口禁用snap
this.disableSnap();
this.isFirstScreenActive = false;
// 隐藏滚动提示
this.scrollPrompt.style.opacity = '0';
this.scrollPrompt.style.transition = 'opacity 0.5s ease';
}
});
},
{
threshold: 0.1,
rootMargin: '0px'
}
);
observer.observe(this.firstScreen);
}
setupClickListeners() {
// 点击滚动提示
this.scrollPrompt.addEventListener('click', () => {
this.scrollToNextSection();
});
// 点击第一屏其他区域
this.firstScreen.addEventListener('click', (e) => {
if (e.target === this.firstScreen) {
this.scrollToNextSection();
}
});
}
setupKeyboardListeners() {
window.addEventListener('keydown', (e) => {
if (this.isAnimating) return;
if (this.isFirstScreenActive) {
if (e.key === 'ArrowDown' || e.key === 'PageDown' || e.key === ' ') {
e.preventDefault();
this.scrollToNextSection();
}
}
});
}
handleScroll() {
const currentScrollY = window.scrollY;
const firstScreenHeight = this.firstScreen.offsetHeight;
if (this.isFirstScreenActive) {
// 如果用户尝试手动滚动离开第一屏,帮助他们完成过渡
if (currentScrollY > 50 && currentScrollY < firstScreenHeight && currentScrollY > this.lastScrollY) {
// 向下滚动,帮助过渡到第二屏
this.scrollToNextSection();
}
}
this.lastScrollY = currentScrollY;
}
scrollToNextSection() {
if (this.isAnimating) return;
this.isAnimating = true;
const firstScreenHeight = this.firstScreen.offsetHeight;
const targetScroll = firstScreenHeight;
// 平滑滚动
window.scrollTo({
top: targetScroll,
behavior: 'smooth'
});
// 滚动完成后标记
setTimeout(() => {
this.isAnimating = false;
this.disableSnap();
this.isFirstScreenActive = false;
this.scrollPrompt.style.opacity = '0';
}, 800);
}
enableSnap() {
this.body.classList.add('snap-enabled');
this.body.classList.remove('snap-disabled');
}
disableSnap() {
this.body.classList.add('snap-disabled');
this.body.classList.remove('snap-enabled');
// 移除snap属性
document.documentElement.style.scrollSnapType = 'none';
document.body.style.scrollSnapType = 'none';
}
updateProgressBar() {
const scrollTop = window.scrollY;
const scrollHeight = document.documentElement.scrollHeight - window.innerHeight;
const progress = (scrollTop / scrollHeight) * 100;
this.progressBar.style.width = `${progress}%`;
}
}
// 初始化
const scrollController = new SmartScrollController();
// 处理页面刷新时的位置
window.addEventListener('load', () => {
const scrollY = window.scrollY;
const firstScreenHeight = document.getElementById('hero').offsetHeight;
if (scrollY > firstScreenHeight * 0.5) {
// 如果刷新时已经超过第一屏的一半直接禁用snap
scrollController.disableSnap();
scrollController.isFirstScreenActive = false;
scrollController.scrollPrompt.style.opacity = '0';
}
});
</script>
</body>
</html>