본문 바로가기
JAVASCRIPT

자바스크립트 콜백 지옥 해결 : GSAP 라이브러리

by dongjin6539 2023. 5. 4.
728x90
반응형

자바스크립트 콜백 지옥 해결 : GSAP 라이브러리

 

길라잡이

  • https://dongjin6539.tistory.com/101 콜백 함수 블로그에서 확인 할 수 있는 콜백 지옥을 해결하기 위해 GSAP 라이브러리를 사용해서 가성이 좋도록 해결할 수 있습니다.
  • 콜백 함수를 해결하려는 이유는 콜백 함수를 중첩해서 사용하면서 코드가 복잡해지고 가독성이 떨어지는 현상을 말합니다. 비동기적으로 수행되는 작업이 많아지면서 콜백 함수가 중첩되어 사용되면서 코드가 복잡해지고, 디버깅이 어려워지고, 유지보수가 어려워지는 문제점이 발생하기 때문입니다.

 

 

예시

 

코드 구성

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>포트폴리오 메인페이지</title>
    <link href="https://fonts.googleapis.com/css2?family=Abel&display=swap" rel="stylesheet">
    <style>
        @font-face {
            font-family: 'PPNeueWorld-CondensedRegular';
            font-weight: normal;
            font-style: normal;
            src: url('fonts/PPNeueWorld-CondensedRegular.woff2') format('woff2');
            font-display: swap;
        }
        @font-face {
            font-family: 'PPNeueWorld-ExtendedThin';
            font-weight: normal;
            font-style: normal;
            src: url('fonts/PPNeueWorld-ExtendedThin.woff2') format('woff2');
            font-display: swap;
        }
        @font-face {
            font-family: 'PPNeueWorld-Regular';
            font-weight: normal;
            font-style: normal;
            src: url('fonts/PPNeueWorld-Regular.woff2') format('woff2');
            font-display: swap;
        }
        @font-face {
            font-family: 'PPNeueWorld-SemiCondensedUltrabold';
            font-weight: normal;
            font-style: normal;
            src: url('fonts/PPNeueWorld-SemiCondensedUltrabold.woff2') format('woff2');
            font-display: swap;
        }
        @font-face {
            font-family: 'PPNeueWorld-SemiExtendedBlack';
            font-weight: normal;
            font-style: normal;
            src: url('fonts/PPNeueWorld-SemiExtendedBlack.woff2') format('woff2');
            font-display: swap;
        }
        @font-face {
            font-family: 'PPNeueWorld-SuperCondensedLight';
            font-weight: normal;
            font-style: normal;
            src: url('fonts/PPNeueWorld-SuperCondensedLight.woff2') format('woff2');
            font-display: swap;
        }
        @font-face {
            font-family: 'PPNeueWorld-Thin';
            font-weight: normal;
            font-style: normal;
            src: url('fonts/PPNeueWorld-Thin.woff2') format('woff2');
            font-display: swap;
        }
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        body {
            width: 100%;
            height: 100vh;
            background-color: #0b130d;
            overflow: hidden;
        }
        em {
            font-style: normal;
        }
        /* header */
        #header {
            position: fixed;
            left: 0;
            top: 0;
            width: 100%;
            align-items: end;
            justify-content: space-between;
            padding: 10px 20px;
            font-family: 'Abel';
            z-index: 1000;
            display: flex;
        }
        #header h1 {
            font-weight: normal;
            color: #D2E3C0;
            font-size: 28px;
        }
        #header nav li {
            list-style: none;
            display: inline-block;
        }
        #header nav li a {
            color: #D2E3C0;
            text-transform: uppercase;
            font-weight: bold;
            padding: 10px;
            font-size: 18px;
        }
        #footer {
            position: fixed;
            left: 50%;
            bottom: 1vw;
            transform: translateX(-50%);
            z-index: 1000;
        }
        #footer a {
            color: #fff;
            font-family: 'Abel';
            text-decoration: none;
        }
        #footer a:hover {
            text-decoration: underline;
            text-underline-position: under;
        }
        #main {
            display: flex;
            align-items: center;
            justify-content: center;
            width: 100%;
            height: 100vh;
            position: relative;
        }
        .text__inner {
            text-align: center;
            color: #D2E3C0;
            position: relative;
            z-index: 3000;
        }
        .text__inner > div {
            font-size: 8vw;
            line-height: 1.2;
        }
        .text__inner > div.ti1 {
            font-family: 'PPNeueWorld-ExtendedThin';
        }
        .text__inner > div.ti1 em {
            font-family: 'PPNeueWorld-Regular';
        }
        .text__inner > div.ti2 {
            font-family: 'PPNeueWorld-SemiCondensedUltrabold';
        }
        .text__inner > div.ti3 {
            font-family: 'PPNeueWorld-CondensedRegular';
        }
        .text__inner > div.ti3 em {
            font-family: 'PPNeueWorld-Thin';
        }
        .img__inner > img{
            position: absolute;
            width: 10vw;
            z-index: 2000;
        }
        .img__inner .ii1 {
            left: 45%;
            top: 50%;
            transform: translateY(-200%);
        }
        .img__inner .ii2 {
            left: 15%;
            top: 50%;
            transform: translateY(60%);
        }
        .img__inner .ii3 {
            left: 80%;
            top: 50%;
            transform: translateY(-100%);
        }
        #webgl {
            position: absolute;
            left: 0;
            top: 0;
            width: 100%;
            height: 100vh;
            z-index: 1;
        }
        #webgl iframe {
            width: 100%;
            height: 100%;
        }
    </style>
</head>
<body>
    <header id="header">
        <h1>DJ's PORTFOLIO</h1>
        <nav>
            <ul>
                <li><a href="#">work</a></li>
                <li><a href="#">about</a></li>
            </ul>
        </nav>
    </header>
    <!-- //header -->

    <main id="main">
        <div class="text__inner">
            <div class="ti1">let's <em>introduce</em></div>
            <div class="ti2 split">frontend developer's</div>
            <div class="ti3"><em>all</em> works <em>of</em> portfolio</div>
        </div>
        <div class="img__inner">
            <img class="ii1" src="img/figure01.png" alt="이미지1">
            <img class="ii2" src="img/figure02.png" alt="이미지2">
            <img class="ii3" src="img/figure03.png" alt="이미지3">
        </div>
        <div id="webgl">
            <iframe src="three.html" frameborder="0"></iframe>
        </div>
    </main>
    <!-- //main -->

    <footer id="footer">
        <a href="https://dongjin6539.tistory.com/" target="_blank">dongjin6539.tistory.com</a>
    </footer>
    <!-- //footer -->
</body>
</html>
  • body 구역에 header 태그, main 태그, footer 태그를 알맞게 입력 후 각 태그별 알맞는 태그를 사용해서 텍스트를 입력하고 알맞는 class 명을 입력해줍니다.
  • head 구역에 css 스타일을 입력하기 위해 style 태그를 사용해줍니다. 텍스트에 다양한 폰트를 주기 위해 폰트를 @font-face로 가져옵니다. 그리고 class 명과 알맞는 태그에 다양한 폰트를 font-family를 사용해서 입력해줍니다. 그리고 다른 class 명과 알맞는 태그에 다양한 css 스타일 속성을 입력해줍니다.

 

SCRIPT

<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.11.5/gsap.min.js"></script>
<script>
    // 글씨 분리하기
    document.querySelectorAll(".split").forEach(desc => {
        let splitText = desc.innerText
        let splitWrap = splitText.split('').join("</span><span aria-hidden='true'>");
            splitWrap = "<span aria-hidden='true'>" + splitWrap + "</span>";
            desc.innerHTML = splitWrap;
            desc.setAttribute("aria-label", splitText);
    });

    // 메인 기본 세팅
    gsap.set(".text__inner .ti1", {opacity: 0, y: "10vh"});
    gsap.set(".text__inner .ti2 span", {opacity: 0, x: 500, scale: 10, display: "inline-block", minWidth: "1.4vw"});
    gsap.set(".text__inner .ti3", {opacity: 0, y: "-10vh"});
    gsap.set(".img__inner .ii1", {opacity: 0, rotation: 360, scale: 0});
    gsap.set(".img__inner .ii2", {opacity: 0, rotation: 360, scale: 0});
    gsap.set(".img__inner .ii3", {opacity: 0, rotation: 360, scale: 0});
    gsap.set("#header", {y: -100});
    gsap.set("#footer", {bottom: -100});
    gsap.set("#webgl", {opacity: 0});

    // 메인 애니메이션
    setTimeout(() => {
        let tl = gsap.timeline();

        tl.to(".text__inner .ti2 span", {opacity: 1, x: 0, scale: 1, duration: 0.6, stagger: 0.1, ease: Power3.easeInOut})
        tl.to(".text__inner .ti1", {opacity: 1, y: 0, duration: 0.5, ease: Circ.easeOut}, "shin +=0.5")
        tl.to(".text__inner .ti3", {opacity: 1, y: 0, duration: 0.5, ease: Circ.easeOut}, "shin +=0.5")
        tl.to(".img__inner .ii1", {opacity: 0.8, duration: 0.5, rotation: 0, scale: 1})
        tl.to(".img__inner .ii2", {opacity: 0.8, duration: 0.5, rotation: -30, scale: 1})
        tl.to(".img__inner .ii3", {opacity: 0.8, duration: 0.5, rotation: 30, scale: 1})
        tl.to("#header", {y: 0, duration: 0.5}, "dong +=0.5")
        tl.to("#footer", {bottom: 0, duration: 0.5}, "dong +=0.5")
        tl.to("#webgl", {opacity: 1, duration: 1})
    }, 3000);
</script>
  • 콜백 함수 지옥을 해결하기 위해 GSAP 라이브러리를 사용해봤습니다.
  • 먼저 GSAP 라이브러리 cdn 링크를 가져옵니다. (https://greensock.com/)
  • <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.11.5/gsap.min.js"></script>유형으로 가져옵니다.
  • GSAP 라이브러리 스타일 속성을 궁금하면 홈페이지에서 확인이 가능합니다.(https://greensock.com/get-started/)
  • 먼저 태그 부분의 스타일을 기본 세팅을 하기 위해서는 gsap.set(".class명", {속성: 값}) 형식으로 입력합니다.
  • 애니메이션 스타일 속성을 주기 위해서는 gsap.to(".class명", {속성: 값}) 형식으로 입력합니다.
  • translateX를 x로 간단하게 translateY를 y로 간단하게 입력이 가능하고 transform 속성을 굳이 입력하지 않고 바로 입력이 가능합니다. 그리고 transition을 입력하지 않아도 애니메이션 효과가 자연스럽게 나타납니다.

 

위와 같이 자바스크립트에서 함수를 일일이 주는 것보다 훨씬 더 간단하게 사용이 가능하고 가독성이 좋고 불편하지가 않습니다. 가끔 이러한 라이브러리 함수를 사용해서 script를 손쉽게 사용해주는 것도 좋습니다. 이렇게 다양한 방법을 사용하면서 훨씬 더 좋은 방법이 무엇인지 그 상황에 맞게 사용할 수 있을 것입니다.

 

728x90
반응형