|
1 |
| -import { useEffect, useRef, type RefObject } from 'react'; |
| 1 | +import { useEffect, useRef, type RefObject } from "react"; |
2 | 2 |
|
3 | 3 | export function useScrollToBottom<T extends HTMLElement>(): [
|
4 | 4 | RefObject<T>,
|
5 | 5 | RefObject<T>,
|
6 | 6 | ] {
|
7 | 7 | const containerRef = useRef<T>(null);
|
8 | 8 | const endRef = useRef<T>(null);
|
| 9 | + const userScrolledRef = useRef(false); |
| 10 | + const autoScrollInProgress = useRef(false); |
9 | 11 |
|
10 | 12 | useEffect(() => {
|
11 | 13 | const container = containerRef.current;
|
12 | 14 | const end = endRef.current;
|
13 | 15 |
|
14 | 16 | if (container && end) {
|
15 | 17 | const observer = new MutationObserver(() => {
|
16 |
| - end.scrollIntoView({ behavior: 'instant', block: 'end' }); |
| 18 | + if (!userScrolledRef.current) { |
| 19 | + autoScrollInProgress.current = true; |
| 20 | + end.scrollIntoView({ behavior: "smooth", block: "end" }); |
| 21 | + |
| 22 | + // 处理滚动结束事件 |
| 23 | + const handleScrollEnd = () => { |
| 24 | + autoScrollInProgress.current = false; |
| 25 | + container.removeEventListener("scrollend", handleScrollEnd); |
| 26 | + }; |
| 27 | + |
| 28 | + if ("onscrollend" in window) { |
| 29 | + container.addEventListener("scrollend", handleScrollEnd); |
| 30 | + } else { |
| 31 | + // 回退:假设滚动动画在1秒内完成 |
| 32 | + setTimeout(() => { |
| 33 | + autoScrollInProgress.current = false; |
| 34 | + }, 1000); |
| 35 | + } |
| 36 | + } |
17 | 37 | });
|
18 | 38 |
|
| 39 | + const handleScroll = () => { |
| 40 | + if (autoScrollInProgress.current) return; |
| 41 | + |
| 42 | + // 检查是否滚动到底部 |
| 43 | + const { scrollTop, scrollHeight, clientHeight } = container; |
| 44 | + const isAtBottom = scrollHeight - (scrollTop + clientHeight) < 1; |
| 45 | + |
| 46 | + userScrolledRef.current = !isAtBottom; |
| 47 | + }; |
| 48 | + |
| 49 | + container.addEventListener("scroll", handleScroll); |
19 | 50 | observer.observe(container, {
|
20 | 51 | childList: true,
|
21 | 52 | subtree: true,
|
22 | 53 | attributes: true,
|
23 | 54 | characterData: true,
|
24 | 55 | });
|
25 | 56 |
|
26 |
| - return () => observer.disconnect(); |
| 57 | + return () => { |
| 58 | + observer.disconnect(); |
| 59 | + container.removeEventListener("scroll", handleScroll); |
| 60 | + }; |
27 | 61 | }
|
28 | 62 | }, []);
|
29 | 63 |
|
|
0 commit comments