TIL

2024.04.17 TIL #์ฑ„ํŒ… #์Šคํฌ๋กค #์Šคํฌ๋กค๋‹ค์šด #์กฐ๊ฑด๋ถ€

inz1234 2024. 5. 8. 13:03

๐Ÿ“Œ Task TODOLIST

- [x] ์Šคํฌ๋กคํ•  div ์„ ์ •ํ•˜๊ธฐ

- [x] ref ๋ถ€์—ฌํ•˜๊ธฐ

- [x] onScroll props๋กœ ์Šคํฌ๋กค ํ•จ์ˆ˜ ๋„ฃ๊ธฐ

- [x] ์Šคํฌ๋กค ๋‹ค์šดํ•˜๊ธฐ


โœจ ๊ฐœ๋ฐœ ๋‚ด์šฉ

1. ์Šคํฌ๋กคํ•  div ์„ ์ •ํ•˜๊ธฐ

2. ref ๋ถ€์—ฌํ•˜๊ธฐ

3. onScroll props๋กœ ์Šคํฌ๋กค ํ•จ์ˆ˜ ๋„ฃ๊ธฐ

 

How(๊ณผ์ •) ?

(1) ์Šคํฌ๋กค์— ๊ด€์—ฌํ•˜๋Š” ์š”์†Œ๋“ค์ด ๋ช‡ ๊ฐ€์ง€ ์žˆ์—ˆ๋Š”๋ฐ, scrollBox๋ฅผ scrollRef ๋ฅผ ์ค€ ๊ฒƒ์˜ current ๊ฐ’, ์ฆ‰ scrollRef.current๋ผ๊ณ  ํ• ๋•Œ, 

scrollBox.scrollTop

- ์Šคํฌ๋กค ๋ฐ”๊ฐ€ ํ˜„์žฌ ์ œ์ผ ์ตœ์ƒ๋‹จ์œผ๋กœ๋ถ€ํ„ฐ ์–ผ๋งˆ๋‚˜ ๋–จ์–ด์ ธ ์žˆ๋Š”์ง€

scrollBox.scrollHeight

- ์Šคํฌ๋กค ๊ฐ€๋Šฅํ•œ ์˜์—ญ์˜ ์ „์ฒด ๋†’์ด

- ๋‚ด์šฉ์ด ์Šคํฌ๋กค ๋ฐ–์œผ๋กœ ๋„˜์น˜๋Š” ๊ฒฝ์šฐ ๋ทฐํฌํŠธ์˜ ๊ธธ์ด๋ณด๋‹ค ์ปค์งˆ ์ˆ˜ ์žˆ์Œ / ํ•ญ์ƒ ์ผ์ • 

scrollBox.clientHeight

- ์ฝ˜ํ…์ธ ๊ฐ€ ๋ณด์—ฌ์ง€๋Š” ์˜์—ญ์˜ ๋†’์ด

- ๋ทฐํฌํŠธ 

 

(2) ์ด ์„ธ ๊ฐœ์˜ ๊ฐœ๋…๋“ค์„ ํ™œ์šฉํ•ด์„œ ํ˜„์žฌ ์Šคํฌ๋กค ์ค‘์ž„์„ 

const isScroll = scrollBox.scrollTop < scrollBox.scrollHeight - scrollBox.clientHeight - SCROLL_GAP;

์ด๋ ‡๊ฒŒ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค. 

์—ฌ๊ธฐ์„œ SCROLL_GAP์ด๋ž€ 5๋ผ๋Š” ์ƒ์ˆ˜์ธ๋ฐ, ์Šคํฌ๋กค ์ค‘์„ ํŒ๋‹จํ•˜๋Š” ์ž„๊ณ„๊ฐ’์œผ๋กœ ์‚ฌ์šฉ๋œ๋‹ค.

์ฆ‰, ๋งŒ์•ฝ SCROLL_GAP์ด 0 ์ด๋ผ๋ฉด ์‚ฌ์šฉ์ž๊ฐ€ ์Šคํฌ๋กค ๋งจ ์•„๋ž˜์— ๋„๋‹ฌํ–ˆ์„ ๋•Œ๋งŒ ์Šคํฌ๋กค ์ค‘์œผ๋กœ ํŒ๋‹จ์ด ๋˜๋Š”๋ฐ,

๋‚ด๊ฐ€ ์›ํ•˜๋Š” ๊ฒƒ์€ ์Šคํฌ๋กค์ด ๋งจ ์œ„/์•„๋ž˜์— ๋„๋‹ฌํ•˜์ง€ ์•Š์„ ๋•Œ๋ฅผ ์Šคํฌ๋กค ์ค‘์œผ๋กœ ํ•˜๊ณ  ์‹ถ๊ธฐ ๋•Œ๋ฌธ์— ์ผ์ • ๊ฐ’์„ ๋นผ๋Š” ๊ฒƒ์ด๋‹ค.

5๊ฐ€ ์•„๋‹Œ 10 ๋˜๋Š” ๊ทธ ์ด์ƒ์œผ๋กœ ํ•ด๋„ ๋œ๋‹ค.

 

(3) ํ•˜์—ฌ ๋‚ด๊ฐ€ ๋งŒ๋“  ์Šคํฌ๋กค ํ•จ์ˆ˜ handleScroll์„ scrollRef์™€ ํ•จ๊ป˜ ์Šคํฌ๋กค ํ•  ์˜์—ญ์— props๋กœ ๋„˜๊ธด๋‹ค.

// ์Šคํฌ๋กค ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•  ๋•Œ
  const handleScroll = () => {
    const scrollBox = scrollRef.current;
    if (scrollBox) {
      const isScroll = scrollBox.scrollTop < scrollBox.scrollHeight - scrollBox.clientHeight - SCROLL_GAP;
      setIsScrolling(isScroll);
      if (!isScroll) {
        setNewAddedMsgNum(0);
      }
      setIsScrollTop(scrollBox.scrollTop === 0);
    }
  };

 ใ„ด setIsScrollTop์€ ์ฑ„ํŒ…๊ฒ€์ƒ‰์— ํ•„์š”ํ•œ ์š”์†Œ๋‹ค.

  return (
    <>
      <div
        className={'w-full h-full flex-1 p-[16px] flex flex-col gap-[8px] overflow-y-auto scroll-smooth'}
        ref={scrollRef}
        onScroll={handleScroll}
      >      
      ...

 

4. ์Šคํฌ๋กค ๋‹ค์šด ํ•˜๊ธฐ

Why(์ด์œ ) ?

์ฑ„ํŒ…์„ ๋ณด๊ณ  ์žˆ๋‹ค๊ฐ€ ์ตœํ•˜๋‹จ์œผ๋กœ ๋‚ด๋ ค๊ฐ€๋Š” ๋ฒ„ํŠผ์„ ๋งŒ๋“ค๊ณ  ์‹ถ์—ˆ๋‹ค.

๋˜ํ•œ, ์ฑ„ํŒ…์„ ๋ณด๊ณ  ์žˆ๋‹ค๊ฐ€ ์ƒˆ๋กœ์šด ๋ฉ”์„ธ์ง€๊ฐ€ ์ถ”๊ฐ€๋˜๋ฉด "์ƒˆ๋กœ์šด ๋ฉ”์„ธ์ง€ 0๊ฐœ ์ถ”๊ฐ€" ๋ผ๋Š” ๋ฒ„ํŠผ๋„ ์กฐ๊ฑด๋ถ€๋กœ ๋งŒ๋“ค๊ณ  ์‹ถ์—ˆ๋‹ค.

 

How(๊ณผ์ •) ?

์œ„์˜ ์„ค๋ช…์„ ํ† ๋Œ€๋กœ ์Šคํฌ๋กค ๋‹ค์šด์„ ๊ตฌํ˜„ํ–ˆ๋‹ค. 

scrollTop์ด scrollHeight ์™€ ๊ฐ™์•„์ง€๋ฉด ํ˜„์žฌ ์Šคํฌ๋กค๋ฐ”์˜ ์œ„์น˜๊ฐ€ ์ „์ฒด ๋†’์ด์™€ ๊ฐ™์•„์ง€๋ฏ€๋กœ ์Šคํฌ๋กค ๋‹ค์šด์ด ๋œ๋‹ค.

 const handleScrollDown = () => {
    scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
  };

์ด ์Šคํฌ๋กค ๋‹ค์šด ํ•จ์ˆ˜๋ฅผ ์ตœํ•˜๋‹จ์œผ๋กœ ๋‚ด๋ฆฌ๋Š” ๋ฒ„ํŠผ์„ ์ƒ์„ฑํ•ด์„œ props๋กœ ๋„˜๊ธด๋‹ค.

๋‚˜๋Š” 1. ์Šคํฌ๋กค ์ค‘์ด๊ณ  2. ์ƒˆ๋กœ ์ถ”๊ฐ€๋œ ๋ฉ”์„ธ์ง€ ์ˆ˜๊ฐ€ 0๊ฐœ์ธ์ง€์˜ ์กฐ๊ฑด์œผ๋กœ

๊ทธ๋ƒฅ ์Šคํฌ๋กค ๋‹ค์šด ๋ฒ„ํŠผ์„ ํ• ์ง€, ์ƒˆ๋กœ์šด ๋ฉ”์„ธ์ง€ ์ถ”๊ฐ€๋ฒ„ํŠผ์„ ํ• ์ง€ ์กฐ๊ฑด๋ถ€๋กœ ๋ Œ๋”๋งํ–ˆ๋‹ค.

{isScrolling ? (
        newAddedMsgNum === 0 ? (
          <ChatScroll handleScrollDown={handleScrollDown} />
        ) : (
          <NewChatAlert
            newAddedMsgNum={newAddedMsgNum}
            handleScrollDown={handleScrollDown}
            setNewAddedMsgNum={setNewAddedMsgNum}
          />
        )
      ) : (
        <></>
      )}

 

const ChatScroll = ({ handleScrollDown }: { handleScrollDown: () => void }) => {
  return (
    <>
        <div className="absolute bottom-28 w-full" id="์Šคํฌ๋กค ๋‚ด๋ฆฌ๋Š” ๋ฒ„ํŠผ">
          <FaArrowCircleDown
            className="w-12 h-12 text-[#E4D4F4] mx-auto cursor-pointer hover:scale-105 transition-all ease-in-out animate-bounce"
            onClick={handleScrollDown}
          />
        </div>
    </>
  );
};

export default ChatScroll;
const NewChatAlert = ({
  newAddedMsgNum,
  handleScrollDown,
  setNewAddedMsgNum
}: {
  newAddedMsgNum: number;
  handleScrollDown: () => void;
  setNewAddedMsgNum: Dispatch<SetStateAction<number>>;
}) => {
  const handleNewMsgAlertScroll = () => {
    handleScrollDown();
    setNewAddedMsgNum(0);
  };

  return (
    <div className="absolute bottom-28 w-full">
      <div className="flex mx-auto w-full cursor-pointer" onClick={handleNewMsgAlertScroll}>
        <div className="flex gap-[6px] mx-auto my-auto px-[16px] py-[14px] bg-[#F2EAFA] rounded-lg font-bold text-lg text-mainColor font-semibold">
          <div className="my-auto">
            <FaChevronDown />
          </div>
          <h1>{newAddedMsgNum} New Message</h1>
        </div>
      </div>
    </div>
  );
};

export default NewChatAlert;

 

๐Ÿšจ  TroubleShotting

-

๊ทธ๋Ÿด ๋“ฏํ•œ ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ…์€ ์—†์—ˆ๋‹ค. ์ƒ์†Œํ•œ ๊ฐœ๋…์ด ์–ด๋ ค์› ์„ ๋ฟ..

๐Ÿ“ธ ์Šคํฌ๋ฆฐ์ƒท

์Šคํฌ๋กค ์ค‘ + ์ƒˆ๋กœ์šด ๋ฉ”์„ธ์ง€ ์ˆ˜ === 0 ์Šคํฌ๋กค ์ค‘ + ์ƒˆ๋กœ์šด ๋ฉ”์„ธ์ง€๊ฐ€ ์ถ”๊ฐ€๋์„ ๋•Œ
์Šคํฌ๋กค ๋‹ค์šด

 

๐Ÿ“š ๋ ˆํผ๋Ÿฐ์Šค

์Šคํฌ๋กค ๊ฐœ๋… ์ •๋ฆฌ๋ฅผ ๋„ˆ๋ฌด ์ž˜ํ•ด๋‘์…จ๋‹ค.

https://devbirdfeet.tistory.com/228

 

์Šคํฌ๋กค ๋ฐ” ๊พธ๋ฏธ๊ธฐ

https://www.geeksforgeeks.org/how-to-change-style-of-scrollbar-using-tailwind-css/