diff --git a/components/domains/Emoji/Emojis.tsx b/components/domains/Emoji/Emojis.tsx index 608785c..d9042fc 100644 --- a/components/domains/Emoji/Emojis.tsx +++ b/components/domains/Emoji/Emojis.tsx @@ -1,6 +1,7 @@ import { motion } from "framer-motion"; import { useRecoilValue } from "recoil"; import { Book, Heart, MirrorBall, Note } from "~/assets/svgs"; +import { useLayoutWidth } from "~/components/uis/Layout"; import { emojiAtomState } from "~/store/emoji"; const Emojis = ({ stage }: { stage: 1 | 2 | 3 }) => { @@ -19,9 +20,11 @@ export default Emojis; export const EmojiScaled = ({ stage }: { stage: 1 | 2 | 3 }) => { const emoji = useRecoilValue(emojiAtomState); - const getRandomHalfToFull = () => Math.random() / 4 + 3 / 4; + const getRandom3QuarterToFull = () => Math.random() / 4 + 3 / 4; const randomMinus = Math.random() > 0.5 ? 1 : -1; + const { width } = useLayoutWidth(); + const EmojiSVG = emoji ? emoji.emojiType === "HEART" ? Heart @@ -40,16 +43,16 @@ export const EmojiScaled = ({ stage }: { stage: 1 | 2 | 3 }) => { marginBottom: stage === 3 ? 140 : 1, }} initial={{ - x: (randomMinus * window.innerWidth * Math.random()) / 2, - y: 400 * getRandomHalfToFull(), + x: (randomMinus * width * Math.random()) / 2, + y: 400 * getRandom3QuarterToFull(), }} animate={{ - x: (randomMinus * window.innerWidth * Math.random()) / 2, - y: -2000 * getRandomHalfToFull(), + x: (randomMinus * width * Math.random()) / 2, + y: -2000 * getRandom3QuarterToFull(), opacity: Math.random() * 0.9 + 0.1, }} - transition={{ duration: 3 * getRandomHalfToFull() }} + transition={{ duration: 3 * getRandom3QuarterToFull() }} > diff --git a/components/uis/Layout/index.tsx b/components/uis/Layout/index.tsx index ca706ea..724f466 100644 --- a/components/uis/Layout/index.tsx +++ b/components/uis/Layout/index.tsx @@ -1,45 +1,58 @@ -import React from "react"; -import styled from "@emotion/styled"; +import React, { createContext, useContext } from "react"; +import { css } from "@emotion/react"; +import { useElementSize } from "~/hooks/commons"; + +const Context = createContext({ width: 450 }); +export const useLayoutWidth = () => useContext(Context); interface Props { children: React.ReactNode; screenColor?: string; } -function Layout({ children, screenColor = "#000000" }: Props) { - return ( - - {children} - - ); -} +const Layout = ({ children, screenColor = "#000000" }: Props) => { + const [ref, size] = useElementSize(); -const S = { - Container: styled.div` - height: 100%; - width: 100%; - max-height: 100%; - z-index: 100; - display: flex; - justify-content: center; - `, - - Screen: styled.div<{ screenColor?: string }>` - max-width: 450px; - width: 100%; - height: 100%; - overflow-y: auto; - overflow-x: hidden; - background: ${(p) => p.screenColor}; - color: #fff; - position: relative; - backdrop-filter: blur(10px); + return ( + +
+
+ {children} +
+
+
+ ); }; export default Layout; diff --git a/hooks/commons/index.ts b/hooks/commons/index.ts index d195135..f41d473 100644 --- a/hooks/commons/index.ts +++ b/hooks/commons/index.ts @@ -8,3 +8,5 @@ export { default as useLongPress } from "./useLongPress"; export { default as useDisclosure } from "./useDisclosure"; export { default as useThrottle } from "./useThrottle"; export { default as useIsMobile } from "./useIsMobile"; +export { default as useElementSize } from "./useElementSize"; +export { default as useEventListener } from "./useEventListener"; diff --git a/hooks/commons/useElementSize/index.ts b/hooks/commons/useElementSize/index.ts new file mode 100644 index 0000000..f4acf07 --- /dev/null +++ b/hooks/commons/useElementSize/index.ts @@ -0,0 +1,36 @@ +import { useCallback, useState } from "react"; +import useEventListener from "../useEventListener"; +import useIsomorphicLayoutEffect from "../useIsomorphicLayoutEffect"; + +interface Size { + width: number; + height: number; +} + +function useElementSize(): [ + (node: T | null) => void, + Size +] { + const [ref, setRef] = useState(null); + const [size, setSize] = useState({ + width: 0, + height: 0, + }); + + const handleSize = useCallback(() => { + setSize({ + width: ref?.offsetWidth || 0, + height: ref?.offsetHeight || 0, + }); + }, [ref?.offsetHeight, ref?.offsetWidth]); + + useEventListener("resize", handleSize); + + useIsomorphicLayoutEffect(() => { + handleSize(); + }, [ref?.offsetHeight, ref?.offsetWidth]); + + return [setRef, size]; +} + +export default useElementSize; diff --git a/hooks/commons/useEventListener/index.ts b/hooks/commons/useEventListener/index.ts new file mode 100644 index 0000000..7c590ae --- /dev/null +++ b/hooks/commons/useEventListener/index.ts @@ -0,0 +1,64 @@ +import type { RefObject } from "react"; +import { useEffect, useRef } from "react"; +import useIsomorphicLayoutEffect from "../useIsomorphicLayoutEffect"; + +function useEventListener( + eventName: K, + handler: (event: WindowEventMap[K]) => void, + element?: undefined, + options?: boolean | AddEventListenerOptions +): void; + +function useEventListener< + K extends keyof HTMLElementEventMap, + T extends HTMLElement = HTMLDivElement +>( + eventName: K, + handler: (event: HTMLElementEventMap[K]) => void, + element: RefObject, + options?: boolean | AddEventListenerOptions +): void; + +function useEventListener( + eventName: K, + handler: (event: DocumentEventMap[K]) => void, + element: RefObject, + options?: boolean | AddEventListenerOptions +): void; + +function useEventListener< + KW extends keyof WindowEventMap, + KH extends keyof HTMLElementEventMap, + T extends HTMLElement | void = void +>( + eventName: KW | KH, + handler: ( + event: WindowEventMap[KW] | HTMLElementEventMap[KH] | Event + ) => void, + element?: RefObject, + options?: boolean | AddEventListenerOptions +) { + const savedHandler = useRef(handler); + + useIsomorphicLayoutEffect(() => { + savedHandler.current = handler; + }, [handler]); + + useEffect(() => { + const targetElement: T | Window = element?.current || window; + if (!(targetElement && targetElement.addEventListener)) { + return; + } + + const eventListener: typeof handler = (event) => + savedHandler.current(event); + + targetElement.addEventListener(eventName, eventListener, options); + + return () => { + targetElement.removeEventListener(eventName, eventListener); + }; + }, [eventName, element, options]); +} + +export default useEventListener;