优化实战 第 14 期 - 页面平滑滚动(返回顶部)

1,036 阅读2分钟

在一个长内容的页面中,当滚动到一定的距离时,会出现一个 "返回顶部" 的图标,点击会平滑的返回到页面顶部,有助于提升用户体验

页面内平滑滚动

  • 使用 a 标签锚点定位

    <nav>
      <a href="#hat">点位一</a>
      <a href="#shoes">点位二</a>
      <a href="#clothes">点位三</a>
    </nav>
    <section id="hat">内容一</section>
    <section id="shoes">内容二</section>
    <section id="clothes">内容三</section>
    

    给根元素添加滚动行为属性,实现锚点定位平滑滚动效果

    html {
      scroll-behavior: smooth;
    }
    

    注意:在使用上依赖于 a 标签的 href 属性,不仅有局限性,还会污染页面路径,如果页面路径中包含有参数,则不推荐使用

  • 使用 Element 的 scrollIntoView 方法

    <ul id="navigation">
      <li data-anchor="hat">点位一</li>
      <li data-anchor="shoes">点位二</li>
      <li data-anchor="clothes">点位三</li>
    </ul>
    <section id="hat">内容一</section>
    <section id="shoes">内容二</section>
    <section id="clothes">内容三</section>
    
    const navigation = document.querySelector('#navigation')
    navigation.addEventListener('click', event => {
      const anchor = event.target.dataset.anchor
      // 可能不会完全滚动到顶端或底端,这取决于其它元素的布局情况
      anchor.scrollIntoView({ behavior: 'smooth', block: 'end' })
    })
    

    总结:弥补了 a 标签锚点定位的不足,既可以应用到任何元素,也不影响页面路径

  • 滚动到指定元素区域

    const smoothScroll = selector => {
      document
        .querySelector(selector.startsWith('#') ? selector : `#${selector}`)
        .scrollIntoView({ behavior: 'smooth' })
    }
    
  • 重复地滚动某个距离(距离叠加)

    window.scrollBy({ top: window.innerHeight, behavior: 'smooth' })
    

返回顶部(JS版本)

  • HTML基本结构

    <div class="backtop">TOP</div>
    
  • CSS布局技巧

    .backtop {
      position: fixed;
      bottom: 30px; right: 25px;
      background-color: rgba(0, 0, 0, .45);
      opacity: 0; visibility: hidden;
      transition: all .25s;
    }
    .backtop:hover {
      opacity: .8;
      cursor: pointer;
    }
    .backtop.fade {
      opacity: 1;
      visibility: visible;
    }
    

    opacityvisibility 结合使用实现淡入淡出效果

  • 事件绑定逻辑

    const el = document.querySelector('.backtop')
    el.addEventListener('click', () => {
      window.scroll({ top: 0, behavior: 'smooth' })
    })
    window.addEventListener('scroll', () => {
      const { scrollTop } = document.documentElement || document.body
      if (scrollTop > 200 && !el.classList.contains('fade')) {
        el.classList.add('fade')
      }
      if (scrollTop < 200 && el.classList.contains('fade')) {
        el.classList.remove('fade')
      }
    })
    

    注意:由于 scroll 事件高频触发,在性能上会有一定的影响

  • 使用防抖优化 scroll 事件

    const debounce = (func, wait = 350) => {
      let timer = null
      return () => {
        timer && clearTimeout(timer)
        timer = setTimeout(() => { func.apply(this, null) }, wait)
      }
    }
    

    无论调用多少次,都在操作 350ms 后执行最后一次,以此来降低执行频率且不影响效果

返回顶部(CSS版本)

  • 平滑滚动到顶部

     <a href="#" class="backtop"></a>
    
    html {
      scroll-behavior: smooth;
    }
    

    使用锚点定位和根元素的滚动行为属性实现,上边我们提到这种方式会污染页面路径

  • 返回图标的位置

    .backtop {
      position: sticky;
      top: -115px; right: 15px;
      float: right;
      width: 50px; height: 50px;
      border-radius: 50%;
      transform: translateY(calc(100vh + 50px));
      background: url("data:image/svg+xml,%3Csvg class='icon' viewBox='0 0 1812 1024' xmlns='http://www.w3.org/2000/svg' width='353.906' height='200'%3E%3Cpath d='M889.849 273.684L194.723 968.809c-44.462 44.462-116.654 44.455-161.186-.076-44.841-44.842-44.565-116.697-.076-161.186L807.674 33.334c44.461-44.462 116.654-44.455 161.185.077a117.133 117.133 0 0 1 5.942 6.4 114.147 114.147 0 0 1 19.08 15.38l774.212 774.212c44.49 44.49 44.766 116.344-.076 161.186-44.532 44.532-116.724 44.539-161.186.077L889.849 273.684z' fill='%23fff'/%3E%3C/svg%3E") center no-repeat #1e90ff;
      background-size: 50%;
    }
    

本期总结

经过以上分析,我们可以把两种实现方式结合一下,点击返回平滑滚动使用 JS 来实现,图标的位置使用 CSS 来实现,这样既不污染页面路径又避免了高频的 scroll 事件

通过 smooth scroll behavior polyfill 解决平滑滚动的兼容性问题

一起学习,加群交流看 沸点