头图

前言: 从最开始使用掘金之后,就对掘金 PC 端和移动端Tab 栏的实现感到好奇。自己对于页面滚动时要做出交互效果一直是我薄弱的地方。但是目前公司的项目核心都是以移动端为主,如果不掌握一些这部分知识,就无法做出一些令人舒服的交互逻辑。遂今天特意抽时间去学习了一下相关原理,这种效果的实现思路有很多,我只讲解我学习到的一种方式。


我们先预览最终效果图:
6.gif

一. 分析需求

  1. 我们以 PC 端为例来分析接下来要讲的内容。

    3.gif
  2. 可以清楚的看到,首页顶部 Tab 随着我们的滚动开始进行自动隐藏。

    image.png
  3. 这是起始状态,页面未发生滚动,顶部只存在一个 Tab
    image.png
  4. 这是中间状态,在某一个时刻同时存在两个 Tab

    image.png
  5. 这是最终状态,随着页面滚动到某一个临界值,最后只留下了头像 Tab

    image.png

二. 准备工作

  1. 我们先创建一个简单的可以滚动的场景。(你也可以直接在源码标题直接复制我的代码,前提是你也在使用 UnoCSS,如果你不清楚 UnoCSS 的使用方法,那你可以点击下面这篇文章。)

    🫱 🎁手把手教你创建自己的代码仓库
    image.png
  2. 下面是我们目前的状态,没有做任何关于滚动的响应式。

    4.gif
  3. 我相信大部分读者非常清楚的知道,当我们页面发生滚动的时候,滚动的元素会触发一个 onScroll 事件,这个事件也是经常被拿来当防抖和截流的例子,但是今天我们不考虑防抖和截流,我们先用用这个事件是如何触发的。
  4. 你需要在你设置了 overflow-auto 的元素身上绑定一个回调函数,当页面发生滚动的时候,该函数就会被正确的触发,这也是我们本文的重点。

    image.png

    可以看到,当我滚动页面的时候,这个函数正在被触发,并且触发频率非常快。

    image.png

三. scrollTop

  1. 这里你要知道,当滚动事件触发的时候,该回调函数会被传递一个事件参数。

    image.png
  2. etarget 属性指的就是正在发生滚动的这个元素。

    image.png
  3. 它身上有一个十分重要的属性,就是我们今天的主角 scrollTop。我们直接打印一下,看一下这个属性是如何变化的。

    image.png
  4. 聪明的你一定很快就能发现,它其实就代表着我们实际上滚动的距离。说的更确切一点,它代表着我们滚动元素距离页面顶部的距离

    4.gif
  5. 既然我们已经知道了页面实际滚动的距离,我们还知道这两个 Tab 的具体高度,那么我们就可以在合适的时机去操作这两个元素的属性,让它随之我们的页面来做一些响应式的变化。
  6. 我们先给这两个 Tab 取两个名字,并且打上对于的 ref,因为接下来我们要频繁的去操作这两个元素的样式。

    image.png

四. 实现页面滚动响应式

  1. 回顾第一个状态,页面没有发生滚动,我们的 avatar 标签是没有展示的。

    image.png
  2. 我们这里重新设计一下样式,切换我们的 searchsticky,并且设置 top-0,使它一开始可以固定在我们的页面顶部。然后再加一点点和 avatar 的间距,待会我们马上就要通过滚动实现一些效果。

    image.png

    相对应的页面效果如下:

    1.gif
  3. 这里其实我们要用到一个障眼法,我们需要给 search 栏设置一个兄弟元素,并且这个兄弟元素的属性恰好和我们的 avatar 栏是一模一样的。(指的是样式和功能一样)所以你现在的页面应该是下面这个样子。

    image.png
  4. 首先明确的要知道,头像栏1 在最开始的没滚动到一临界值的时候是不会展示出来的。这里我们之间给它设置一个 opacity-0,让它的透明度是0。

    image.png
  5. 接下来干的事情就非常简单了,我们这里再看一下掘金的效果。

    image.png

    这里我们看到,在同一时间会同时存在两个框框,那么我们滚动的最小值即可算出来。

    image.png

    根据上图我们可以知道,当我们滚动 200px(margin-top)+ 70px 的时候,恰好 search头像栏的顶部接触。

    image.png
  6. 那么此时我们不做任何限制,继续往下滚动头像栏2 的高度70px。那么页面此时恰好会把头像栏2遮挡住对吧?为了方便展示,我把 scrollTop 的值贴上去了。(这里有些许误差,是我滚轮并不是特别容易滚动到那个数值。)

    2.gif

五. 实现顶部过渡切换

  1. 这里我们需要加一个变量 isShow,并且我们把头像栏2的透明度样式设置:style 的形式使之可以动态的切换。

    image.png

    image.png

    此时的页面效果应该是这样:

    3.gif
  2. 可以看到当我们页面滚动到完全看不到头像栏2的时候,我们只需要让头像栏1的透明度变为1即可实现类似于欺骗用户眼睛的感觉,好像头像栏2又贴着回来了。
  3. 此时还差最后的一步,search 栏上移,只展示头像栏1

    image.png

  4. 那么接下来要做的事情非常非常简单,就是让搜索栏在此时往上移动自身高度即可。

    image.png

    效果如下:

    4.gif
  5. 嗯...效果有些许生硬,我们加一个简单的过渡,让 search 消失的不那么生硬。

    image.png

    最终效果如下:

    5.gif

六. 源码

<script setup lang="ts">
import { ref } from "vue";

const scrollTop = ref<number>(0);
const isShow = ref<boolean>(false);

function scroll(e: any) {
  scrollTop.value = e.target.scrollTop;
  if (scrollTop.value > 280) isShow.value = true;
  else isShow.value = false;
}
</script>

<template>
  <div
    @scroll="scroll"
    class="w-[390px] relative h-[500px] flex itmes-center bg-blue m-x-auto overflow-scroll"
  >
    <div class="fixed z-[999999] top-0 right-390px text- text-[20px]">
      {{ scrollTop }}
    </div>

    <div class="relative w-full h-5000px flex flex-col">
      <div
        class="sticky w-full"
        :style="[
          isShow ? { top: '-50px' } : { top: 0 },
          { transition: `all 1s` },
        ]"
      >
        <div ref="search" class="h-50px bg-red leading-50px">
          <span>搜索栏 height:50px</span>
        </div>
        <div
          class="h-70px bg-black leading-70px"
          :style="isShow ? { opacity: 1 } : { opacity: 0 }"
        >
          <span class="text-white">头像栏1 height:70px</span>
        </div>
      </div>

      <div ref="avatar" class="mt-200px w-full h-70px bg-black leading-70px">
        <span class="text-white">头像栏2 height:70px</span>
      </div>
    </div>
  </div>
</template>

<style></style>

总结

移动端和这个的实现方式大同小异,这里面还有很多类似的方法,但其实核心知识点就是 scrollTop 属性的使用。掌握好这个属性的使用方法,可以让你在判断用户滚动到某些高度的时候做的交互更加得心应手。

如果你对 offsetWidthscrollWidthcilentWidth 这几个属性不太了解,那么我强烈建议你看看下面这篇文章,带你深入浅出了解这几个高度的概念,他们对应的 height 属性你也自然而然可以推导出,会让你在移动端的开发中思路异常清晰。

🫱 你必须知道的 clientWidth, offsetWidth, scrollWidth.🎁

最近在实现一个 window 的全套 UI ,代码开源到了 github。我会在之后一直更新类似的内容,包括拖拽的实现。
image.png

**如果感觉本文对你有帮助,不妨点个赞~

赠人玫瑰,手有余香🌹**


FFF方
451 声望12 粉丝