翼度科技»论坛 编程开发 JavaScript 查看内容

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

5

主题

5

帖子

15

积分

新手上路

Rank: 1

积分
15
背景

现在手机应用经常有这样一个场景:
页面上有一个导航,导航位置在页面中间位置,当页面顶部滚动到导航位置时,导航自动吸顶,页面继续往下滚动时,它就一直在页面视窗顶部显示,当往上滚动时,经过最初位置时,导航自动复原,不再吸顶。
效果就如京东超市首页的导航栏一样:

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

  • 需要可以设置是
    1. 吸顶
    复制代码
    还是
    1. 吸底
    复制代码

    1. 吸顶
    复制代码
    可以设置距离视窗顶部的位置,
    1. 吸顶
    复制代码
    可以设置距离视窗底部的位置。
  • 可以对正常组件都生效,不影响组件自身的样式。

实现

组件主要是为了
  1. 吸顶
复制代码
或者
  1. 吸底
复制代码
功能,那么就命名为
  1. AutoFixed
复制代码

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

获取自身位置一般可以用
  1. 滚动的位置
复制代码
  1. 自身距离页面顶部
复制代码
的位置来判断,但实现起来会麻烦一些,
  1. IntersectionObserver
复制代码
也很好用,而且性能会更好,因此这里将直接使用
  1. IntersectionObserver
复制代码
来处理。
下面,我们先实现一个基于
  1. IntersectionObserver
复制代码
实现的判断位置的
  1. hook
复制代码

定义 props 类型:
  1. import { RefObject } from "react";
  2. type Props = {
  3.   el: React.RefObject<Element>;
  4.   options?: IntersectionObserverInit;
  5. };
复制代码
可接受参数:
  1. el
复制代码
: React 的
  1. ref
复制代码
实例,被判断判断位置的 DOM 元素。
  1. options
复制代码
: IntersectionObserver 构造函数的初始化参数。
具体实现:
  1. import React, { useEffect, useState } from "react";
  2. export function useIntersection(props: Props): boolean {
  3.   const { el, options } = props;
  4.   // 是否到了指定位置区域
  5.   const [intersection, setIntersection] = useState(true);
  6.   useEffect(() => {
  7.     if (!el.current) return;
  8.     // 初始化 IntersectionObserver 实例
  9.     const intersectionObserver = new IntersectionObserver(
  10.       function (entries) {
  11.         setIntersection(entries[0].intersectionRatio === 1);
  12.       },
  13.       { ...options, threshold: [1] }
  14.     );
  15.     // 开始监听
  16.     intersectionObserver.observe(el.current);
  17.     return (): void => {
  18.       // 销毁
  19.       intersectionObserver.disconnect();
  20.     };
  21.   }, [el.current]);
  22.   return intersection;
  23. }
复制代码
现在实现了一个可以根据传入的参数来控制否到了指定位置区域的 hook :
  1. useIntersection
复制代码
  1. useIntersection
复制代码
只是对
  1. IntersectionObserver
复制代码
的简单封装,并没有复杂实现,具体作用就是用于判断某个元素是否进入了
  1. 可视窗口
复制代码
,想了解更多可以点击去查看它的MDN文档
下面再来实现我们要实现的具备吸顶和吸底功能的组件:
  1. AutoFixed
复制代码

参数定义:
  1. export type AutoFixedProps = React.ImgHTMLAttributes<HTMLDivElement> & {
  2.   /** 吸顶距离 */
  3.   top?: string;
  4.   /** 吸底距离 */
  5.   bottom?: string;
  6.   /** 是否一直吸顶或者吸底 */
  7.   alwaysFixed?: boolean;
  8.   zIndex?: number;
  9.   children: React.ReactNode;
  10.   /** 元素框高度 */
  11.   height: number | string;
  12.   /** 相对的目标元素,因为是用的 fixed 定位,记得做相应处理。 */
  13.   root?: Element | Document | null;
  14.   /** 固定的时候才有的className */
  15.   fixedClassName?: string;
  16.   /** 固定的时候才有的样式 */
  17.   fixedStyle?: React.CSSProperties;
  18.   /** fixed状态改变时调用 */
  19.   onFixedChange?: (isFixed: boolean) => void;
  20. };
复制代码
可接受参数 基于
  1. React.HtmlHTMLAttributes<HTMLDivElement>
复制代码
,也就是继承了
  1. div
复制代码
的默认属性。
其他自定义参数说明:

    1. top
    复制代码
    吸顶距离,
    1. 元素顶部
    复制代码
    距离
    1. 视窗顶部
    复制代码
    小于等于
    1. top
    复制代码
    时,进行吸顶。
    1. bottom
    复制代码
    吸底部距离,
    1. 元素底部
    复制代码
    距离
    1. 视窗底部
    复制代码
    大于等于
    1. bottom
    复制代码
    时,进行吸底。注意逻辑是和
    1. 吸顶
    复制代码
    相反。
    1. alwaysFixed
    复制代码
    ,用于支持默认就要一直吸顶或者吸底的情况,需要配合
    1. top
    复制代码
    1. bottom
    复制代码
    来使用。
    1. zIndex
    复制代码
    控制吸顶或者吸底时的样式层级。
    1. children
    复制代码
    1. children
    复制代码
    元素是正常的 React 组件即可。
    1. height
    复制代码
    被包裹元素的高度.也就是
    1. children
    复制代码
    元素 的高度。
    1. root
    复制代码
    ,相对视窗的目标元素,也就是可以控制在某个区域内进行吸顶和吸底,但因为这里是用的
    1. fixed
    复制代码
    定位,如果需要设置
    1. root
    复制代码
    时,需要改变成
    1. absolute
    复制代码
    定位。
    1. fixedClassName
    复制代码
    吸顶和吸底的时候需要动态添加的
    1. className
    复制代码

    1. fixedStyle
    复制代码
    吸顶和吸底的时候需要动态添加的
    1. 样式
    复制代码

    1. onFixedChange
    复制代码
    吸顶和吸底的时候告诉外界。
具体实现:
  1. import React, { useRef, useEffect } from "react";
  2. import { useIntersection } from "../../components/hooks/use-intersection";
  3. export const AutoFixed = (props: AutoFixedProps) => {
  4.   const {
  5.     alwaysFixed,
  6.     top,
  7.     bottom,
  8.     style,
  9.     height,
  10.     root,
  11.     zIndex = 100,
  12.     children,
  13.     className,
  14.     fixedClassName,
  15.     fixedStyle,
  16.     onFixedChange,
  17.     ...rest
  18.   } = props;
  19.   // `bottom` 值存在时,表面要悬浮底部
  20.   const isFiexdTop = !bottom;
  21.   const wrapperRef = useRef<HTMLDivElement>(null);
  22.   // 设置监听参数控制:top 为吸顶距离,bottom 为吸底距离
  23.   const options = {
  24.     rootMargin: isFiexdTop
  25.       ? `-${top || "0px"} 0px 1000000px 0px`
  26.       : `0px 0px -${bottom || "0px"} 0px`,
  27.     // 设置root
  28.     root,
  29.   } as IntersectionObserverInit;
  30.   // 是否悬浮
  31.   const intersection = useIntersection({ el: wrapperRef, options });
  32.   const shouldFixed = alwaysFixed ? true : !intersection;
  33.   useEffect(() => {
  34.     // 通知外部
  35.     onFixedChange?.(shouldFixed);
  36.   }, [shouldFixed, onFixedChange]);
  37.   return (
  38.     <div
  39.       style={{ ...style, height }}
  40.       {...rest}
  41.       className={`${className}${shouldFixed ? " fixedClassName" : ""}`}
  42.       ref={wrapperRef}
  43.     >
  44.       <div
  45.         style={{
  46.           height,
  47.           position: shouldFixed ? "fixed" : "initial",
  48.           top: isFiexdTop ? top || 0 : undefined,
  49.           bottom: isFiexdTop ? undefined : bottom || 0,
  50.           zIndex: zIndex,
  51.           ...(shouldFixed ? fixedStyle : {}),
  52.         }}
  53.       >
  54.         {children}
  55.       </div>
  56.     </div>
  57.   );
  58. };
复制代码
实现逻辑:

  • 使用了
    1. alwaysFixed
    复制代码
    判断是否一直悬浮。
  • 默认悬浮顶部,
    1. bottom
    复制代码
    值存在时,表明要悬浮底部。
    1. useIntersection
    复制代码
    传入监听位置控制参数。
  • 根据
    1. useIntersection
    复制代码
    的结果来判断是否应该
    1. 吸顶
    复制代码
    1. 吸底
    复制代码

  • 做了
    1. style
    复制代码
    样式和
    1. className
    复制代码
    传入处理的问题,以及 zIndex 层级问题。
  • 吸顶时,不进行设置
    1. bottom
    复制代码
    ,吸底时,不进行设置
    1. bottom
    复制代码

主要核心逻辑是第
  1. 3
复制代码
点:
  1. const options = {
  2.     rootMargin: `-${top || "0px"} 0px -${bottom || "0px"} 0px`,
  3. };
复制代码
  1. rootMargin
复制代码
中:
  1. -${top || "0px"}
复制代码
为吸顶距离,
  1. -${bottom || "0px"}
复制代码
为吸底距离。一定要是负的,正数表示延伸到了视窗外的距离,负数表示距离视窗顶部或者底部的距离。
使用方式:
  1. <AutoFixed
  2.     // 距离顶部为 20px 吸顶
  3.     top="20px"
  4.     // 占位高度,也就是 children 的高度
  5.     height="20px"
  6.     // fixed状态改变时
  7.     onFixedChange={(isFixed) => {
  8.       console.log(`isFixed: ` + isFixed);
  9.     }}
  10.     // fixed状态需要添加的className
  11.     fixedClassName="hello"
  12.     // fixed状态需要添加的style
  13.     fixedStyle={{ color: "red" }}
  14. >
  15.     <div>
  16.         我是悬浮内容,高度 20px, 距离顶部为 20px 吸顶
  17.     </div>
  18. </AutoFixed>
复制代码
实现效果:

可以看出
  1. 一直吸顶
复制代码
  1. 滚动到设定位置吸顶
复制代码
  1. 一直吸底
复制代码
  1. 滚动到设定位置吸底
复制代码
四个功能都可以正常工作。
  1. 滚动到设定位置吸底
复制代码
指的是,从底部向上滚动的时候,这个功能就是为了在划出屏幕区域的时候显示在底部。
大家也可以打开 示例 自己去体验一下。

结语

这是之前在比较多的页面会用到的一个功能点,然后写了几次后,发现每次实现这个功能都有点复杂,于是封装了
  1. 吸顶
复制代码
组件,本次写文章,就想着刚好可以完善一下,把
  1. 吸底
复制代码
功能也开发出来,因为后续也有用到过不少次。
以上就是React 实现具备吸顶和吸底功能组件实例的详细内容,更多关于React吸顶吸底功能的资料请关注脚本之家其它相关文章!

来源:https://www.jb51.net/article/276256.htm
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

举报 回复 使用道具