You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

709 lines
24 KiB
HTML

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0"
/>
<title>强烈吸附滚动效果</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family:
'Segoe UI',
system-ui,
-apple-system,
sans-serif;
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
color: #fff;
overflow: hidden;
height: 100vh;
}
/* 导航指示器 */
.nav-indicator {
position: fixed;
right: 30px;
top: 50%;
transform: translateY(-50%);
z-index: 100;
display: flex;
flex-direction: column;
gap: 20px;
background: rgba(255, 255, 255, 0.1);
padding: 20px 15px;
border-radius: 30px;
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.2);
}
.nav-dot {
width: 12px;
height: 12px;
background: rgba(255, 255, 255, 0.3);
border-radius: 50%;
cursor: pointer;
transition: all 0.4s cubic-bezier(0.68, -0.55, 0.265, 1.55);
position: relative;
}
.nav-dot:hover::after {
content: attr(data-title);
position: absolute;
right: 25px;
top: 50%;
transform: translateY(-50%);
background: rgba(0, 0, 0, 0.8);
color: white;
padding: 6px 12px;
border-radius: 6px;
font-size: 14px;
white-space: nowrap;
pointer-events: none;
}
.nav-dot.active {
width: 18px;
height: 18px;
background: #00ff88;
box-shadow: 0 0 20px rgba(0, 255, 136, 0.5);
animation: pulse 2s infinite;
}
@keyframes pulse {
0%,
100% {
transform: scale(1);
box-shadow: 0 0 20px rgba(0, 255, 136, 0.5);
}
50% {
transform: scale(1.2);
box-shadow: 0 0 30px rgba(0, 255, 136, 0.8);
}
}
/* 滚动容器 */
.scroll-container {
height: 100vh;
overflow-y: auto;
scroll-behavior: auto; /* 禁用默认平滑滚动,使用自定义动画 */
scroll-snap-type: y mandatory;
position: relative;
scrollbar-width: none; /* Firefox */
-ms-overflow-style: none; /* IE/Edge */
}
.scroll-container::-webkit-scrollbar {
display: none; /* Chrome/Safari */
}
/* 滚动区域 */
.section {
height: 100vh;
scroll-snap-align: start;
position: relative;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 40px;
opacity: 0.5;
transform: scale(0.9);
transition: all 0.8s cubic-bezier(0.34, 1.56, 0.64, 1);
border-bottom: 2px solid rgba(255, 255, 255, 0.1);
}
.section.active {
opacity: 1;
transform: scale(1);
z-index: 2;
}
/* 每个section的独特样式 */
.section-1 {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.section-2 {
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
}
.section-3 {
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
}
.section-4 {
background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);
}
.section-5 {
background: linear-gradient(135deg, #fa709a 0%, #fee140 100%);
}
/* 内容样式 */
.section-content {
text-align: center;
max-width: 800px;
padding: 40px;
background: rgba(0, 0, 0, 0.3);
border-radius: 20px;
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.1);
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);
transform: translateY(50px);
transition: transform 0.8s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.section.active .section-content {
transform: translateY(0);
}
.section h1 {
font-size: 3.5rem;
margin-bottom: 20px;
background: linear-gradient(45deg, #fff, #00ff88);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
text-shadow: 0 5px 15px rgba(0, 255, 136, 0.3);
}
.section p {
font-size: 1.2rem;
line-height: 1.6;
margin-bottom: 30px;
color: rgba(255, 255, 255, 0.9);
}
/* 控制按钮 */
.controls {
position: fixed;
bottom: 40px;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 20px;
z-index: 100;
}
.control-btn {
background: rgba(255, 255, 255, 0.2);
border: none;
color: white;
padding: 15px 30px;
border-radius: 50px;
font-size: 16px;
cursor: pointer;
transition: all 0.3s ease;
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.3);
display: flex;
align-items: center;
gap: 10px;
font-weight: 600;
}
.control-btn:hover {
background: rgba(255, 255, 255, 0.3);
transform: translateY(-3px);
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2);
}
.control-btn:active {
transform: translateY(-1px);
}
/* 滚动提示 */
.scroll-hint {
position: absolute;
bottom: 40px;
left: 50%;
transform: translateX(-50%);
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
color: rgba(255, 255, 255, 0.7);
animation: bounce 2s infinite;
opacity: 0;
transition: opacity 0.5s;
}
.section.active .scroll-hint {
opacity: 1;
}
@keyframes bounce {
0%,
20%,
50%,
80%,
100% {
transform: translateY(0) translateX(-50%);
}
40% {
transform: translateY(-10px) translateX(-50%);
}
60% {
transform: translateY(-5px) translateX(-50%);
}
}
.mouse {
width: 30px;
height: 50px;
border: 2px solid rgba(255, 255, 255, 0.7);
border-radius: 20px;
position: relative;
}
.wheel {
width: 4px;
height: 10px;
background: rgba(255, 255, 255, 0.7);
border-radius: 2px;
position: absolute;
top: 10px;
left: 50%;
transform: translateX(-50%);
animation: wheel 2s infinite;
}
@keyframes wheel {
0% {
transform: translateX(-50%) translateY(0);
opacity: 1;
}
100% {
transform: translateX(-50%) translateY(20px);
opacity: 0;
}
}
/* 进度条 */
.progress-bar {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 4px;
background: rgba(255, 255, 255, 0.1);
z-index: 1000;
}
.progress {
height: 100%;
background: linear-gradient(90deg, #00ff88, #00ccff);
width: 0%;
transition: width 0.3s ease;
box-shadow: 0 0 10px rgba(0, 255, 136, 0.5);
}
/* 响应式设计 */
@media (max-width: 768px) {
.section h1 {
font-size: 2.5rem;
}
.section-content {
padding: 20px;
margin: 20px;
}
.nav-indicator {
right: 15px;
padding: 15px 10px;
}
.controls {
bottom: 20px;
}
.control-btn {
padding: 12px 20px;
font-size: 14px;
}
}
</style>
</head>
<body>
<!-- 进度条 -->
<div class="progress-bar">
<div class="progress"></div>
</div>
<!-- 导航指示器 -->
<div class="nav-indicator">
<div
class="nav-dot active"
data-title="欢迎"
onclick="scrollToSection(0)"
></div>
<div
class="nav-dot"
data-title="功能"
onclick="scrollToSection(1)"
></div>
<div
class="nav-dot"
data-title="特性"
onclick="scrollToSection(2)"
></div>
<div
class="nav-dot"
data-title="演示"
onclick="scrollToSection(3)"
></div>
<div
class="nav-dot"
data-title="开始"
onclick="scrollToSection(4)"
></div>
</div>
<!-- 滚动容器 -->
<div
class="scroll-container"
id="scrollContainer"
>
<!-- Section 1 -->
<section class="section section-1 active">
<div class="section-content">
<h1>🎯 强烈吸附滚动</h1>
<p>体验具有强烈动画效果的滚动吸附技术。每个页面都有独特的过渡效果和视觉反馈。</p>
<p>使用自定义缓动函数和物理模拟实现流畅而有力的滚动体验。</p>
</div>
<div class="scroll-hint">
<div class="mouse">
<div class="wheel"></div>
</div>
<span>向下滚动</span>
</div>
</section>
<!-- Section 2 -->
<section class="section section-2">
<div class="section-content">
<h1>⚡ 强力动画</h1>
<p>自定义缓动函数创造强烈的物理感,让滚动不再单调。</p>
<p>每个滚动动作都有弹簧般的回弹效果和流畅的视觉过渡。</p>
<p>基于物理的动画模拟,让交互更加自然和令人愉悦。</p>
</div>
</section>
<!-- Section 3 -->
<section class="section section-3">
<div class="section-content">
<h1>🎨 视觉反馈</h1>
<p>实时视觉反馈增强用户感知,让每个交互都有明确的响应。</p>
<p>缩放、阴影、发光效果共同创造沉浸式的浏览体验。</p>
<p>进度指示器和导航点帮助用户了解当前位置。</p>
</div>
</section>
<!-- Section 4 -->
<section class="section section-4">
<div class="section-content">
<h1>🚀 性能优化</h1>
<p>使用GPU加速的CSS transform属性确保60fps流畅动画。</p>
<p>智能节流和防抖技术,避免过度渲染。</p>
<p>移动端优化,保持触控设备的流畅体验。</p>
</div>
</section>
<!-- Section 5 -->
<section class="section section-5">
<div class="section-content">
<h1>🚀 立即开始</h1>
<p>将这种强大的滚动效果应用到你的项目中,提升用户体验。</p>
<p>代码完全可定制,支持响应式设计,兼容现代浏览器。</p>
</div>
</section>
</div>
<!-- 控制按钮 -->
<div class="controls">
<button
class="control-btn"
onclick="prevSection()"
>
<span>⬆️</span> 上一页
</button>
<button
class="control-btn"
onclick="nextSection()"
>
下一页 <span>⬇️</span>
</button>
</div>
<script>
// 获取元素
const scrollContainer = document.getElementById('scrollContainer');
const sections = document.querySelectorAll('.section');
const navDots = document.querySelectorAll('.nav-dot');
const progress = document.querySelector('.progress');
// 当前激活的section索引
let currentSection = 0;
let isScrolling = false;
// 自定义缓动函数 - 强烈的弹性效果
const easingFunctions = {
easeOutElastic: (x) => {
const c4 = (2 * Math.PI) / 3;
return x === 0 ? 0 : x === 1 ? 1 : Math.pow(2, -10 * x) * Math.sin((x * 10 - 0.75) * c4) + 1;
},
easeOutBack: (x) => {
const c1 = 1.70158;
const c3 = c1 + 1;
return 1 + c3 * Math.pow(x - 1, 3) + c1 * Math.pow(x - 1, 2);
},
// 强烈的弹跳效果
strongBounce: (x) => {
if (x < 1 / 2.75) {
return 7.5625 * x * x;
} else if (x < 2 / 2.75) {
return 7.5625 * (x -= 1.5 / 2.75) * x + 0.75;
} else if (x < 2.5 / 2.75) {
return 7.5625 * (x -= 2.25 / 2.75) * x + 0.9375;
} else {
return 7.5625 * (x -= 2.625 / 2.75) * x + 0.984375;
}
},
};
// 平滑滚动函数
function smoothScrollTo(targetPosition, duration = 800) {
if (isScrolling) return;
isScrolling = true;
const startPosition = scrollContainer.scrollTop;
const distance = targetPosition - startPosition;
const startTime = performance.now();
// 根据滚动距离调整持续时间
const adjustedDuration = Math.min(duration, 500 + Math.abs(distance) / 3);
function animate(currentTime) {
const elapsed = currentTime - startTime;
const progress = Math.min(elapsed / adjustedDuration, 1);
// 使用强烈的弹性效果
const easing = easingFunctions.easeOutElastic(progress);
scrollContainer.scrollTop = startPosition + distance * easing;
if (progress < 1) {
requestAnimationFrame(animate);
} else {
setTimeout(() => {
isScrolling = false;
updateActiveSection();
}, 100);
}
}
requestAnimationFrame(animate);
}
// 滚动到指定section
function scrollToSection(index) {
if (index < 0 || index >= sections.length || isScrolling) return;
currentSection = index;
const targetSection = sections[index];
const targetPosition = targetSection.offsetTop;
// 更新导航点
updateNavDots(index);
// 执行平滑滚动
smoothScrollTo(targetPosition, 1000);
}
// 下一个section
function nextSection() {
if (currentSection < sections.length - 1) {
scrollToSection(currentSection + 1);
} else {
// 如果已经是最后一个,回到第一个
scrollToSection(0);
}
}
// 上一个section
function prevSection() {
if (currentSection > 0) {
scrollToSection(currentSection - 1);
} else {
// 如果已经是第一个,跳到最后
scrollToSection(sections.length - 1);
}
}
// 更新导航点状态
function updateNavDots(activeIndex) {
navDots.forEach((dot, index) => {
dot.classList.toggle('active', index === activeIndex);
});
}
// 更新section激活状态
function updateActiveSection() {
const scrollMiddle = scrollContainer.scrollTop + scrollContainer.clientHeight / 2;
sections.forEach((section, index) => {
const sectionTop = section.offsetTop;
const sectionBottom = sectionTop + section.offsetHeight;
if (scrollMiddle >= sectionTop && scrollMiddle < sectionBottom) {
section.classList.add('active');
currentSection = index;
updateNavDots(index);
} else {
section.classList.remove('active');
}
});
}
// 更新进度条
function updateProgress() {
const scrollTop = scrollContainer.scrollTop;
const scrollHeight = scrollContainer.scrollHeight - scrollContainer.clientHeight;
const progressPercent = (scrollTop / scrollHeight) * 100;
progress.style.width = `${progressPercent}%`;
}
// 处理鼠标滚轮事件
let wheelTimeout;
scrollContainer.addEventListener(
'wheel',
(e) => {
e.preventDefault();
clearTimeout(wheelTimeout);
wheelTimeout = setTimeout(() => {
if (isScrolling) return;
const direction = e.deltaY > 0 ? 1 : -1;
const newIndex = Math.max(0, Math.min(sections.length - 1, currentSection + direction));
if (newIndex !== currentSection) {
scrollToSection(newIndex);
}
}, 100);
},
{ passive: false }
);
// 处理触摸滑动
let touchStartY = 0;
scrollContainer.addEventListener(
'touchstart',
(e) => {
touchStartY = e.touches[0].clientY;
},
{ passive: true }
);
scrollContainer.addEventListener(
'touchend',
(e) => {
if (isScrolling) return;
const touchEndY = e.changedTouches[0].clientY;
const deltaY = touchStartY - touchEndY;
// 只有滑动距离足够大才触发
if (Math.abs(deltaY) > 50) {
const direction = deltaY > 0 ? 1 : -1;
const newIndex = Math.max(0, Math.min(sections.length - 1, currentSection + direction));
if (newIndex !== currentSection) {
scrollToSection(newIndex);
}
}
},
{ passive: true }
);
// 监听滚动事件
scrollContainer.addEventListener('scroll', () => {
updateActiveSection();
updateProgress();
});
// 键盘控制
document.addEventListener('keydown', (e) => {
if (e.key === 'ArrowDown' || e.key === ' ') {
e.preventDefault();
nextSection();
} else if (e.key === 'ArrowUp') {
e.preventDefault();
prevSection();
} else if (e.key === 'Home') {
e.preventDefault();
scrollToSection(0);
} else if (e.key === 'End') {
e.preventDefault();
scrollToSection(sections.length - 1);
}
});
// 初始更新
updateProgress();
// 添加一些动态效果到section内容
sections.forEach((section, index) => {
const content = section.querySelector('.section-content');
// 鼠标悬停效果
content.addEventListener('mouseenter', () => {
if (section.classList.contains('active')) {
content.style.transform = 'translateY(-10px) scale(1.02)';
content.style.boxShadow = '0 30px 60px rgba(0, 0, 0, 0.4)';
}
});
content.addEventListener('mouseleave', () => {
if (section.classList.contains('active')) {
content.style.transform = 'translateY(0) scale(1)';
content.style.boxShadow = '0 20px 40px rgba(0, 0, 0, 0.3)';
}
});
});
// 自动滚动演示(可选)
let autoScrollInterval;
function startAutoScroll() {
if (autoScrollInterval) clearInterval(autoScrollInterval);
autoScrollInterval = setInterval(() => {
nextSection();
}, 5000);
}
function stopAutoScroll() {
if (autoScrollInterval) clearInterval(autoScrollInterval);
}
// 用户交互时停止自动滚动
scrollContainer.addEventListener('wheel', stopAutoScroll);
scrollContainer.addEventListener('touchstart', stopAutoScroll);
document.addEventListener('keydown', stopAutoScroll);
// 页面加载后开始自动滚动(可选)
// setTimeout(() => startAutoScroll(), 3000);
</script>
</body>
</html>