JAVASCRIPT

포트폴리오 애니메이션 만들기

Kim do hyun 2023. 5. 7. 22:31
728x90
반응형

포트폴리오 애니메이션 

 

 

포트폴리오 제작을 위해 만들어본 첫번째 애니메이션 효과입니다. gsap를 이용하여 좀 더 쉽게 효과를 만들었습니다.

 

 

 

HTML

<!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%;
            display: flex;
            align-items: end;
            justify-content: space-between;
            padding: 10px 20px;
            font-family: 'Abel';
            z-index: 1000;
        }
        #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;
        }
        .text__inner > div.ti1 {
            font-family: 'PPNeueWorld-CondensedRegular';
        }
        .text__inner > div.ti1 em {
            font-family: 'PPNeueWorld-ExtendedThin';
        }
        .text__inner > div.ti2 {
            font-family: 'PPNeueWorld-Regular';
        }
        .text__inner > div.ti3 {
            font-family: 'PPNeueWorld-ExtendedThin';
        }
        .text__inner > div.ti3 em {
            font-family: 'PPNeueWorld-Regular';
        }
        .img__inner > img {
            position: absolute;
            width: 10vw;
            z-index: 2000;
        }
        .img__inner .ii1 {
            left: 45%;
            top: 50%;
            transform: translateY(-180%);
        }
        .img__inner .ii2 {
            left: 10%;
            top: 50%;

        }
        .img__inner .ii3 {
            left: 80%;
            top: 50%;
            transform: translateY(-30%)
        }
        #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>Dohyun'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://qqw2.tistory.com/" target="_blank">blog.link</a>
    </footer>
    <!-- footer -->
</body>
</html>

 

스크립트

<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, scale: 3, y: "-200%"});
        gsap.set(".img__inner .ii2", {opacity: 0, scale: 3});
        gsap.set(".img__inner .ii3", {opacity: 0, scale: 3});
        gsap.set("#header", {duration: 0.5, opacity: 0});
        gsap.set("#footer", {duration: 0.5, opacity: 0});
        gsap.set("#webgl", {duration: 0.5, opacity: 0});
        
        
        // 메인 애니메이션
        setTimeout(() => {
            let tl = gsap.timeline();

            tl.to(".text__inner .ti2 span", {opacity: 1, x: 0, scale: 1, rotation: 0, duration: 0.6, stagger: 0.1, ease: Power3.easeInOut})
            tl.to(".text__inner .ti1", {opacity: 1, y: 0, duration: 0.6, ease: Circ.easeOut}, "kim +=0.5")
            tl.to(".text__inner .ti3", {opacity: 1, y: 0, duration: 0.6, ease: Circ.easeOut}, "kim")
            tl.to(".img__inner .ii1", {duration: 0.5, opacity: 1, scale: 1, y: "-180%"})
            tl.to(".img__inner .ii2", {duration: 0.5, opacity: 1, scale: 1})
            tl.to(".img__inner .ii3", {duration: 0.5, opacity: 1, scale: 1})
            tl.to("#header", {duration: 0.5, opacity: 1})
            tl.to("footer", {duration: 0.5, opacity: 1})
            tl.to("#webgl", {duration: 0.5, opacity: 1})
        }, 1000);
    </script>

코드 설명

먼저, 이 코드는 https://cdnjs.cloudflare.com/ajax/libs/gsap/3.11.5/gsap.min.js 경로에서 GSAP 라이브러리를 가져와서 사용합니다.

이 코드에서는 querySelectorAll() 메서드를 사용하여 .split 클래스를 가진 모든 요소에 대해 반복적으로 작업을 수행합니다. 이 작업은 텍스트를 각 글자별로 분리하여 스팬 태그로 묶어서 시각적으로 효과를 주는 것입니다.

그 다음으로, 페이지 로딩 후 1초 후에 실행되는 setTimeout() 메서드 내부에서는 GSAP 라이브러리를 사용하여 웹페이지의 다양한 요소들에 대한 애니메이션 효과를 설정합니다. 예를 들어, gsap.set() 메서드를 사용하여 초기 CSS 스타일을 설정하고, gsap.timeline() 메서드를 사용하여 각각의 애니메이션을 순차적으로 실행할 타임라인을 만듭니다.

그리고 to() 메서드를 사용하여 요소의 스타일 속성들이 어떻게 변화할지를 정의합니다. 이 메서드는 대상 요소, 변화할 속성들, 애니메이션 시간, easing, delay 등 다양한 옵션을 지원합니다.

즉, 이 코드는 GSAP 라이브러리를 사용하여 웹페이지를 좀 더 생동감 있게 만드는 애니메이션 효과를 구현하는 것입니다.