前言: 从最开始使用掘金之后,就对掘金 PC 端和移动端的 Tab 栏的实现感到好奇。自己对于页面滚动时要做出交互效果一直是我薄弱的地方。但是目前公司的项目核心都是以移动端为主,如果不掌握一些这部分知识,就无法做出一些令人舒服的交互逻辑。遂今天特意抽时间去学习了一下相关原理,这种效果的实现思路有很多,我只讲解我学习到的一种方式。
我们先预览最终效果图:
一. 分析需求
- 我们以 PC 端为例来分析接下来要讲的内容。
- 可以清楚的看到,首页顶部 Tab 随着我们的滚动开始进行自动隐藏。
- 这是起始状态,页面未发生滚动,顶部只存在一个 Tab。
- 这是中间状态,在某一个时刻同时存在两个 Tab 。
- 这是最终状态,随着页面滚动到某一个临界值,最后只留下了头像 Tab。
二. 准备工作
- 我们先创建一个简单的可以滚动的场景。(你也可以直接在源码标题直接复制我的代码,前提是你也在使用 UnoCSS,如果你不清楚 UnoCSS 的使用方法,那你可以点击下面这篇文章。)
🫱 🎁手把手教你创建自己的代码仓库) - 下面是我们目前的状态,没有做任何关于滚动的响应式。
- 我相信大部分读者非常清楚的知道,当我们页面发生滚动的时候,滚动的元素会触发一个
onScroll
事件,这个事件也是经常被拿来当防抖和截流的例子,但是今天我们不考虑防抖和截流,我们先用用这个事件是如何触发的。 - 你需要在你设置了
overflow-auto
的元素身上绑定一个回调函数,当页面发生滚动的时候,该函数就会被正确的触发,这也是我们本文的重点。
可以看到,当我滚动页面的时候,这个函数正在被触发,并且触发频率非常快。
三. scrollTop
- 这里你要知道,当滚动事件触发的时候,该回调函数会被传递一个事件参数。
- 而 e 的
target
属性指的就是正在发生滚动的这个元素。 - 它身上有一个十分重要的属性,就是我们今天的主角 scrollTop。我们直接打印一下,看一下这个属性是如何变化的。
- 聪明的你一定很快就能发现,它其实就代表着我们实际上滚动的距离。说的更确切一点,它代表着我们滚动元素距离页面顶部的距离。
- 既然我们已经知道了页面实际滚动的距离,我们还知道这两个 Tab 的具体高度,那么我们就可以在合适的时机去操作这两个元素的属性,让它随之我们的页面来做一些响应式的变化。
- 我们先给这两个 Tab 取两个名字,并且打上对于的
ref
,因为接下来我们要频繁的去操作这两个元素的样式。
四. 实现页面滚动响应式
- 回顾第一个状态,页面没有发生滚动,我们的
avatar
标签是没有展示的。 - 我们这里重新设计一下样式,切换我们的
search
为sticky
,并且设置top-0
,使它一开始可以固定在我们的页面顶部。然后再加一点点和avatar
的间距,待会我们马上就要通过滚动实现一些效果。
相对应的页面效果如下: - 这里其实我们要用到一个障眼法,我们需要给
search
栏设置一个兄弟元素,并且这个兄弟元素的属性恰好和我们的avatar
栏是一模一样的。(指的是样式和功能一样)所以你现在的页面应该是下面这个样子。 - 首先明确的要知道,头像栏1 在最开始的没滚动到一临界值的时候是不会展示出来的。这里我们之间给它设置一个
opacity-0
,让它的透明度是0。 - 接下来干的事情就非常简单了,我们这里再看一下掘金的效果。
这里我们看到,在同一时间会同时存在两个框框,那么我们滚动的最小值即可算出来。
根据上图我们可以知道,当我们滚动200px(margin-top)+ 70px
的时候,恰好search
和头像栏
的顶部接触。 - 那么此时我们不做任何限制,继续往下
滚动头像栏2
的高度70px
。那么页面此时恰好会把头像栏2
遮挡住对吧?为了方便展示,我把scrollTop
的值贴上去了。(这里有些许误差,是我滚轮并不是特别容易滚动到那个数值。)
五. 实现顶部过渡切换
- 这里我们需要加一个变量
isShow
,并且我们把头像栏2的透明度样式设置:style
的形式使之可以动态的切换。
此时的页面效果应该是这样: - 可以看到当我们页面滚动到完全看不到头像栏2的时候,我们只需要让头像栏1的透明度变为1即可实现类似于欺骗用户眼睛的感觉,好像头像栏2又贴着回来了。
- 此时还差最后的一步,
search
栏上移,只展示头像栏1。 - 那么接下来要做的事情非常非常简单,就是让搜索栏在此时往上移动自身高度即可。
效果如下: - 嗯...效果有些许生硬,我们加一个简单的过渡,让
search
消失的不那么生硬。
最终效果如下:
六. 源码
<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
属性的使用。掌握好这个属性的使用方法,可以让你在判断用户滚动到某些高度的时候做的交互更加得心应手。
如果你对 offsetWidth
,scrollWidth
和 cilentWidth
这几个属性不太了解,那么我强烈建议你看看下面这篇文章,带你深入浅出了解这几个高度的概念,他们对应的 height
属性你也自然而然可以推导出,会让你在移动端的开发中思路异常清晰。
🫱 你必须知道的 clientWidth, offsetWidth, scrollWidth.🎁
最近在实现一个 window
的全套 UI
,代码开源到了 github
。我会在之后一直更新类似的内容,包括拖拽的实现。
**如果感觉本文对你有帮助,不妨点个赞~
赠人玫瑰,手有余香🌹**
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。