Image placeholder

响应式兼容手机的侧栏垂直固定导航

Image placeholder
F2EX 2017-09-25

一个响应式的垂直固定导航,当用户交互时,圆形指示器将变成图标。在移动端中,导航自动移动到右下角。使用 CSS 和 jQuery 。

将圆形指示器导航放在网页一侧的基本思想是向用户提示有多少个页面(横切)。每个指示器导航都对应一个页面(横切)。在单一的指示器导航中,用户必须点击指示器导航才知道这个页面是什么内容。

为了优化这种模式,我们决定在用户与指示器导航进行交互时,通过放大并显示图标+标签来切换页面(横切)。用户不需要点击进入页面,只需要将鼠标移动到一个指示器导航中,他们就知道即将访问的页面将是什么主题内容。

下面是这个效果的快速预览:

创建结构

指示器导航是包含在 nav.cd-vertical-nav 中的无序列表。 button.cd-nav-trigger 用于打开移动设备上的导航。

此外,还为每个页面(横切)创建一个 section.cd-section 部分。

<nav class="cd-vertical-nav">
<ul>
<li><a href="#section1" class="active"><span class="label">Intro</span></a></li>
<li><a href="#section2"><span class="label">Events</span></a></li>
<!-- 其他导航项目在这里 -->
</ul>
</nav><!-- .cd-vertical-nav -->
<button class="cd-nav-trigger cd-image-replace">Open navigation<span aria-hidden="true"></span></button>
<section id="section1" class="cd-section">
<div class="content-wrapper">
<h1>Vertical Fixed Navigation #2</h1>
<a href="#section2" class="cd-scroll-down cd-image-replace">scroll down</a>
</div>
</section><!-- cd-section -->
<section id="section2" class="cd-section">
<div class="content-wrapper">
<!-- 横切内容 -->
</div>
</section><!-- cd-section -->
<!-- 附加横切部分在这里 -->

添加样式

在小型设备(视口宽度小于800px)上,我们设置了一个位置:固定为 .cd-nav-trigger 和 <nav> 元素,并将它们放置在页面的右下角;然后我们缩小导航,使用右下角作为变换导航。

当用户单击 .cd-nav-trigger 元素时,.open 类被附加到导航,将其缩放值从 0 更改为 1 ,并通过 CSS3 过渡来实现平滑动画。

.cd-nav-trigger {
  display: block;
  position: fixed;
  z-index: 2;
  bottom: 30px;
  right: 5%;
}
 
.cd-vertical-nav {
  position: fixed;
  z-index: 1;
  right: 5%;
  bottom: 30px;
  transform: scale(0);
  transform-origin: right bottom;
  transition: transform 0.2s;
}
.cd-vertical-nav.open {
  transform: scale(1);
}

在较大的设备上,我们使用 Modernizr 来检测触摸和无触摸设备( 使用 .touch.no-touch 类)。

在触摸设备上,默认情况下,指示器导航项(标签和图标)默认可见,而在无触摸设备上,当用户悬停在指示器导航上时,才会显示它们。

我们为 <nav> 元素设置一个固定的高度和宽度,并将其放在视口的右侧。我们使用它的 ::before 伪元素来创建导航背景;仅在无触摸设备上,::before 元素默认情况下在右侧(视口外),并在用户悬停在导航栏上时将其移回原位置。对于 span.label 元素也是一样。

@media only screen and (min-width: 800px) {
  .cd-vertical-nav {
    right: 0;
    top: 0;
    height: 100vh;
    width: 90px;
  }
  .cd-vertical-nav::before {
    /* this is the navigation background */
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: rgba(0, 0, 0, 0.8);
    transform: translateX(100%);
    transition: transform 0.4s;
  }
  .no-touch .cd-vertical-nav:hover::before, 
  .touch .cd-vertical-nav::before {
    transform: translateX(0);
  }
  .cd-vertical-nav .label {
    display: block;
    transform: translateX(100%);
    transition: transform 0.4s;
  }
  .no-touch .cd-vertical-nav:hover .label, 
  .touch .cd-vertical-nav .label {
    transform: translateX(0);
  }
}

要创建导航项目的图标和指示器(小圆点),我们分别使用导航锚点元素(&ly;a>)中的 ::after::before 伪元素。仅在无触摸设备上,::after::before 默认缩小,然后在用户悬停在导航上时放大。

@media only screen and (min-width: 800px) {
  .cd-vertical-nav a {
    position: relative;
    padding: 3em 0 0;
    margin: 1.4em auto;
  }
  .cd-vertical-nav a::before, 
  .cd-vertical-nav a::after {
    /* used to create the filled circle and the background icon */
    content: '';
    position: absolute;
    left: 50%;
    transition: transform 0.4s 0s;
  }
  .cd-vertical-nav a::before {
    /* filled circle */
    top: 0;
    height: 32px;
    width: 32px;
    border-radius: 50%;
    background: #eaf2e3;
    transform: translateX(-50%) scale(0.25);
  }
  .cd-vertical-nav a::after {
    /* icon */
    top: 8px;
    height: 16px;
    width: 16px;
    background: url(../img/cd-nav-icons.svg) no-repeat;
    transform: translateX(-50%) scale(0);
  }
  .no-touch .cd-vertical-nav:hover a::before, 
  .no-touch .cd-vertical-nav:hover a::after, 
  .touch .cd-vertical-nav li:nth-of-type(n) a::before, 
  .touch .cd-vertical-nav li:nth-of-type(n) a::after {
    transform: translateX(-50%) scale(1);
  }
}

现在是难点的部分:当指示器导航(小圆点)缩小时,它们距离另一个太远。我们可以减少沿着 Y 轴来转换它们的距离。

我们从中心点开始为例(例子中的第二个和第三个); 例子中的第二个 translateY 是一个正值,第三个 translateY 是一个负值。例:

.cd-vertical-nav li:nth-of-type(2) a::after {
    transform: translateX(-50%) translateY(1.5em) scale(0);
  }
  .cd-vertical-nav li:nth-of-type(2) a::before {
    transform: translateX(-50%) translateY(1.5em) scale(0.25);
  }
  .cd-vertical-nav li:nth-of-type(3) a::after {
    transform: translateX(-50%) translateY(-1.5em) scale(0);
  }
  .cd-vertical-nav li:nth-of-type(3) a::before {
    transform: translateX(-50%) translateY(-1.5em) scale(0.25);
  }

然后,第一个指示器导航(小圆点)的 translateY 值将是第二个点的三倍,而第四个点的值与第一个相同(第三个点的值的三倍)。

.cd-vertical-nav li:first-of-type a::after {
    transform: translateX(-50%) translateY(4.5em) scale(0);
  }
  .cd-vertical-nav li:first-of-type a::before {
    transform: translateX(-50%) translateY(4.5em) scale(0.25);
  }
  .cd-vertical-nav li:nth-of-type(4) a::after {
    transform: translateX(-50%) translateY(-4.5em) scale(0);
  }
  .cd-vertical-nav li:nth-of-type(4) a::before {
    transform: translateX(-50%) translateY(-4.5em) scale(0.25);
  }

如果你有很多数量的导航项目,则必须相应地更改这些 translateY 的值。 例如,如果你有六个项目,从中心点(以第三个和第四个)开始计算,你可以为其分配一个 1.5em/-1.5em 的 translateY 值; 然后到第二和第五个 translateY 值为 4.5em/-4.5em(3 * 1.5),最后到第一和第六个 translateY 值为 7.5em/-7.5em(5 * 1.5)。

如果你有一个奇数的项目,比如 5 个导航项目,你不用更改中心的值(第三个)。然后,你可以将第二和第四个点的 3em/-3em(2 * 1.5)的 translateY 值分配给第一和第五个点的 6em/-6em(4 * 1.5)的 translateY 值。

事件处理

当用户滚动页面(横切)时,updateSections() 函数将判断当前正在查看的部分,并将 .active 类分配给相应的导航项。

此外,我们监听 button.cd-nav-trigger 上的点击事件以打开/关闭小型设备上的导航。


2017-09-25