HTML文档的内容可能非常长,难以通过滚动来访问。 由于这个艰巨的任务,开发人员经常使用内部链接(页面跳转)作为页面周围的另一种传输模式。这个有用的技术已经改进,有了 Javascript 的帮助可以提供更好的体验,我们可以借助 Gumshoe, Smooth Scroll 和 Anime.js 自定义滚动监听脚本。滚动监听主要用于根据滚动位置自动更新导航列表中的链接。
通过这个教程,我们将构建一个自定义滚动监听( Scrollspy )组件。效果如下:
要完成这个自定义滚动监听( Scrollspy )我们将使用:
- Gumshoe:简单,与框架无关的滚动监听脚本。
- Smooth Scroll:轻量脚本动画滚动到锚点链接。
- Anime.js:灵活而轻巧的 Javascript 动画库。
可以点上面的链接访问它们的 Github ,了解它们的功能和使用方法。
HTML
让我们从我们将要使用的HTML结构开始,描述注释中的关键元素:
<section>
<!-- Fixed header -->
<!-- The [data-gumshoe-header] attribute tell Gumshoe that automatically offset it's calculations based on the header's height -->
<!-- The [data-scroll-header] attribute do the same thing but for Smooth Scroll calculations -->
<header class="page-header" data-gumshoe-header data-scroll-header>
<div class="page-nav">
<!-- Nav and links -->
<!-- The [data-gumshoe] attribute indicates the navigation list that Gumshoe should watch -->
<nav data-gumshoe>
<!-- Turn anchor links into Smooth Scroll links by adding the [data-scroll] data attribute -->
<a data-scroll href="#eenie">Eenie</a>
<a data-scroll href="#meanie">Meanie</a>
<a data-scroll href="#minnie">Minnie</a>
<a data-scroll href="#moe">Moe</a>
</nav>
<!-- Arrows -->
<a class="nav-arrow nav-arrow-left"><svg class="icon"><use xlink:href="#arrow-up"/></svg></a>
<a class="nav-arrow nav-arrow-right"><svg class="icon"><use xlink:href="#arrow-down"/></svg></a>
</div>
</header>
<!-- Page content -->
<main class="page-content">
<section>
<h2 id="eenie"><a data-scroll href="#eenie">Eenie</a></h2>
<p>Lorem ipsum dolor sit amet, has dico eligendi ut.</p>
<!-- MORE CONTENT HERE -->
</section>
</main>
</section>
CSS
为上面的 HTML 添加一些风格。
h2 {
/* This is to solve the headbutting/padding issue. Read more: https://css-tricks.com/hash-tag-links-padding/ */
/* 110px = 80px (fixed header) + 30px (additional margin) */
&:before {
display: block;
content: " ";
margin-top: -110px;
height: 110px;
visibility: hidden;
}
}
/* Fixed header */
.page-header {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 80px; /* The height of fixed header */
background-color: #2D353F;
text-align: center;
z-index: 2;
}
/* Content container */
.page-content {
display: inline-block; /* This is for clearing purpose. */
margin: 80px 50px 30px; /* Margin top = 80px because of fixed header */
}
/* Nav container */
.page-nav {
display: inline-block;
position: relative;
margin-top: 20px;
height: 40px; /* This is the same height of each link */
width: 400px;
max-width: 100%; /* Responsive behavior */
overflow: hidden; /* Only current link visible */
background-color: #427BAB;
}
/* Nav and links */
nav {
position: relative;
width: 100%;
line-height: 40px;
text-align: center;
background-color: rgba(0, 0, 0, 0.05);
a {
display: block;
font-size: 18px;
color: #fff;
outline: none;
}
}
JAVASCRIPT
开始增加功能和动画,我们需要获得需要此功能的所有元素。此外,我们将声明使用的附加变量。
// Init variables
var navOpen = false;
var pageNav = document.querySelector('.page-nav');
var navEl = document.querySelector('.page-nav nav');
var navLinks = document.querySelectorAll('.page-nav nav a');
var arrowLeft = document.querySelector('.nav-arrow-left');
var arrowRight = document.querySelector('.nav-arrow-right');
var navHeight = 40;
var activeIndex, activeDistance, activeItem, navAnimation, navItemsAnimation;
以下是关键部分。此函数将 nav
元素转换为仅显示所选链接,使用 activeIndex
值。
// This translate the nav element to show the selected item
function translateNav(item) {
// If animation is defined, pause it
if (navItemsAnimation) navItemsAnimation.pause();
// Animate the `translateY` of `nav` to show only the current link
navItemsAnimation = anime({
targets: navEl,
translateY: (item ? -activeIndex * navHeight : 0) + 'px',
easing: 'easeOutCubic',
duration: 500
});
// Update link on arrows, and disable/enable accordingly if first or last link
updateArrows();
}
然后,我们需要一种方法来打开和关闭 。打开状态应该让我们看到所有的链接,让我们可以直接选择其中之一。关闭状态是默认关闭状态,只看到选定的链接。
// Open the nav, showing all the links
function openNav() {
// Updating states
navOpen = !navOpen;
pageNav.classList.add('nav-open');
// Moving the nav just like first link is active
translateNav();
// Animate the `height` of the nav, letting see all the links
navAnimation = anime({
targets: pageNav,
height: navLinks.length * navHeight + 'px',
easing: 'easeOutCubic',
duration: 500
});
}
// Close the nav, showing only the selected link
function closeNav() {
// Updating states
navOpen = !navOpen;
pageNav.classList.remove('nav-open');
// Moving the nav showing only the active link
translateNav(activeItem);
// Animate the `height` of the nav, letting see just the active link
navAnimation = anime({
targets: pageNav,
height: navHeight + 'px',
easing: 'easeOutCubic',
duration: 500
});
}
现在让我们来看看如何处理事件。我们需要处理程序来相应地打开或关闭 nav
。
// Init click events for each nav link
for (var i = 0; i < navLinks.length; i++) {
navLinks[i].addEventListener('click', function (e) {
if (navOpen) {
// Just close the `nav`
closeNav();
} else {
// Prevent scrolling to the active link and instead open the `nav`
e.preventDefault();
e.stopPropagation();
openNav();
}
});
}
document.addEventListener('click', function (e) {
if (navOpen) {
var isClickInside = pageNav.contains(e.target);
if (!isClickInside) {
closeNav();
}
}
});
现在,准备让 Gumshoe 和 Smooth Scroll 变魔术。看看如何初始化它们:
// Init Smooth Scroll
// Init Gumshoe
gumshoe.init({
// The callback is triggered after setting the active link, to show it as active in the `nav`
callback: function (nav) {
// Check if active link has changed
if (activeDistance !== nav.distance) {
// Update states
activeDistance = nav.distance;
activeItem = nav.nav;
activeIndex = getIndex(activeItem);
// Translate `nav` to show the active link, or close it
if (navOpen) {
closeNav();
} else {
translateNav(activeItem);
}
}
}
});
完成!
2017-08-01