代码改变世界

浮动底部按钮的实现,跟随内容移动与固定

2023-06-16 16:08  前端小白的江湖路  阅读(562)  评论(0编辑  收藏  举报

背景

未超过一屏时,底部按钮跟随内容

超过一屏时,底部按钮固定在底部

 

原理

我们需要同时监听DOM变动函数以及窗口大小变化,大于一屏时,添加底部按钮fixed布局

小于一屏时移除

实现

import React, { useState, useRef, useEffect, useCallback } from "react";
import throttle from "lodash/throttle";
import cx from "classnames";
import "./index.scss";

interface IProps {
  content: React.ReactNode;
  footer: React.ReactNode;
  containerClassName?: string;
  contentClassName?: string;
  footerClassName?: string;
  throttleTime?: number;
}

const FloatFooter: React.FC<IProps> = (props) => {
  const {
    content,
    footer,
    containerClassName,
    contentClassName,
    footerClassName,
    throttleTime = 500,
  } = props;

  const containerRef = useRef<HTMLDivElement>(null);
  const contentRef = useRef<HTMLDivElement>(null);
  const footerRef = useRef<HTMLDivElement>(null);
  const [fixed, setFixed] = useState<boolean>(false);
  const [footerHeight, setFooterHeight] = useState<number>(0);

  const handleHeightChange = useCallback(
    throttle(
      () => {
        const contentElement = contentRef.current;
        const footerElement = footerRef.current;
        if (!contentElement || !footerElement) return;
        const contentElementHeight = contentElement.clientHeight;
        const viewportHeight = document.documentElement.clientHeight;
        if (contentElementHeight > viewportHeight) {
          setFixed(true);
          setFooterHeight(footerElement.clientHeight);
        } else {
          setFixed(false);
          setFooterHeight(0);
        }
      },
      throttleTime,
      { trailing: false }
    ),
    []
  );

  useEffect(() => {
    handleHeightChange();
    window.addEventListener("resize", handleHeightChange);

    const observer = new MutationObserver(handleHeightChange);
    const observerConfig = { childList: true, subtree: true, attributes: true, characterData: true};
    if (containerRef.current) {
      observer.observe(containerRef.current, observerConfig);
    }

    return () => {
      window.removeEventListener("resize", handleHeightChange);
      observer.disconnect();
    };
  }, []);

  return (
    <article
      className={cx("i-container", containerClassName)}
      ref={containerRef}
    >
      <section className={cx("i-content", contentClassName)} ref={contentRef}>
        {content}
        {fixed && <div style={{ height: `${footerHeight}px` }}></div>}
      </section>

      <footer
        className={cx("i-footer", footerClassName, { "i-fixed-footer": fixed })}
        ref={footerRef}
      >
        {footer}
      </footer>
    </article>
  );
};

export default FloatFooter;
.i-fixed-footer {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
}

 

PS: 常用的还有另外一种固定底部的布局

未超过一屏固定在底部,超过一屏跟随内容,附在底部。

这个多用在网站的首页,底部为通用的信息展示。

可以这样实现:

最重要的是给一个min-height 属性

1.如果使用flex,就上下布局,分散2头。

2.如果使用absolute布局,可以直接固定在底部

import React from "react";
import "./index.scss";

const FixedFooter = () => {
  const list = new Array(10).fill(1).map((item, index) => index);
  // const list = new Array(10).fill(1).map((item, index) => index);
  return (
    <article className="main">
      <section className="content">
        {list.map((item) => (
          <div key={item}>{item}</div>
        ))}
      </section>
      <footer className="footer">I am footer</footer>
    </article>
  );
};

export default FixedFooter;
.main {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  min-height: 100vh;
  .content {
  }
  .footer {
    background-color: black;
    color: white;
  }
}

// .main {
//   position: relative;
//   min-height: 100vh;
//   .content {
//     padding-bottom: 30px;
//   }
//   .footer {
//     position: absolute;
//     bottom: 0;
//     width: 100%;
//     height: 30px;
//     background-color: black;
//     color: white;
//   }
// }