import React, { useEffect, useState } from 'react'; import classNames from 'classnames'; import raf from 'raf'; import { Container } from '../_utils/dom'; import { BaseProps } from '../_utils/props'; export interface BackTopProps extends BaseProps { target: () => Container; onClick: (e: React.MouseEvent) => void; visibilityHeight: number; children?: React.ReactNode; } const easeInOutCubic = (t: number, b: number, c: number, d: number): number => { const cc = c - b; t /= d / 2; if (t < 1) { return (cc / 2) * t * t * t + b; } else { return (cc / 2) * ((t -= 2) * t * t + 2) + b; } }; const BackTop = (props: BackTopProps) => { const { prefixCls = 'ty-backtop', visibilityHeight = 300, target = () => window, onClick, className, style, children, } = props; const cls = classNames(prefixCls, className); const [visible, setVisible] = useState(true); const getDistanceFromTop = (): number => { const targetNode = target(); if (targetNode === window) { return window.pageYOffset || document.body.scrollTop || document.documentElement.scrollTop; } return (targetNode as HTMLElement).scrollTop; }; const setScrollToTop = (distance: number): void => { const targetNode = target(); if (targetNode === window) { document.body.scrollTop = distance; document.documentElement.scrollTop = distance; } else { (targetNode as HTMLElement).scrollTop = distance; } }; const scrollToTop = (e: React.MouseEvent) => { const scrollTop = getDistanceFromTop(); const startTime = Date.now(); const step = () => { const timestamp = Date.now(); const time = timestamp - startTime; setScrollToTop(easeInOutCubic(time, scrollTop, 0, 450)); if (time < 450) { raf(step); } else { setScrollToTop(0); } }; raf(step); onClick && onClick(e); }; const onScroll = (): void => { if (getDistanceFromTop() > visibilityHeight) { !visible && setVisible(true); } else if (visible) { setVisible(false); } }; useEffect(() => { const targetNode = target(); targetNode.addEventListener('scroll', onScroll); onScroll(); return () => { targetNode.removeEventListener('scroll', onScroll); }; }, []); if (visible) { return (
{children || ( )}
); } return null; }; export default BackTop;