[HYBE]

๋…•์ดยท2022๋…„ 11์›” 2์ผ
1

Portfolio

๋ชฉ๋ก ๋ณด๊ธฐ
3/4
post-thumbnail

๐Ÿ’ป HYBE coding

์‚ฌ์ดํŠธ๋ช… : HYBE
์‚ฌ์šฉ์–ธ์–ด : html / scss / js
๋ถ„๋ฅ˜ : ์ ์‘ํ˜• PC / ๋ฐ˜์‘ํ˜•

๐ŸŽˆ HYBE HOMEPAGE
https://hybecorp.com/kor/company/artist

Check Point

  • loading-page
  • text-animation
  • gsap - scrollTrigger

1. loading-page

๐ŸŽˆ ๊ธฐ๋ณธ์›๋ฆฌ
: gsapํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์‚ฌ์šฉํ•˜์—ฌ, ์ˆœ์ฐจ์ ์ธ ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ๊ตฌํ˜„ํ•จ

<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.11.2/gsap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.11.2/ScrollTrigger.min.js"></script>

โœ๐Ÿป ์ฝ”๋“œ์ž‘์„ฑ

<div id="loading">
  <div class="loading-txt">
      <div class="wrap">
        <span class="first">H</span>
        <span class="sec">Y</span>
        <span class="sec">B</span>
        <span class="sec">E</span>
      </div>
  </div>
</div>

  1. text๊ฐ€ ์ˆœ์ฐจ์ ์œผ๋กœ ๋‚˜์™€์•ผํ•˜๊ธฐ ๋•Œ๋ฌธ์—, wrap์œผ๋กœ ์ „์ฒด๋ฅผ ๋ฌถ์–ด ์œ„์น˜๋ฅผ ์žก์•„์ฃผ๊ณ  class๋ช…์„ ๋‹ค๋ฅด๊ฒŒ ์ฃผ์—ˆ๋‹ค.
.wrap{
        overflow: hidden;
        padding: 0 10px;
        transform: translateX(20%);
    }
    span{
        font-size: 15vw; 
        font-weight: 900;
        color: $color-main;
        display: inline-block;
        transform: translateY(100%);
    }
  1. gsap.timeline์ ์šฉ
    : ๋กœ๋”ฉํŽ˜์ด์ง€ ์ด๋ฒคํŠธ ํ›„, ์‚ฌ๋ผ์ง€๋ฉด์„œ ์•„๋ž˜์˜ visual์ด ๋‚˜ํƒ€๋‚จ

โœ๐Ÿป ์ฝ”๋“œ์ž‘์„ฑ

const intro = gsap.timeline({ 
        defaults:{
        },
        onComplete:function(){ ๐Ÿ‘ˆ๐Ÿป ์™„๋ฃŒ๋œํ›„์—
            scaleIntro.play();
        }
    })
    intro
    .addLabel('a')
    .to('#loading .first',1,{yPercent: -95},'a')
    .to('#loading .sec',1,{yPercent: -95, stagger:0.1},'a+=0.2')
    .to('#loading .wrap',0.5,{xPercent:-20},'a+=0.5')
    .to('#loading',1,{height:0})

    const scaleIntro = gsap.from('.sc-visual .visual-bg img',0.5,{
        scale:1.2,
        paused:true,
    })

โœ” gsap.timeline : gsap๋ฅผ ํ•œ๊บผ๋ฒˆ์— ๋ฌถ์–ด ์ˆœ์ฐจ์ ์œผ๋กœ ์‹คํ–‰ํ•˜๋Š” ๋ฉ”์„œ๋“œ
โœ” addLabel : gsap๋ฅผ ๊ทธ๋ฃน์œผ๋กœ ๋ฌถ์–ด, ๋™์‹œ์— ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ ์šฉํ•˜๋Š” ๋ฉ”์„œ๋“œ

2. text-animation

๐ŸŽˆ ๊ธฐ๋ณธ์›๋ฆฌ
: ์•„๋ž˜๋™์˜์ƒ์„ ์ฐธ๊ณ ํ•˜์—ฌ ์ฝ”๋“œ์ ์šฉ

๐Ÿ‘‡๐Ÿป ๊ตฌํ˜„ํ•ด์•ผ๋˜๋Š” ๋ชจ์Šต
: text-in๊ณผ text-out์œผ๋กœ ์ปจํŠธ๋กคํ•˜์—ฌ ๊ณ„์† ๋กค๋ง๋˜๊ฒŒ ํ•˜์—ฌ์•ผํ•จ

โœ๐Ÿป ์ฝ”๋“œ์ž‘์„ฑ

<div class="visual-content">
   <h2 class="title">
       <div class="title-list">
          <span class="title-item">BTS</span>
          <span class="title-item">LEE HYUN</span>
          <span class="title-item">LE SSERAFIM</span>
          <span class="title-item">NewJeans</span>
          <span class="title-item">SEVENTEEN</span>
          <span class="title-item">ENHYPEN</span>
          <span class="title-item">TXT</span>
       </div>
       HYBE ARTIST
   </h2>                
</div>
  1. ๋ณ€์ˆ˜์„ ์–ธ
const txts = document.querySelector('.title-list').children;
txtsLen = txts.length;
let index = 0;
  1. for๋ฐ˜๋ณต๋ฌธ('text-in', 'text-out')
function aniMate1() {
    for (let i = 0; i < txtsLen; i++) {
        txts[i].classList.remove('text-in', 'text-out');
    }
    txts[index].classList.add('text-in');
}
function aniMate2() {
    txts[index-1].classList.add('text-out');
}
  1. setInterval() ์ ์šฉ
    โœ๐Ÿป setTimeout / setInterval ์ฐจ์ด์ 
    setTimeout() : ์ฝ”๋“œ๋ฅผ ๋ฐ”๋กœ ์‹คํ–‰ํ•˜์ง€ ์•Š๊ณ  ์ผ์ • ์‹œ๊ฐ„ ๊ธฐ๋‹ค๋ฆฐ ํ›„ ์‹คํ–‰ํ•ด์•ผํ•˜๋Š” ๊ฒฝ์šฐ์— ์‚ฌ์šฉ
    setInterval() : ์ฝ”๋“œ๋ฅผ ์ผ์ •ํ•œ ์‹œ๊ฐ„ ๊ฐ„๊ฒฉ์„ ๋‘๊ณ  ๋ฐ˜๋ณตํ•ด์„œ ์‹คํ–‰ํ•˜๊ณ  ์‹ถ์„ ๋•Œ ์‚ฌ์šฉ
setInterval(function () {
    aniMate2()
}, 3500);

setInterval(function () {
    if (index == txtsLen - 1) {
        index = 0;
    }
    else {
        index++;
    }
    aniMate1()
}, 3000);

3. gsap - scrollTrigger

๐ŸŽˆ ๊ธฐ๋ณธ๋ฌธ๋ฒ•
: ์–ด๋–ค ์ง€์ ์— ๋„๋‹ฌํ•˜๋ฉด ์Šคํฌ๋กค ํ•˜๋”๋ผ๋„ ๋ฐฐ๊ฒฝ์€ ์œ ์ง€๋œ ์ฑ„, ์ˆ˜ํ‰ ๋ฐฉํ–ฅ์—์„œ ์š”์†Œ๊ฐ€ ๋“ฑ์žฅํ•˜๋Š” ํšจ๊ณผ๊ฐ€ ๋ชฉ์ 

scrollTrigger:{
	trigger:"๊ธฐ์ค€์ด๋ ํƒœ๊ทธ",
	start:"top top", ๐Ÿ‘ˆ๐Ÿป ๊ธฐ์ค€ํƒœ๊ทธ์˜ ์‹œ์ž‘์ง€์ , ์œˆ๋„์šฐ๊ธฐ์ค€ ์‹œ์ž‘์ง€์  
	end:"bottom top" ๐Ÿ‘ˆ๐Ÿป ๊ธฐ์ค€ํƒœ๊ทธ์˜ ๋์ง€์ , ์œˆ๋„์šฐ๊ธฐ์ค€ ๋์ง€์ 
	markers:true, ๐Ÿ‘ˆ๐Ÿป ์ธ๋””์ผ€์ดํ„ฐ๋ฅผ ํ‘œ์‹œ
	scrub:1 ๐Ÿ‘ˆ๐Ÿป ๋˜๊ฐ๊ธฐ(์Šคํฌ๋กค์ด ์š”์†Œ ์ด์ „์œผ๋กœ ๋Œ์•„๊ฐ€๋ฉด ์• ๋‹ˆ๋ฉ”์ด์…˜ ์—ญ์‹œ ๋˜๋Œ์•„๊ฐ€๋Š” ๊ธฐ๋Šฅ) 
}

๐Ÿ‘‡๐Ÿป ๊ตฌํ˜„ํ•ด์•ผ๋˜๋Š” ๋ชจ์Šต

  1. GSAP ScrollTrigger์˜ Pin๊ณผ timeline์„ ์‚ฌ์šฉํ•˜์—ฌ ํšจ๊ณผ๋ฅผ ๊ตฌํ˜„
    : ๋ฐฐ๊ฒฝ์„ ์œ ์ง€ํ•˜๋Š” ๊ฒƒ์€ pin, ์š”์†Œ๊ฐ€ ๋“ฑ์žฅํ•˜๋Š” ๊ฒƒ์€ timeline์œผ๋กœ ๊ตฌํ˜„
const viewSlide = gsap.timeline({
                defaults:{
                    ease:'none',
                },
                scrollTrigger:{
                    trigger:'.sc-view',
                    start:'top top',
                    end:'+=800%', ๐Ÿ‘ˆ๐Ÿป ๊ฐ’์ด ํด์ˆ˜๋ก ์Šคํฌ๋กค์ด ์ฒœ์ฒœํžˆ๋Œ
                    scrub:1,
                    markers:true, ๐Ÿ‘ˆ๐Ÿป ์‹œ์ž‘๊ณผ ๋์œ„์น˜ ํ™•์ธ
                    pin:true, ๐Ÿ‘ˆ๐Ÿป ๊ณ ์ •
                },
            })
  1. gsap.set({})๊ธฐ์ดˆ์…‹ํŒ…
gsap.set('.sc-view .img-item .item01',{ scale:1.3 })
            gsap.set('.sc-view .img-item .item02',{ rotate:30, })
            gsap.set('.sc-view .img-item .item03',{ rotate:-10,scale:0.8 })
            gsap.set('.sc-view .img-item .item05',{ rotate:-50,scale:1.5 })
            gsap.set('.sc-view .img-item .item05',{ scale:1.1 })
            gsap.set('.sc-view .img-item .item07',{ rotate:-120})
            gsap.set('.sc-view .img-item .item08',{ rotate:20,scale:1.2 })
            gsap.set('.sc-view .img-item .item09',{ rotate:45})
            gsap.set('.sc-view .img-item .img',{ yPercent:-50})
  1. .addLabel('a')๋กœ ๊ทธ๋ฃน์œผ๋กœ ํšจ๊ณผ์ ์šฉ
.addLabel('a')
            .to('.sc-view .title',{ xPercent:-20 },'a')
            .to('.sc-view .img-item',{ xPercent:-110 },'a')
            .to('.sc-view .img-item .img',{ yPercent:100, stagger:0.02 },'a')
            .to('.sc-view .img-item .item01',{ rotate:-90 },'a')
            .to('.sc-view .img-item .item02',{ rotate:0 },'a')
            .to('.sc-view .img-item .item03',{ rotate:0 },'a')
            .to('.sc-view .img-item .item04',{ rotate:60 },'a')
            .to('.sc-view .img-item .item05',{ rotate:0 },'a')
            .to('.sc-view .img-item .item06',{ rotate:30 },'a')
            .to('.sc-view .img-item .item07',{ rotate:0 },'a')
            .to('.sc-view .img-item .item08',{ rotate:0 },'a')
            .to('.sc-view .img-item .item09',{ rotate:0, left:'100px', bottom:'-150px' },'a+=.1')

๊ฐ์ฒด์˜ ์œ„์น˜๋ฅผ ์žก๋Š”๊ฒŒ point์˜€๋‹ค.

Issue Point

  • ์˜์—ญ์•ˆ์—์„œ mousemove/mouseout
  • GSAP ScrollTrigger - 3D Transforms

1. ์˜์—ญ์•ˆ์—์„œ mousemove/mouseout

๐ŸŽˆ ๊ธฐ๋ณธ์›๋ฆฌ

[์ฐธ๊ณ ์‚ฌ์ดํŠธ]
https://zzzmh.tistory.com/13

๐Ÿ‘‡๐Ÿป ๊ตฌํ˜„ํ•ด์•ผ๋˜๋Š” ๋ชจ์Šต

โœ๐Ÿป ์ฝ”๋“œ์ž‘์„ฑ

function menuOpen() {
    var target = document.querySelector('.menu');
    var motion = document.querySelector('.open-btn');
    target.addEventListener('mousemove', (event) => {
        let x = (event.layerX * 0.1);
        let y = (event.layerY * 0.1);

        motion.style.transform = `translate(${x}px,${y}px)`
    })
    target.addEventListener('mouseout', (event) => {
        motion.style.transform = '';
    })
}

โœ” MouseEvent.layerX์ฝ๊ธฐ ์ „์šฉ ์†์„ฑ: ํ˜„์žฌ ๋ ˆ์ด์–ด๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์ด๋ฒคํŠธ์˜ ์ˆ˜ํ‰ ์ขŒํ‘œ๋ฅผ ๋ฐ˜ํ™˜
โœ” MouseEvent.layerY์ฝ๊ธฐ ์ „์šฉ ์†์„ฑ: ํ˜„์žฌ ๋ ˆ์ด์–ด๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์ด๋ฒคํŠธ์˜ ์ˆ˜์ง ์ขŒํ‘œ๋ฅผ ๋ฐ˜ํ™˜

๐Ÿ‘‰๐Ÿป ๋งˆ์šฐ์Šค ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•  ๋•Œ ๋งˆ์šฐ์Šค ํฌ์ธํ„ฐ์˜ x ์ขŒํ‘œ์— ๋Œ€ํ•œ ์ •์ˆ˜ ๊ฐ’์ด ๋‚˜์˜ด

2. GSAP ScrollTrigger - 3D Transforms

๐ŸŽˆ ๊ธฐ๋ณธ์›๋ฆฌ
[์ฐธ๊ณ ์‚ฌ์ดํŠธ]
https://codepen.io/jonathan/pen/XXdNjR
๐Ÿ‘‡๐Ÿป ๊ตฌํ˜„๋œ ๋ชจ์Šต


1. transform-origin์†์„ฑ์ ์šฉ

<h2 class="title">
  <span class="effect"><span class="em-txt">HYBE</span> is committed to satisfying</span>
  <span class="effect">customers  with the 'content' leading the</span>
  <span class="effect"><span class="em-txt">global trend and our 'fans'</span></span>
</h2>
.title{
    transform-origin: top left;
}

<span class="effect"> ๐Ÿ‘ˆ๐Ÿป ํšจ๊ณผ๋ฅผ ์ฃผ๊ธฐ์œ„ํ•ด class๋ช…์„ ๋„ฃ์–ด์คŒ

[์ฐธ๊ณ ์‚ฌ์ดํŠธ]
https://developer.mozilla.org/en-US/docs/Web/CSS/transform-origin

  1. ์Šคํฌ๋ฆฝํŠธ ์ž‘์„ฑ
gsap.set(".sc-intro .effect", { ๐Ÿ‘ˆ๐Ÿป ๊ธฐ์ดˆ์…‹ํŒ…
        rotationX: -45,
        transformStyle: "preserve-3d",
        transformOrigin: "0% 50% -100%",
        opacity: 0,
        yPercent: 110,
    });

    const introEffect = gsap.to('.sc-intro .effect', {
        scrollTrigger:{
            trigger:'.sc-intro',
            start:'top 50%',
            end:'bottom top',
        },

        transformStyle: "preserve-3d",
        rotationX: 0,
        duration:1,
        opacity: 1,
        yPercent: 0,
        transformOrigin: "50% 50%",
        stagger:0.1,
    });
  1. css์— perspective(์›๊ทผ๊ฐ)์ถ”๊ฐ€
perspective: 400px;

โœ” ๋ถ€๋ชจ์—๊ฒŒ perspective ์†์„ฑ์„ ์ฃผ๊ณ , ์ด๋ฒคํŠธ ๋Œ€์ƒ์—๋Š” transform์„ ์‹œํ‚จ ๋ชจ์Šต
โœ” ์—ฌ๊ธฐ์—์„œ์˜ 400px์€ '๋‚ด๊ฐ€ ์–ผ๋งˆ๋‚˜ ๋–จ์–ด์ ธ์„œ ๋ณด๊ณ  ์žˆ๋Š”๊ฐ€'๋ฅผ ๋‚˜ํƒ€๋‚ธ๋‹ค๊ณ  ํ•  ์ˆ˜ ์žˆ์Œ
โœ” ๊ฐ’์ด ์ž‘์œผ๋ฉด ์ž‘์„ ์ˆ˜๋ก ๋” ๊ฐ€๊นŒ์ด์—์„œ ๋ณด๋Š” ๊ฒƒ์œผ๋กœ ์ฒ˜๋ฆฌ
[์ฐธ๊ณ ์‚ฌ์ดํŠธ]
https://nykim.work/26

profile
ๅ…‰่€Œไธ่€€ :) ํผ๋ธ”๋ฆฌ์‹ฑ-ing

0๊ฐœ์˜ ๋Œ“๊ธ€