ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Python(Flask)+React+SASS+mongoDB로 웹 서비스 제작하기 프로젝트(2-3) – Login Component 만들기(Animation, SASS)
    Node 2021. 4. 27. 19:45

    진짜 진짜 진짜 하고싶었던 것 애니메이션 적용시켜서 Web-Application을 조금 더
    ‘트렌디’하게 보이게 하는 것… 오늘 했다 그래서 포스팅 연속으로 3개 올리고 있다.

    사실 paper_lee가 만든 API를 통해 회원가입 데이터 송수신 하는 것 까지 했는데 아직
    완성이 안돼서 오늘 여기까지만 쓸 것..!

    먼저 여기 들어가보셈
    http://anicollection.github.io
    쩔음.

    내가 사용한 것은, bounceIn Left Right, bounceOut Left Right, fadeIn 이렇게 5개 씀.

    먼저 여기서 코드 복사해서 ‘src/Animation.css’ 에 작성

    src/Animation

    /*base code*/
    
    .animated {
        -webkit-animation-duration: 1s;
        animation-duration: 1s;
        -webkit-animation-fill-mode: both;
        animation-fill-mode: both;
    }
    
    .animated.infinite {
        -webkit-animation-iteration-count: infinite;
        animation-iteration-count: infinite;
    }
    
    .animated.hinge {
        -webkit-animation-duration: 2s;
        animation-duration: 2s;
    }
    @-webkit-keyframes bounceInLeft {
        0%, 100%, 60%, 75%, 90% {
            -webkit-transition-timing-function: cubic-bezier(0.215, .61, .355, 1);
            transition-timing-function: cubic-bezier(0.215, .61, .355, 1)
        }
        0% {
            opacity: 0;
            -webkit-transform: translate3d(-3000px, 0, 0);
            transform: translate3d(-3000px, 0, 0)
        }
        60% {
            opacity: 1;
            -webkit-transform: translate3d(25px, 0, 0);
            transform: translate3d(25px, 0, 0)
        }
        75% {
            -webkit-transform: translate3d(-10px, 0, 0);
            transform: translate3d(-10px, 0, 0)
        }
        90% {
            -webkit-transform: translate3d(5px, 0, 0);
            transform: translate3d(5px, 0, 0)
        }
        100% {
            -webkit-transform: none;
            transform: none
        }
    }
    @keyframes bounceInLeft {
        0%, 100%, 60%, 75%, 90% {
            -webkit-transition-timing-function: cubic-bezier(0.215, .61, .355, 1);
            transition-timing-function: cubic-bezier(0.215, .61, .355, 1)
        }
        0% {
            opacity: 0;
            -webkit-transform: translate3d(-3000px, 0, 0);
            -ms-transform: translate3d(-3000px, 0, 0);
            transform: translate3d(-3000px, 0, 0)
        }
        60% {
            opacity: 1;
            -webkit-transform: translate3d(25px, 0, 0);
            -ms-transform: translate3d(25px, 0, 0);
            transform: translate3d(25px, 0, 0)
        }
        75% {
            -webkit-transform: translate3d(-10px, 0, 0);
            -ms-transform: translate3d(-10px, 0, 0);
            transform: translate3d(-10px, 0, 0)
        }
        90% {
            -webkit-transform: translate3d(5px, 0, 0);
            -ms-transform: translate3d(5px, 0, 0);
            transform: translate3d(5px, 0, 0)
        }
        100% {
            -webkit-transform: none;
            -ms-transform: none;
            transform: none
        }
    }
    .bounceInLeft {
        -webkit-animation-name: bounceInLeft;
        animation-name: bounceInLeft
    }
    @-webkit-keyframes bounceInRight {
        0%, 100%, 60%, 75%, 90% {
            -webkit-transition-timing-function: cubic-bezier(0.215, .61, .355, 1);
            transition-timing-function: cubic-bezier(0.215, .61, .355, 1)
        }
        0% {
            opacity: 0;
            -webkit-transform: translate3d(3000px, 0, 0);
            transform: translate3d(3000px, 0, 0)
        }
        60% {
            opacity: 1;
            -webkit-transform: translate3d(-25px, 0, 0);
            transform: translate3d(-25px, 0, 0)
        }
        75% {
            -webkit-transform: translate3d(10px, 0, 0);
            transform: translate3d(10px, 0, 0)
        }
        90% {
            -webkit-transform: translate3d(-5px, 0, 0);
            transform: translate3d(-5px, 0, 0)
        }
        100% {
            -webkit-transform: none;
            transform: none
        }
    }
    @keyframes bounceInRight {
        0%, 100%, 60%, 75%, 90% {
            -webkit-transition-timing-function: cubic-bezier(0.215, .61, .355, 1);
            transition-timing-function: cubic-bezier(0.215, .61, .355, 1)
        }
        0% {
            opacity: 0;
            -webkit-transform: translate3d(3000px, 0, 0);
            -ms-transform: translate3d(3000px, 0, 0);
            transform: translate3d(3000px, 0, 0)
        }
        60% {
            opacity: 1;
            -webkit-transform: translate3d(-25px, 0, 0);
            -ms-transform: translate3d(-25px, 0, 0);
            transform: translate3d(-25px, 0, 0)
        }
        75% {
            -webkit-transform: translate3d(10px, 0, 0);
            -ms-transform: translate3d(10px, 0, 0);
            transform: translate3d(10px, 0, 0)
        }
        90% {
            -webkit-transform: translate3d(-5px, 0, 0);
            -ms-transform: translate3d(-5px, 0, 0);
            transform: translate3d(-5px, 0, 0)
        }
        100% {
            -webkit-transform: none;
            -ms-transform: none;
            transform: none
        }
    }
    .bounceInRight {
        -webkit-animation-name: bounceInRight;
        animation-name: bounceInRight
    }
    
    @-webkit-keyframes bounceOutLeft {
        20% {
            opacity: 1;
            -webkit-transform: translate3d(20px, 0, 0);
            transform: translate3d(20px, 0, 0)
        }
        100% {
            opacity: 0;
            -webkit-transform: translate3d(-2000px, 0, 0);
            transform: translate3d(-2000px, 0, 0)
        }
    }
    @keyframes bounceOutLeft {
        20% {
            opacity: 1;
            -webkit-transform: translate3d(20px, 0, 0);
            -ms-transform: translate3d(20px, 0, 0);
            transform: translate3d(20px, 0, 0)
        }
        100% {
            opacity: 0;
            -webkit-transform: translate3d(-2000px, 0, 0);
            -ms-transform: translate3d(-2000px, 0, 0);
            transform: translate3d(-2000px, 0, 0)
        }
    }
    .bounceOutLeft {
        -webkit-animation-name: bounceOutLeft;
        animation-name: bounceOutLeft
    }
    
    
    @-webkit-keyframes bounceOutRight {
        20% {
            opacity: 1;
            -webkit-transform: translate3d(-20px, 0, 0);
            transform: translate3d(-20px, 0, 0)
        }
        100% {
            opacity: 0;
            -webkit-transform: translate3d(2000px, 0, 0);
            transform: translate3d(2000px, 0, 0)
        }
    }
    @keyframes bounceOutRight {
        20% {
            opacity: 1;
            -webkit-transform: translate3d(-20px, 0, 0);
            -ms-transform: translate3d(-20px, 0, 0);
            transform: translate3d(-20px, 0, 0)
        }
        100% {
            opacity: 0;
            -webkit-transform: translate3d(2000px, 0, 0);
            -ms-transform: translate3d(2000px, 0, 0);
            transform: translate3d(2000px, 0, 0)
        }
    }
    .bounceOutRight {
        -webkit-animation-name: bounceOutRight;
        animation-name: bounceOutRight
    }
    
    /*the animation definition*/
    @-webkit-keyframes fadeIn {
        0% {
            opacity: 0
        }
        100% {
            opacity: 1
        }
    }
    @keyframes fadeIn {
        0% {
            opacity: 0
        }
        100% {
            opacity: 1
        }
    }
    .fadeIn {
        -webkit-animation-name: fadeIn;
        animation-name: fadeIn
    }

    사이트 들어가기 귀찮으면 이거 복붙하면 됨!

    pages/Main.js 수정

    import React, { Component } from 'react';
    import Join from 'components/Join';
    import Login from 'components/Login';
    import '../Animation.css'
    import queryString from "query-string";
    import 'style/Main.scss';
    import leftImg from '/home/linesys/OLLOC/src/img/main.png'
    
    class Main extends Component {
        state = {
            direction: 'animated fadeIn'
        }
    
        loginsplit = (query) => {
            if(query === 'login') return false;
            else return true;
        }
    
        move = (getDirection) => {  // 자식 컴포넌트에서 값을 가져옴
            this.setState({
                direction: getDirection,
            })
        }
        render() {
            const {location} = this.props;
            const query = queryString.parse(location.search);
            const detail = this.loginsplit(query.detail);
    
            return (
                <div id = "main">
                    <img className = "animated fadeIn" src={leftImg} alt="이미지"/>  // 이미지도 초기 로드시 fadeIn효과로 불러지게 함
                    {
                        detail
                            ? <Join
                                animation={this.move}
                                direction={this.state.direction}
                            />
                            : <Login
                                    animation={this.move}
                                    direction={this.state.direction}
                            />
                    }
                </div>
            );
        };
    }
    
    export default Main;

    사용법은 간단하게 class명에 animated와 ‘적용시킬 애니메이션 이름’을 추가해주면 된다.
    페이지 새로고침을 했을 때 이전에 애니메이션이 중복되서 나오는 것을 방지하기 위해
    direction을 fadeIn으로 초기화 시켰다.
    join, login 서로 컴포넌트가 전환될 때 어떤 애니메이션을 불러올지 통신을 위해 animation, direction props를 전달해준다.

    components/Join.js 수정

    import React, {Component} from "react";
    import "../Animation.css"
    import { Redirect } from 'react-router-dom';
    
    class Join extends Component{
    
        changeView = () => {
            this.setState({
                animation: 'animated bounceOutLeft'
            });
            this.props.animation('animated bounceInRight');
            setTimeout(()=>{
                this.setState({
                    redirect: true
                })
            }, 300);
        }
    
        constructor(props){
            super(props);
            this.state ={
                animation: this.props.direction,
                redirect: false,
            }
        }
    
        render(){ 
            if(this.state.redirect){
                return <Redirect push to ='main/?detail=login' />;
            }
            return(
                <div id ="join">
                    <form id="loginForm" className={this.state.animation}>
                        <h1>OLLoc</h1>
                        <span className="loginText">친구들의 지도에 그려진 사진과 글을 보려면 가입하세요</span>
                        <button className="mainBtn">facebook으로 로그인</button>
                        <div id="line">또는</div>
                        <input
                            className="textInput"
                            placeholder="휴대폰 번호 또는 이메일 주소"
                        />
                        <input
                            className="textInput"
                            placeholder="성명"
                        />
                        <input
                            className="textInput"
                            placeholder="사용자 이름"
                        />
                        <input
                            className="textInput"
                            placeholder="비밀번호"
                        />
                        <button className="mainBtn">가입</button>
                        <span className="loginText">가입하면 OLLoc의 약관, 데이터 정책 및 쿠키 정책에 동의하게 됩니다.</span>
                    </form>
                    <div id="checkMem" className={this.state.animation}>
                        계정이 있으신가요? <span id = "loginBtn" onClick={this.changeView} >로그인</span>
                    </div>
                </div>
            );
        }
    }
    
    export default Join;

    부모에게 받은 props.direction으로 state.animation을 초기화한다.
    #loginForm -> div안에있는 form 태그에 state.animation을 className으로 정해줘
    애니메이션 효과를 준다.
    이렇게 하면 초기 로드시 fadeIn 효과를 줄 수 있다.
    이어서 하단의 ‘<span id = “loginBtn” onClick={this.changeView} >로그인</span>’ 을 클릭하면 changeView라는 함수를 통하게 된다.

    먼저 현재 state.animation을 bounceOutLeft로 변경하여 re-rendering 되며 왼쪽으로 사라지는 애니메이션을 주게된다.
    그리고 부모 컴포넌트로부터 받은 props.animation 함수를 통해 매개변수로 bounceInRight를 줬다. 이렇게 하면 부모가 Join.js의 형제(?)인 Login.js에게 bounceInRight를 줄 것이고 Login Component가 오른쪽에서부터 왼쪽으로 나타나는 애니메이션을 주며 뷰가 전환될 것이다. (새로고침 없이! react-router!)
    그리고 이어서 setTimeout이 있다 사실 componentWillUnmount 메소드를 통해
    애니메이션을 주려 했으나 저기에서 state를 변경해도 새로운 DOM이 나타나 버려서
    애니메이션이 진행될 시간인 300ms의 딜레이 후 /main/?detail=login로 리다이렉트 하게 했다.

    state.redirect가 true가 되며 render(){if(this.state.redirect)}를 통해 리다이렉팅 되게 된다.

    components/Login.js수정

    import React, {Component} from 'react';
    import { Redirect, Link } from 'react-router-dom';
    
    class Login extends Component{
        changeView = () => {
            this.setState({
                animation: 'animated bounceOutRight'
            });
    
            this.props.animation('animated bounceInLeft');
    
            setTimeout(()=>{
                this.setState({
                    redirect: true
                })
            }, 300);
        }
    
        constructor(props){
            super(props);
            this.state ={
                animation: this.props.direction,
                redirect: false,
            }
        }
    
        render(){
            if(this.state.redirect){
                return <Redirect push to ='/main' />;
            }
            return(
                <div id ="login">
                    <form className={this.state.animation} id="loginForm">
                        <span id="prevBtn" onClick={this.changeView}><i className="fas fa-arrow-left"></i></span>
                        <h1 id="mainTitle">OLLoc</h1>
                        <span className="loginText">친구들의 지도에 그려진 사진과 글을 보려면 가입하세요</span>
                        <button className="mainBtn">facebook으로 로그인</button>
                        <div id="line">또는</div>
                        <input
                            className="textInput"
                            placeholder="휴대폰 번호 또는 이메일 주소"
                        />
                        <input
                            className="textInput"
                            placeholder="비밀번호"
                        />
                        <Link to ="/" className="noUnderLine" ><button className="mainBtn">로그인</button></Link>
                        <span className="loginText">가입하면 OLLoc의 약관, 데이터 정책 및 쿠키 정책에 동의하게 됩니다.</span>
                    </form>
                </div>
            );
        }
    }
    
    export default Login;

    ‘components/Join.js’ 와 같은 원리로 작동한다.

    스타일을 주자! ‘style/Main.scss’ 작성

    sass는 기존 css에 변수와 조건문, 반복문 등 기존의 css를 확장시켜 좀 더 편리하게
    해주는 스타일시트 언어다.
    사용하기 위해서는 ‘node-sass’라는 라이브러리가 필요하다. 웹브라우져가 기존의 css코드로 이해할 수 있도록 컴파일 해주는 역할을 한다.

    $ yarn add node-sass

    이제 코드를 작성하자.

    #join, #login{ overflow: hidden; display: inline-block; width: 400px;}

    다만 위 코드만 설명하면  상단에 이 부분은 애니메이션이 bounceIn&Out될 때
    양쪽 끝에서 움직이기
    때문에 제한을 걸기 위해 overflow:hidden, display: inline-block; width: 400px로
    크기를 정해주고 내부 요소들이 부모 요소 밖으로 넘치는 경우를 예방했다.

    결과

    홈으로 접속하여 회원가입 화면으로 전환될 때 fadeIn 효과
    로그인 버튼 누르고 이전 버튼을 눌렀을 때 bounceIn&Out 효과
    그리고 다시 새로고침을 했을 때 fadeIn 효과가 나오는 것을 확인할 수 있다.

Designed by Tistory.