构建动态交互式H5导航栏:滑动高亮、吸顶和锚点导航技巧详解

功能描述

产品要求在h5页面实现集锚点、吸顶及滑动高亮为一体的功能,如下图展示的一样。当页面滑动时,内容区域对应的选项卡高亮。当点击选项卡时,内容区域自动滑动到选项卡正下方。

布局设计

css 布局

为了更清晰的描述各功能实现的方式,将页面布局进行了如下的拆分。

★ 最外层的元素定义为 contentWrap,是使用 Intersection 定义的观察根元素。 ★ 所有可纵向滑动的元素包裹在 vertScrollWrap 中,也是粘性定位需要找到的父元素。 ★ 横向可滑动的导航栏是 horiScrollWrap ,实现吸顶功能需要设置粘性定位。 ★ observerWrap 用来包裹可观察的元素,observerItem 用来形容每一个可观察的子元素。

数据结构

导航栏的数据结构为数组,里面包括了选项卡需要显示的文案,对应的值,以及唯一值 key 。

const list = {

label: "选项卡一",

value: "1",

key: "1",

height: 150, // 模拟使用,真实场景并不需要,数据会自动将盒子撑开

}]

在我们真实的业务场景中,导航栏的标题来源于后端接口,内容区域也需要根据标题类型结合数据展示不同的内容,在获取接口数据后,我会为每一条数据增加一个随机的 key(非索引值,不会重复的8位哈希值) ,在选项卡和内容区域增加自定义属性,如 data-tab-item-id,这样可以精准的获取到所需要的 dom 元素。

选项卡吸顶

按照这个场景,首先把选项卡横向滚动及吸顶的功能实现。这里代码语法很简单,通过 position: sticky 就能实现,但需要注意的是,这里的 dom 元素布局很重要,父元素需要包裹滑动时无需展示的中间区域,以及选项卡、及里面的内容区域。

具体代码如下,这样就能实现向上滑动时,选项卡一整行固定在头部区域和内容区域之间。

// 父元素

.vertScrollWrap {

position: relative;

overflow: scroll;

height: calc(100vh - 100px);

}

// 子元素

.horiScrollWrap {

position: sticky

top: 0

}

滑动导航高亮

当手指触摸页面滑动时,我们需要知道当前出现在可视区域的内容区域是哪些,传统方案可以通过绑定 scroll 方法,这里我使用的是 IntersectionObserver,通过观察元素与父元素的交叉状态,注意⚠️ 这个api有一定的浏览器版本要求。

map 保存 dom 结构信息

在页面滑动时,需要知道每个内容区域距离父元素顶部的距离,找出距离顶部最近的元素,才能高亮对应的选项卡。当选项卡点击时,我们希望知道每个内容区域的高度,高度计算后,滚动整体到指定的高度,让选项卡对应的内容元素放在选项卡的最下方。

根据以上逻辑,需要每个内容模块的属性,这里我使用map来保存这些数据,key 为 dom 元素,value 值为对象,其中包含是否与父元素相交、距离顶部元素、元素高度等属性。

// 初始化map

domMap = new Map();

// 设置map属性

setDomMap = (dom, obj) => {

const element = this.domMap.get(dom);

const value = {

key: element?.key,

top: element?.top,

height: element?.height,

index: element?.index,

isIntersecting: element?.isIntersecting,

...obj,

};

this.domMap.set(dom, value);

};

IntersectionObserver 观察相交状态

使用 new IntersectionObserver(callback[, options]) 来定义观察逻辑。

初始化 domMap

在组件挂载时,初始化map数据,遍历所有的内容区域元素。

const prefix = "nav";

const blockId = `${

prefix}-block-id`

奥特曼成人版震撼登场,你不可不知的真相!
LinkedIn领英个人档案打造完美避坑指南