背景
未超过一屏时,底部按钮跟随内容
超过一屏时,底部按钮固定在底部
原理
我们需要同时监听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 }; 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; }