一个简单吸顶效果的实现笔记(react)

70 阅读1分钟
 <AutoFixed
        // 距离顶部为 20px 吸顶
        top="0px"
        // 占位高度也就是 children 的高度
        height="56px"
        // fixed状态改变时
        onFixedChange={(isFixed) => {
          setIsFixed(isFixed);
        }}
        // fixed状态需要添加的className
        fixedClassName=""
        // fixed状态需要添加的style
        fixedStyle={{ color: 'red' }}
        root={containerRef?.current}
        >
        <div
          className={styles.tabsBoxWrap}
          style={{
            width: isFixed ? '100vw' : '100%',
          }}>
          <div className={styles.tabsBox}>
            <Tabs
              defaultActiveKey="1"
              tabBarGutter={'96px'}
              tabBarStyle={tabsStyle}
              items={items}
              onChange={(v, e) => onChange(v, e)}
            />
          </div>
        </div>
      </AutoFixed>


  • AutoFixed.js
import React, { useEffect, useRef } from 'react';

import { useIntersection } from './hooks/useIntersection';

const AutoFixed = (props) => {
  const {
    alwaysFixed,
    top,
    bottom,
    style,
    height,
    root,
    zIndex = 100,
    children,
    className,
    fixedClassName,
    fixedStyle,
    onFixedChange,
    ...rest
  } = props;
  // `bottom` 值存在时,表面要悬浮底部
  const isFiexdTop = !bottom;
  const wrapperRef = useRef(null);
  // 设置监听参数控制:top 为吸顶距离,bottom 为吸底距离
  const options = {
    rootMargin: isFiexdTop
      ? `-${top || '0px'} 0px 10000px 0px` // 偏移量  假设上面的全部在视野里面,不在视野里就吸顶
      : `0px 0px -${bottom || '0px'} 0px`,
    // 设置root
    root,
  };
  // 是否悬浮
  const intersection = useIntersection({ el: wrapperRef, options });
  useEffect(() => {
    // 通知外部
    onFixedChange?.(intersection); // 在视野里不需要吸顶,返回false
    console.log('需要吸顶吗', intersection);
  }, [intersection]);

  return (
    <div
      style={{ ...style, height }}
      {...rest}
      className={`${className}${intersection ? ' fixedClassName' : ''}`}
      ref={wrapperRef}>
      <div
        style={{
          height,
          position: intersection ? 'fixed' : 'initial',
          top: isFiexdTop ? top || 0 : undefined,
          bottom: isFiexdTop ? undefined : bottom || 0,
          zIndex: zIndex,
          ...(intersection ? fixedStyle : {}),
        }}>
        {children}
      </div>
    </div>
  );
};
export default AutoFixed;

  • useIntersection
import React, { useEffect, useState } from 'react';

//
export function useIntersection(props) {
  const { el, options } = props;
  // 是否到了指定位置区域
  const [intersection, setIntersection] = useState(true);

  useEffect(() => {
    if (!el.current) return;
    // 初始化 IntersectionObserver 实例
    const intersectionObserver = new IntersectionObserver(
      function (entries) {
        console.log(entries);
        setIntersection(entries[0].intersectionRatio === 1);
      },
      { ...options, threshold: [1] }
    );

    // 开始监听
    intersectionObserver.observe(el.current);

    return () => {
      // 销毁
      intersectionObserver.disconnect();
    };
  }, [el.current]);

  return !intersection; // 在视野里不需要吸顶,返回false
}