From a4d577ffde146a20afba05611365e637ff7cf650 Mon Sep 17 00:00:00 2001 From: wkylin Date: Mon, 9 Dec 2024 17:40:46 +0800 Subject: [PATCH] feat: get bounding client rect --- package-lock.json | 11 ++++ package.json | 2 + .../container/masonryContainer/index.jsx | 4 +- src/components/hooks/useMasonry/index.tsx | 4 +- src/components/hooks/useRect/index.tsx | 60 +++++++++++++++++++ src/pages/home/index.jsx | 23 ++++--- 6 files changed, 93 insertions(+), 11 deletions(-) create mode 100644 src/components/hooks/useRect/index.tsx diff --git a/package-lock.json b/package-lock.json index 380df966..94c742f0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -104,6 +104,7 @@ "remark-gfm": "^4.0.0", "remark-math": "^6.0.0", "remove-markdown": "^0.5.5", + "resize-observer-polyfill": "^1.5.1", "sanitize.css": "^13.0.0", "screenfull": "^6.0.2", "sse": "github:mpetazzoni/sse.js", @@ -145,6 +146,7 @@ "@types/react": "^18.3.12", "@types/react-dom": "^18.3.1", "@types/react-test-renderer": "^18.3.0", + "@types/resize-observer-browser": "^0.1.11", "@types/three": "^0.170.0", "@types/user-agents": "^1.0.4", "@typescript-eslint/eslint-plugin": "^8.16.0", @@ -10062,6 +10064,13 @@ "@types/react": "*" } }, + "node_modules/@types/resize-observer-browser": { + "version": "0.1.11", + "resolved": "https://registry.npmmirror.com/@types/resize-observer-browser/-/resize-observer-browser-0.1.11.tgz", + "integrity": "sha512-cNw5iH8JkMkb3QkCoe7DaZiawbDQEUX8t7iuQaRTyLOyQCR2h+ibBD4GJt7p5yhUHrlOeL7ZtbxNHeipqNsBzQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/retry": { "version": "0.12.2", "dev": true, @@ -32570,6 +32579,8 @@ }, "node_modules/resize-observer-polyfill": { "version": "1.5.1", + "resolved": "https://registry.npmmirror.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==", "license": "MIT" }, "node_modules/resolve": { diff --git a/package.json b/package.json index 3c84d246..bc3371b3 100644 --- a/package.json +++ b/package.json @@ -111,6 +111,7 @@ "@types/react": "^18.3.12", "@types/react-dom": "^18.3.1", "@types/react-test-renderer": "^18.3.0", + "@types/resize-observer-browser": "^0.1.11", "@types/three": "^0.170.0", "@types/user-agents": "^1.0.4", "@typescript-eslint/eslint-plugin": "^8.16.0", @@ -307,6 +308,7 @@ "remark-gfm": "^4.0.0", "remark-math": "^6.0.0", "remove-markdown": "^0.5.5", + "resize-observer-polyfill": "^1.5.1", "sanitize.css": "^13.0.0", "screenfull": "^6.0.2", "sse": "github:mpetazzoni/sse.js", diff --git a/src/components/container/masonryContainer/index.jsx b/src/components/container/masonryContainer/index.jsx index aad6acad..94383279 100644 --- a/src/components/container/masonryContainer/index.jsx +++ b/src/components/container/masonryContainer/index.jsx @@ -1,6 +1,6 @@ import useMasonry from '@hooks/useMasonry' -const Masonry = () => { +const MyMasonry = () => { const masonryContainer = useMasonry() return (
{ ) } -export default Masonry +export default MyMasonry diff --git a/src/components/hooks/useMasonry/index.tsx b/src/components/hooks/useMasonry/index.tsx index 269147d7..6ade020a 100644 --- a/src/components/hooks/useMasonry/index.tsx +++ b/src/components/hooks/useMasonry/index.tsx @@ -1,10 +1,10 @@ -import { useEffect, useState, useRef } from 'react' +import { useEffect, useLayoutEffect, useState, useRef } from 'react' const useMasonry = () => { const masonryContainer = useRef(null) const [items, setItems] = useState([]) - useEffect(() => { + useLayoutEffect(() => { if (masonryContainer.current) { const masonryItem = Array.from(masonryContainer.current.children) setItems(masonryItem) diff --git a/src/components/hooks/useRect/index.tsx b/src/components/hooks/useRect/index.tsx new file mode 100644 index 00000000..33329237 --- /dev/null +++ b/src/components/hooks/useRect/index.tsx @@ -0,0 +1,60 @@ +import { useCallback, useLayoutEffect, useRef, useState } from "react"; + +import ResizeObserver from 'resize-observer-polyfill' + +type RectResult = { + bottom: number; + height: number; + left: number; + right: number; + top: number; + width: number; +} | null; + +const getRect = (element: HTMLElement | null): RectResult | null => { + if (!element) return null; + return element.getBoundingClientRect(); +}; + +const useRect = (): [ + RectResult, + React.MutableRefObject +] => { + const ref = useRef(null); + const current = ref.current || null; + const [rect, setRect] = useState(getRect(current)); + + const handleResize = useCallback(() => { + if (!ref.current) return; + + // Update client rect + setRect(getRect(ref.current)); + }, [ref]); + + useLayoutEffect(() => { + const element = ref.current; + if (!element) return; + + handleResize(); + if (typeof ResizeObserver === "function") { + let resizeObserver: ResizeObserver | null = new ResizeObserver(() => + handleResize() + ); + resizeObserver.observe(element); + + return () => { + if (!resizeObserver) return; + resizeObserver.disconnect(); + resizeObserver = null; + }; + } + // set resize listener + window.addEventListener("resize", handleResize); + // remove resize listener + return () => window.removeEventListener("resize", handleResize); + }, [handleResize]); + + return [rect, ref]; +}; + +export default useRect diff --git a/src/pages/home/index.jsx b/src/pages/home/index.jsx index 47f2f7cc..657b9197 100644 --- a/src/pages/home/index.jsx +++ b/src/pages/home/index.jsx @@ -27,6 +27,7 @@ import AnimateOnScreen from '@stateless/AnimateOnScreen' import AnimateRipple from '@stateless/AnimateRipple' import AnimateWave from '@stateless/AnimateWave' import MeshGradientBackground from '@stateless/MeshGradientBackground' +import useRect from '@hooks/useRect' import TagCloud from '@stateless/TagCloud' // import SlideLinear from '@stateless/SlideLinear' // import Masonry from '@container/masonryContainer' @@ -165,7 +166,9 @@ const Home = () => { }) } - const { scrollDir, scrollPosition } = useDetectScroll({target: document.getElementById('container')}) + // const { scrollDir, scrollPosition } = useDetectScroll({target: document.getElementById('container')}) + + const [barRect, barRef] = useRect() return ( @@ -192,12 +195,12 @@ const Home = () => {
-
+ {/*

Scroll direction: {`${scrollDir}`}

Scroll position - Top: {scrollPosition.top}, Bottom: {scrollPosition.bottom}

-
+
*/}

React Animate On Scroll. @@ -274,11 +277,14 @@ const Home = () => {

-
-
- 我的中国心 +
+
+ dot mask
+
+
RectResult.
+ width: {parseInt(barRect?.width)} height: {parseInt(barRect?.height)} top: {parseInt(barRect?.top)} bottom: {parseInt(barRect?.bottom)} right: {parseInt(barRect?.right)} left: {parseInt(barRect?.left)}
@@ -373,6 +379,10 @@ const Home = () => { + + + +
@@ -389,7 +399,6 @@ const Home = () => {
Vue
-
) }