본문 바로가기

코딩/Today I Learn

#1 NETFLIX HEADER

728x90

justify-content와 justify-items차이점

justify-content: Grid 아이템들의 너비를 모두 합한 값이 Grid 컨테이너의 너비보다 작을 때 Grid 아이템들을 통째로 정렬합니다.

justify-items: 아이템들을 가로(row축) 방향으로 정렬합니다. 컨테이너에 적용합니다.

 

 

place-items
align-items와 justify-items를 같이 쓸 수 있는 단축 속성이에요.
align-items, justify-items의 순서로 작성하고, 하나의 값만 쓰면 두 속성 모두에 적용됩니다.

 

place-content
align-content와 justify-content를 같이 쓸 수 있는 단축 속성이에요.
align-content, justify-content의 순서로 작성하고, 하나의 값만 쓰면 두 속성 모두에 적용됩니다.

 

 

CSS Grid 설명 참고사이트

https://studiomeal.com/archives/533

 

Layout 애니메이션 다양한 사용 예시들

https://www.framer.com/docs/animate-shared-layout/#syncing-layout-animations

 

AnimateSharedLayout | Framer for Developers

Animate layout changes across, and between, multiple components.

www.framer.com

 

 

import styled from "styled-components";
import { useState } from "react";

import { motion, AnimatePresence, Variants } from "framer-motion";
const Wrapper = styled(motion.div)`
    height: 100vh;
    width: 100vw;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    background: linear-gradient (135deg, rgb(238, 0, 153), rgba(221, 0, 28));
`;
const Box = styled(motion.div)`
    height: 400px;
    background-color: rgba(255, 255, 255, 1);
    border-radius: 40px;
    display: flex;
    justify-content: center;
    align-items: center;
    box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1), 0 10px 20px rgba(0, 0, 0, 0.06);
`;
const Grid = styled.div`
    width: 50vw;
    gap: 10px;
    display: grid;
    grid-template-columns: repeat(3, 1fr); //총6칸
    div:first-child,
    div:last-child {
        grid-column: span 2;
    }
`;

const Overlay = styled(motion.div)` //애니메이션효과주려면
    width: 100%;
    height: 100%;
    background-color: rgba(0, 0, 0, 0.5);
    position: absolute;
    display: flex;
    justify-content: center;
    align-items: center;
`;
function App() {
    const [clicked, setClicked] = useState(false);
    const toggleClicked = () => setClicked((prev) => !prev);
    return (
        <Wrapper onClick={toggleClicked}>
            <Grid>
                <Box layoutId="hello" /> //박스네개
                <Box /> 
                <Box />
                <Box />
            </Grid>
            <AnimatePresence>
                {clicked ? (
                    <Overlay
                        initial={{ opacity: 0 }}
                        animate={{ opacity: 1 }}
                        exit={{ opacity: 0 }}
                    >
                        <Box
                            layoutId="hello"
                            style={{ width: 400, height: 400 }}
                        />
                    </Overlay>
                ) : null}
            </AnimatePresence>
        </Wrapper>
    );
}

export default App;

 

 

 

 



function App() {
    const [id, setId] = useState<null | string>(null);
    console.log(id);

    return (
        <Wrapper>
            <Grid>
                {["1", "2", "3", "4"].map((n) => (
                    <Box onClick={() => setId(n)} key={n} layoutId={n} />  //바로 setId()하면 누르지않고 바로 시작하게되니 조심
                ))}
            </Grid>
            <AnimatePresence>
                {id ? (
                    <Overlay
                        onClick={() => setId(null)}
                        initial={{ opacity: 0 }}
                        animate={{ opacity: 1 }}
                        exit={{ opacity: 0 }}
                    >
                        <Box
                            layoutId={id}
                            style={{ width: 400, height: 400 }}
                        />
                    </Overlay>
                ) : null}
            </AnimatePresence>
        </Wrapper>
    );
}

export default App;

 

 

absolute 에서 정중앙에 위치 시킬 때 트릭

left:0, right:0, margin: 0 auto;

 

우리는 같은 도메인일 때 <a></a> 를 사용하면 안된다. 새로고침하기 때문에 <Link>사용해야함

 

React Router 6버전에서는 useRoute()가 useMatch()로 대체되었습니다.

 

Framer Motion repeat
https://www.framer.com/docs/transition/###repeat

whileHover
호버 제스처가 인식되는 동안 애니메이션할 속성 또는 variant label입니다.
ex) < motion.div whileHover={{ scale: 1.2 }} / >
https://www.framer.com/docs/gestures/###whilehover

 

Gestures | Framer for Developers

A powerful gesture recognition system for the browser.

www.framer.com

 

 

*react-router-dom v5 vs v6**

1. Link에서 to는 상대경로로 적으시면 됩니다
ex. '/tv' -> 'tv'

2. exact가 사라졌습니다
대신 알아서 최적의 경로를 react-router-dom이 판단하여 매칭해줍니다

3. useRouteMatch가 useMatch로 변경되었습니다
이 또한 상대경로로 작성하는 것으로 변경되었습니다
ex. useRouteMatch('/tv') -> useMatch('tv')

https://reactrouter.com/docs/en/v6/upgrading/v5#upgrade-to-react-router-v6

 

transform-origin
transform-origin CSS 속성은 엘리먼트 transformation의 원점을 설정합니다.
```
transform-origin: center;
transform-origin: top left;
transform-origin: bottom right 60px;
```
https://developer.mozilla.org/en-US/docs/Web/CSS/transform-origin

 

transform-origin - CSS: Cascading Style Sheets | MDN

The transform-origin CSS property sets the origin for an element's transformations.

developer.mozilla.org

 

컴포넌트 프롭이아닌 애니메이션 코드로 실행시키기

애니메이션 20개를 동시에 작동시켜야한다면 코드로 실행하는 것이 더 나을 것 같은내용 

코드로 실행시키기

mport { motion, useAnimation } from "framer-motion"

function Header() {
    const [searchOpen, setSerachOpen] = useState(false);
    const inputAnimation useAnimation();
    const toggleSearch = () => {
        if (searchOpen) {
            inputAnimation.start({
                scaleX: 0,
            });
        } else {
            inputAnimation.start({ scaleX: 1 });
        }
        setSerachOpen((prev) => !prev);
    };
    return (
        <Nav>
            <Col>
              ...
            </Col>
            <Col>
                <Search onClick={toggleSearch}>
                    <motion.svg
                        animate={{ x: searchOpen ? -200 : 0 }}
                        transition={{ type: "linear" }}
                        fill="currentColor"
                        viewBox="0 0 20 20"
                        xmlns="http://www.w3.org/2000/svg"
                    >
                        <path
                            fillRule="evenodd"
                            d="M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z"
                            clipRule="evenodd"
                        ></path>
                    </motion.svg>
                    <Input
                        transition={{ type: "linear" }}
                        animate={inputAnimation}
                        placeholder="serach for movie"
                    />
                </Search>
            </Col>
        </Nav>
    );
}

 

useViewportScroll() 스크롤 움직일 때 밑에서부터 얼마나 멀리 떨어져있는지 알려줌

첫번 째 값은 progress

두가지 값을 얻는데 x,y에대한 스크롤 진행도를 0에서부터 1 사이값으로 알 수 있음.

얼마나 멀리 이동했는지 픽셀단위로 나타내는 것 .

 

----> useScroll

 

코드로 실행시키는 방법을 알려주기위해 다음같이 만든 것.

애니메이트 시킬게 배경화면만 있지 않으니

실제로 사용할 때는 prop animate로 animate={{background: scrollY >80?   :}} 이렇게 사용하는게 좋다!  

 

useAnimation()


useAnimation 훅을사용하여 시작 및 중지 메서드가 있는 AnimationControls을 만들 수 있습니다.
```
const MyComponent = () => {
const controls = useAnimation()
return < motion.div animate={controls} />
}

// 애니메이션은 controls.start 메소드로 시작할 수 있습니다.
controls.start({ x: "100%", transition: { duration: 3 }})
```
https://www.framer.com/docs/animation/#component-animation-controls

useScroll(): ScrollMotionValues
viewport가 스크롤될 때 업데이트되는 MotionValues를 반환합니다.
주의! body 또는 html을 height: 100% 또는 이와 유사한 것으로 설정하면 페이지 길이를 정확하게 측정하는 브라우저의 기능이 손상되므로 Progress 값이 손상됩니다.
```
export const MyComponent = () => {
const { scrollYProgress } = useScroll()
return < motion.div style={{ scaleX: scrollYProgress }} />
}
```
https://www.framer.com/docs/motionvalue/###useviewportscroll

 

Motion values overview | Framer for Developers

Motion values track the state and velocity of animating values.

www.framer.com

 

import styled from "styled-components";
import { motion, useAnimation, useScroll } from "framer-motion";
import { Link, useMatch } from "react-router-dom";
import { useEffect, useState } from "react";

const Nav = styled(motion.nav)`
    display: flex;
    justify-content: space-between;
    align-items: center;
    position: fixed;
    width: 100%;
    top: 0;
    height: 80px;
    font-size: 14px;
    padding: 20px 60px;
`;
const Col = styled.div`
    display: flex;
    align-items: center;
`;
const Logo = styled(motion.svg)`
    margin-right: 50px;
    width: 95px;
    height: 25px;
    fill: ${(props) => props.theme.red};
    path {
        stroke-width: 6px;
        stroke: white;
    }
`;

const Items = styled.ul`
    display: flex;
    align-items: center;
`;

const Item = styled.li`
    margin-right: 20px;
    color: ${(props) => props.theme.white.darker};
    transition: color 0.3s ease-in-out;
    position: relative;
    display: flex;
    justify-content: center;
    flex-direction: column;
    &:hover {
        color: ${(props) => props.theme.white.lighter};
    }
`;
const Search = styled.span`
    color: white;
    display: flex;
    align-items: center;
    position: relative;
    svg {
        height: 25px;
    }
`;
const Circle = styled(motion.span)`
    position: absolute;
    width: 5px;
    height: 5px;
    border-radius: 50%;
    bottom: -5px;
    left: 0;
    right: 0;
    margin: 0 auto;
    background-color: ${(props) => props.theme.red};
`;
const Input = styled(motion.input)`
    transform-origin: right center;
    position: absolute;
    right: 0px;
    padding: 5px 10px;
    padding-left: 40px;
    z-index: -1;
    color: white;
    font-size: 16px;
    background-color: transparent;
    border: 1px solid ${(props) => props.theme.white.lighter};
`;
const logoVariants = {
    normal: {
        fillOpacity: 1,
    },
    active: {
        fillOpacity: [0, 1, 0],
        transition: {
            repeat: Infinity,
        },
    },
};
const navVariants = {
    top: {
        backgroundColor: "rgba(0,0,0,0)",
    },
    scroll: {
        backgroundColor: "rgba(0,0,0,1)",
    },
};

function Header() {
    const [searchOpen, setSearchOpen] = useState(false);
    const homeMatch = useMatch("/");
    const tvMatch = useMatch("tv");
    const inputAnimation = useAnimation();
    const navAnimation = useAnimation();
    const { scrollY } = useScroll();

    const toggleSearch = () => {
        if (searchOpen) {
            inputAnimation.start({
                scaleX: 0,
            });
        } else {
            inputAnimation.start({ scaleX: 1 });
        }
        setSearchOpen((prev) => !prev);
    };
    useEffect(() => {
        scrollY.onChange(() => {
            if (scrollY.get() > 80) {
                navAnimation.start("scroll");
            } else {
                navAnimation.start("top");
            }
            console.log(scrollY.get());
        });
    }, [scrollY]);

    return (
        <Nav variants={navVariants} animate={navAnimation} initial={"top"}>
            <Col>
                <Logo
                    variants={logoVariants}
                    whileHover="active"
                    initial="normal"
                    xmlns="http://www.w3.org/2000/svg"
                    width="1024"
                    height="276.742"
                    viewBox="0 0 1024 276.742"
                >
                    <motion.path d="M140.803 258.904c-15.404 2.705-31.079 3.516-47.294 5.676l-49.458-144.856v151.073c-15.404 1.621-29.457 3.783-44.051 5.945v-276.742h41.08l56.212 157.021v-157.021h43.511v258.904zm85.131-157.558c16.757 0 42.431-.811 57.835-.811v43.24c-19.189 0-41.619 0-57.835.811v64.322c25.405-1.621 50.809-3.785 76.482-4.596v41.617l-119.724 9.461v-255.39h119.724v43.241h-76.482v58.105zm237.284-58.104h-44.862v198.908c-14.594 0-29.188 0-43.239.539v-199.447h-44.862v-43.242h132.965l-.002 43.242zm70.266 55.132h59.187v43.24h-59.187v98.104h-42.433v-239.718h120.808v43.241h-78.375v55.133zm148.641 103.507c24.594.539 49.456 2.434 73.51 3.783v42.701c-38.646-2.434-77.293-4.863-116.75-5.676v-242.689h43.24v201.881zm109.994 49.457c13.783.812 28.377 1.623 42.43 3.242v-254.58h-42.43v251.338zm231.881-251.338l-54.863 131.615 54.863 145.127c-16.217-2.162-32.432-5.135-48.648-7.838l-31.078-79.994-31.617 73.51c-15.678-2.705-30.812-3.516-46.484-5.678l55.672-126.75-50.269-129.992h46.482l28.377 72.699 30.27-72.699h47.295z" />
                </Logo>
                <Items>
                    <Item>
                        <Link to="/">
                            Home
                            {homeMatch && <Circle layoutId="circle" />}
                        </Link>
                    </Item>
                    <Item>
                        <Link to="tv">
                            Tv Shows
                            {tvMatch && <Circle layoutId="circle" />}
                        </Link>
                    </Item>
                </Items>
            </Col>
            <Col>
                <Search onClick={toggleSearch}>
                    <motion.svg
                        animate={{ x: searchOpen ? -200 : 0 }}
                        transition={{ type: "linear" }}
                        fill="currentColor"
                        viewBox="0 0 20 20"
                        xmlns="http://www.w3.org/2000/svg"
                    >
                        <path
                            fillRule="evenodd"
                            d="M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z"
                            clipRule="evenodd"
                        ></path>
                    </motion.svg>
                    <Input
                        initial={{ scaleX: 0 }}
                        transition={{ type: "linear" }}
                        animate={inputAnimation}
                        placeholder="serach for movie"
                    />
                </Search>
            </Col>
        </Nav>
    );
}

export default Header;

'코딩 > Today I Learn' 카테고리의 다른 글

#3 NETFLIX SLIDER  (0) 2022.10.25
#2 NETFLIX HOME SCREEN  (0) 2022.10.12
React animaiton #framer-motion  (0) 2022.09.26
React Js Master #10 Ref,CrossBoardMovement  (0) 2022.08.03
React Js Master #9 selector set, beautiful-dnd  (0) 2022.07.11