文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

React 实现具备吸顶和吸底功能组件实例

2023-02-23 14:17

关注

背景

现在手机应用经常有这样一个场景:

页面上有一个导航,导航位置在页面中间位置,当页面顶部滚动到导航位置时,导航自动吸顶,页面继续往下滚动时,它就一直在页面视窗顶部显示,当往上滚动时,经过最初位置时,导航自动复原,不再吸顶。

效果就如京东超市首页的导航栏一样:

下面我们就来具体实现这样一个 React 组件,实现后还会再扩展延伸一下 吸底 功能,因为 吸底 场景也不少。

具体要求:

实现

组件主要是为了 吸顶 或者 吸底 功能,那么就命名为 AutoFixed

主要实现逻辑:需要判断自身在视窗内的位置与设置的 吸顶 或者 吸底 位置是否匹配,匹配上了则可以进行 吸顶 或者 吸底

获取自身位置一般可以用 滚动的位置自身距离页面顶部 的位置来判断,但实现起来会麻烦一些,IntersectionObserver也很好用,而且性能会更好,因此这里将直接使用 IntersectionObserver 来处理。

下面,我们先实现一个基于 IntersectionObserver 实现的判断位置的 hook

定义 props 类型:

import { RefObject } from "react";
type Props = {
  el: React.RefObject<Element>;
  options?: IntersectionObserverInit;
};

可接受参数:

el: React 的 ref 实例,被判断判断位置的 DOM 元素。 options: IntersectionObserver 构造函数的初始化参数。

具体实现:

import React, { useEffect, useState } from "react";
export function useIntersection(props: Props): boolean {
  const { el, options } = props;
  // 是否到了指定位置区域
  const [intersection, setIntersection] = useState(true);
  useEffect(() => {
    if (!el.current) return;
    // 初始化 IntersectionObserver 实例
    const intersectionObserver = new IntersectionObserver(
      function (entries) {
        setIntersection(entries[0].intersectionRatio === 1);
      },
      { ...options, threshold: [1] }
    );
    // 开始监听
    intersectionObserver.observe(el.current);
    return (): void => {
      // 销毁
      intersectionObserver.disconnect();
    };
  }, [el.current]);
  return intersection;
}

现在实现了一个可以根据传入的参数来控制否到了指定位置区域的 hook :useIntersection

useIntersection 只是对 IntersectionObserver 的简单封装,并没有复杂实现,具体作用就是用于判断某个元素是否进入了 可视窗口,想了解更多可以点击去查看它的MDN文档。

下面再来实现我们要实现的具备吸顶和吸底功能的组件:AutoFixed

参数定义:

export type AutoFixedProps = React.ImgHTMLAttributes<HTMLDivElement> & {
  
  top?: string;
  
  bottom?: string;
  
  alwaysFixed?: boolean;
  zIndex?: number;
  children: React.ReactNode;
  
  height: number | string;
  
  root?: Element | Document | null;
  
  fixedClassName?: string;
  
  fixedStyle?: React.CSSProperties;
  
  onFixedChange?: (isFixed: boolean) => void;
};

可接受参数 基于 React.HtmlHTMLAttributes<HTMLDivElement> ,也就是继承了 div 的默认属性。

其他自定义参数说明:

具体实现:

import React, { useRef, useEffect } from "react";
import { useIntersection } from "../../components/hooks/use-intersection";
export const AutoFixed = (props: AutoFixedProps) => {
  const {
    alwaysFixed,
    top,
    bottom,
    style,
    height,
    root,
    zIndex = 100,
    children,
    className,
    fixedClassName,
    fixedStyle,
    onFixedChange,
    ...rest
  } = props;
  // `bottom` 值存在时,表面要悬浮底部
  const isFiexdTop = !bottom;
  const wrapperRef = useRef<HTMLDivElement>(null);
  // 设置监听参数控制:top 为吸顶距离,bottom 为吸底距离
  const options = {
    rootMargin: isFiexdTop
      ? `-${top || "0px"} 0px 1000000px 0px`
      : `0px 0px -${bottom || "0px"} 0px`,
    // 设置root
    root,
  } as IntersectionObserverInit;
  // 是否悬浮
  const intersection = useIntersection({ el: wrapperRef, options });
  const shouldFixed = alwaysFixed ? true : !intersection;
  useEffect(() => {
    // 通知外部
    onFixedChange?.(shouldFixed);
  }, [shouldFixed, onFixedChange]);
  return (
    <div
      style={{ ...style, height }}
      {...rest}
      className={`${className}${shouldFixed ? " fixedClassName" : ""}`}
      ref={wrapperRef}
    >
      <div
        style={{
          height,
          position: shouldFixed ? "fixed" : "initial",
          top: isFiexdTop ? top || 0 : undefined,
          bottom: isFiexdTop ? undefined : bottom || 0,
          zIndex: zIndex,
          ...(shouldFixed ? fixedStyle : {}),
        }}
      >
        {children}
      </div>
    </div>
  );
};

实现逻辑:

主要核心逻辑是第 3 点:

const options = {
    rootMargin: `-${top || "0px"} 0px -${bottom || "0px"} 0px`,
};

rootMargin 中:-${top || "0px"} 为吸顶距离,-${bottom || "0px"} 为吸底距离。一定要是负的,正数表示延伸到了视窗外的距离,负数表示距离视窗顶部或者底部的距离。

使用方式:

<AutoFixed
    // 距离顶部为 20px 吸顶
    top="20px"
    // 占位高度,也就是 children 的高度
    height="20px"
    // fixed状态改变时
    onFixedChange={(isFixed) => {
      console.log(`isFixed: ` + isFixed);
    }}
    // fixed状态需要添加的className
    fixedClassName="hello"
    // fixed状态需要添加的style
    fixedStyle={{ color: "red" }}
>
    <div>
        我是悬浮内容,高度 20px, 距离顶部为 20px 吸顶
    </div>
</AutoFixed>

实现效果:

可以看出 一直吸顶滚动到设定位置吸顶一直吸底滚动到设定位置吸底 四个功能都可以正常工作。

滚动到设定位置吸底 指的是,从底部向上滚动的时候,这个功能就是为了在划出屏幕区域的时候显示在底部。

大家也可以打开 示例 自己去体验一下。

结语

这是之前在比较多的页面会用到的一个功能点,然后写了几次后,发现每次实现这个功能都有点复杂,于是封装了 吸顶 组件,本次写文章,就想着刚好可以完善一下,把 吸底 功能也开发出来,因为后续也有用到过不少次。

以上就是React 实现具备吸顶和吸底功能组件实例的详细内容,更多关于React吸顶吸底功能的资料请关注编程网其它相关文章!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-前端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯