๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
์‹œ๋„/๊ตญ๋น„์ง€์›

[D+49] Front-end ๋ฏธ๋‹ˆํ”„๋กœ์ ํŠธ 5์ผ์ฐจ : ์Šคํฌ๋กค ์• ๋‹ˆ๋ฉ”์ด์…˜ ๊ตฌ์ฒดํ™”

by ๐Ÿ‡๋ฐ•๋ด‰๋ด‰๐Ÿ‡ 2022. 11. 30.

 

๊ตญ๋น„์ง€์› D+49

Front-end ๋ฏธ๋‹ˆํ”„๋กœ์ ํŠธ 5์ผ์ฐจ

- ์Šคํฌ๋กค ์• ๋‹ˆ๋ฉ”์ด์…˜ ๊ตฌ์ฒดํ™” -

 


 

์˜ค๋Š˜ ํ•œ ์ผ

- section0์— ๋Œ€ํ•œ ์Šคํฌ๋กค ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ถ”๊ฐ€(๊ตฌ์ฒดํ™”) ๋ฐ ํ…Œ์ŠคํŠธ

 

ํŒŒ์ผ ๊ตฌ์„ฑ ๋ฐ ๋‚ด์šฉ

 


• index.html

- ๋ฉ”์‹œ์ง€ 4๊ฐœ์˜ ์Šคํฌ๋กค ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ๋™์ž‘์‹œํ‚ค๊ธฐ ์œ„ํ•ด ์ฃผ์„์„ ํ•ด์ œํ•ด ์ฃผ์—ˆ๋‹ค.

<section id="section-0" class="scroll-section">
    <div class="section0-product-name">
      <h1 class="product-name">Real Apple Pro</h1>
    </div>
    <div class="sticky-element section0-message a">
      <p>์˜จ์ „ํžˆ ๋น ์ ธ๋“ค๊ฒŒ ํ•˜๋Š” ๋น›๊น”</p>
    </div>
    <div class="sticky-element section0-message b">
      <p>๋‚˜์˜ ๊ฑด๊ฐ•์„ ์ฑ„์›Œ์ฃผ๋Š” ํ–ฅ๊ธฐ</p>
    </div>
    <div class="sticky-element section0-message c">
      <p>๊นจ๋—ํ•œ ์ž์—ฐ์˜ ์‹ ์„ ํ•จ</p>
    </div>
    <div class="sticky-element section0-message d">
      <p>์ƒˆ๋กญ๊ฒŒ ์ž…๊ฐ€๋ฅผ ์ฐพ์•„์˜จ ๋งคํ˜น</p>
    </div>
</section>

 

• main.js

- ์Šคํฌ๋กค์ด๋ฒคํŠธ๋ฅผ ์ข€๋” ๊ตฌ์ฒดํ™”ํ•˜๊ธฐ ์œ„ํ•ด์„œ ๊ธฐ์กด ํ•จ์ˆ˜๋ฅผ ์ข€ ๋” ๊ตฌ์ฒดํ™” ํ•ด์ฃผ์—ˆ๋‹ค.

(() => {
    
    let yOffset = 0;                // ์Šคํฌ๋กค ์œ„์น˜๊ฐ’
    let currentSection = 0;         // ํ˜„์žฌ ์„น์…˜ ๋ฒˆํ˜ธ
    
    // ํ•„์š”ํ•œ ์ด์œ  - yOffset์ด๋ผ๋Š”๊ฐ’์„ ๊ฐ€์ง€๊ณ  ๊ฐ ์„น์…˜์— ๋Œ€ํ•œ ๋น„์œจ์„ ๊ณ„์‚ฐํ•˜๋Š”๋ฐ ์–ด๋ ค์›€
    // ์„น์…˜์˜ ๋น„์œจ๊ฐ’์— ๋”ฐ๋ฅธ CSS๊ฐ’์„ ํŽธํ•˜๊ฒŒ ๊ณ„์‚ฐํ•˜๊ธฐ ์œ„ํ•ด์„œ sectionYOffset์„ ๋งŒ๋“ค์—ˆ๋‹ค.
    let sectionYOffset = 0;         

    // section ์ •๋ณด ์ง‘ํ•ฉ
    const sectionSet = [
        // section-0์˜ ์ •๋ณด๋“ค - ๋†’์ด, ๋ฐฐ์ˆ˜, ์—˜๋ฆฌ๋จผํŠธ์ •๋ณด
        {
            height : 0,
            hMultiple : 5,
            objs : {
                container : document.querySelector('#section-0'),
                messageA : document.querySelector('.section0-message.a'),
                messageB : document.querySelector('.section0-message.b'),
                messageC : document.querySelector('.section0-message.c'),
                messageD : document.querySelector('.section0-message.d'),
            },
            // vals : message์— ๋Œ€ํ•œ ํˆฌ๋ช…๋„์™€ translateY์— ๋Œ€ํ•œ ์ •๋ณด๊ฐ’
            vals : {
                messageA_opacity_out : [0, 1, {start: 0.05, end: 0.14}],
                messageA_opacity_in : [1, 0, {start:0.15, end: 0.24}],
                messageA_translateY_out : [0, -40, {start: 0.05, end: 0.14}],
                messageA_translateY_in : [-40, -80, {start:0.15, end: 0.24}],

                messageB_opacity_out : [0, 1, {start: 0.25, end: 0.34}],
                messageB_opacity_in : [1, 0, {start:0.35, end: 0.44}],
                messageB_translateY_out : [0, -40, {start: 0.25, end: 0.34}],
                messageB_translateY_in : [-40, -80, {start:0.35, end: 0.44}],

                messageC_opacity_out : [0, 1, {start: 0.45, end: 0.54}],
                messageC_opacity_in : [1, 0, {start:0.55, end: 0.64}],
                messageC_translateY_out : [0, -40, {start: 0.45, end: 0.54}],
                messageC_translateY_in : [-40, -80, {start:0.55, end: 0.64}],

                messageD_opacity_out : [0, 1, {start: 0.65, end: 0.74}],
                messageD_opacity_in : [1, 0, {start:0.75, end: 0.84}],
                messageD_translateY_out : [0, -40, {start: 0.65, end: 0.74}],
                messageD_translateY_in : [-40, -80, {start:0.75, end: 0.84}]
            }
        },
        // section-1์˜ ์ •๋ณด๋“ค
        {
            height : 0,
            hMultiple : 2,
            objs : {
                container : document.querySelector('#section-1')
            }
        }
    ];

////////////////////////////////////////////////////////
// ์ผ๋ฐ˜ํ•จ์ˆ˜

// Element์˜ ํฌ๊ธฐ, ์œ„์น˜๋“ฑ์„ ์„ค์ •
const setLayout = function()
{
    // section-0๊ณผ section-1์˜ ๋†’์ด๋ฅผ ์„ค์ •ํ•œ๋‹ค.
    for(let i = 0; i < sectionSet.length; i++)
    {
        sectionSet[i].height = window.innerHeight * sectionSet[i].hMultiple;
        sectionSet[i].objs.container.style.height = `${sectionSet[i].height}px`;
    }

}

const getCurrentSection = function()
{
    let section = 0;

    let segment = [
        sectionSet[0].height,
        sectionSet[0].height + sectionSet[1].height
    ];

    if(yOffset <= segment[0])
    {
        section = 0;
    }
    else if((yOffset > segment[0]) &&
            (yOffset <= segment[1]))
    {
        section = 1;
    }
    else 
    {
    }
    return section;
}

const setBodyID = function(section)
{
    document.body.setAttribute('id', `show-section${section}`);
}

// ํ˜„์žฌ ์„น์…˜์˜ ์œ„์— ์žˆ๋Š” ์„น์…˜์˜ ๋†’์ด ํ•ฉ.
const getPrevSectionHeight = function()
{
    // ํ˜„์žฌ ์„น์…˜์ด 0 ==> 0
    // ํ˜„์žฌ ์„น์…˜ 1 ==> section0์˜ ๋†’์ด
    // ํ˜„์žฌ ์„น์…˜ 2 ==> section0์˜ ๋†’์ด + section1์˜ ๋†’์ด
    let prevHeight = 0;

    for(let i = 0; i < currentSection; i++)
    {
        prevHeight = prevHeight + sectionSet[i].height;
    }

    return prevHeight;
}

// messageA_opacity_out : [0, 1, {start: 0.05, end: 0.14}]
const calcValue = function(values)
{
    let result = 0;
    let rate = 0;
    const height = sectionSet[currentSection].height;

    let partStart;  // start ์Šคํฌ๋กค๊ฐ’
    let partEnd;    // end ์Šคํฌ๋กค๊ฐ’ 
    let partHeihgt; // end - start

    if(values.length === 2)
    {
        // 1. ์Šคํฌ๋กค ๋น„์œจ์„ ๊ตฌํ•œ๋‹ค.
        rate = sectionYOffset / height;

         // 2. ๋น„์œจ์— ๋”ฐ๋ฅธ ์‹ค์ œ ์ ์šฉํ•œ CSS๊ฐ’์„ ๊ณ„์‚ฐํ•œ๋‹ค.
        result = (rate * (values[1] - values[0])) + values[0];
    }

    else if(values.length === 3)
    {
        // step01) ๋ชจ๋“  ๊ฐ’๋“ค์„ ๋น„์œจ์ด ์•„๋‹Œ ์‹ค์ œํ™” ํ•œ๋‹ค.
        // -- ์‹œ์ž‘์œ„์น˜์˜ ์‹ค์ œ ์Šคํฌ๋กค๊ฐ’
        partStart =  values[2].start * height;  

        // -- ๋๋‚˜๋Š” ์œ„์น˜์˜ ์‹ค์ œ ์Šคํฌ๋กค๊ฐ’
        partEnd = values[2].end * height;

        // -- ์‹ค์ œ ๋†’์ด๊ฐ’
        partHeihgt = partEnd - partStart;

        // ์„ค์ •ํ•œ ๋ฒ”์œ„๊ฐ’์„ ๋ฒ—์–ด๋‚œ ๊ฐ’์„ 0๋˜๋Š” 1๋กœ ์„ค์ •ํ•ด์ค€๋‹ค.
        //-- ์Šคํฌ๋กค ๊ฐ’์ด ์‹œ์ž‘์œ„์น˜ ์ด์ „์ธ ๊ฒฝ์šฐ values[0]์„ ํ™•์žฅํ•ด์„œ ์ ์šฉ
        if(sectionYOffset < partStart)
        {
            result = values[0];
        }
        //-- ์Šคํฌ๋กค ๊ฐ’์ด ๋๋‚˜๋Š”๊ฐ’ ์ดํ›„์ธ ๊ฒฝ์šฐ values[1]์„ ํ™•์žฅํ•ด์„œ ์ ์šฉ
        else if(sectionYOffset > partEnd)
        {
            result = values[1];
        }
        else
        {
            // step02) sectionYOffset์—์„œ partStart๊ฐ’์„ ๋นผ๋‚ด์–ด ์‹ค์ œ ๋ฒ”์œ„๋‚ด์˜ ํฌ๊ธฐ๋ฅผ ๊ตฌํ•˜๊ณ 
            // -- ๋น„์œจ์„ ๊ตฌํ•œ๋‹ค. ๋ถ€๋ถ„์˜์—ญ์—์„œ ์–ผ๋งˆํผ ์ง„ํ–‰ํ–ˆ๋Š”์ง€ ๋น„์œจ์„ ๊ตฌํ•œ๋‹ค.
            rate = (sectionYOffset - partStart) / partHeihgt;

            // step03) ๊ฐ’์„ cssํ™” ํ•˜์—ฌ result์•ˆ์— ๋„ฃ์–ด์ค€๋‹ค.
            result = (rate * (values[1] - values[0])) + values[0];
        }
    }

    return result;
}

const playAnimation = function()
{
    let value;

    let scrollRate = sectionYOffset / sectionSet[currentSection].height;
    let objects = sectionSet[currentSection].objs;
    let values = sectionSet[currentSection].vals;

    switch(currentSection)
    {
        case 0:
            // message์— ๋Œ€ํ•œ ํˆฌ๋ช…๋„ ๊ฐ’ ์ดˆ๊ธฐํ™”
            objects.messageA.style.opacity = 0;
            objects.messageB.style.opacity = 0;
            objects.messageC.style.opacity = 0;
            objects.messageD.style.opacity = 0;
            
            if(scrollRate < 0.15)   // messageA_out
            {
                value = calcValue(values.messageA_opacity_out);
                objects.messageA.style.opacity = value;

                value = calcValue(values.messageA_translateY_out);
                objects.messageA.style.transform = `translateY(${value}%)`;

            }
            else if((scrollRate >= 0.15) && (scrollRate < 0.25))    // messageA_in
            {
                value = calcValue(values.messageA_opacity_in);
                objects.messageA.style.opacity = value;

                value = calcValue(values.messageA_translateY_in);
                objects.messageA.style.transform = `translateY(${value}%)`;
            }
            else if((scrollRate >= 0.25) && (scrollRate < 0.35))    // messageB_out
            {
                value = calcValue(values.messageB_opacity_out);
                objects.messageB.style.opacity = value;

                value = calcValue(values.messageB_translateY_out);
                objects.messageB.style.transform = `translateY(${value}%)`;
            }
            else if((scrollRate >= 0.35) && (scrollRate < 0.45))    // messageB_in
            {
                value = calcValue(values.messageB_opacity_in);
                objects.messageB.style.opacity = value;

                value = calcValue(values.messageB_translateY_in);
                objects.messageB.style.transform = `translateY(${value}%)`;
            }
            else if((scrollRate >= 0.45) && (scrollRate < 0.55))    // messageC_out
            {
                value = calcValue(values.messageC_opacity_out);
                objects.messageC.style.opacity = value;

                value = calcValue(values.messageC_translateY_out);
                objects.messageC.style.transform = `translateY(${value}%)`;
            }
            else if((scrollRate >= 0.55) && (scrollRate < 0.65))    // messageC_in
            {
                value = calcValue(values.messageC_opacity_in);
                objects.messageC.style.opacity = value;

                value = calcValue(values.messageC_translateY_in);
                objects.messageC.style.transform = `translateY(${value}%)`;
            }
            else if((scrollRate >= 0.65) && (scrollRate < 0.75))    // messageD_out
            {
                value = calcValue(values.messageD_opacity_out);
                objects.messageD.style.opacity = value;

                value = calcValue(values.messageD_translateY_out);
                objects.messageD.style.transform = `translateY(${value}%)`;
            }
            else if((scrollRate >= 0.75) && (scrollRate < 0.85))    // messageD_in
            {
                value = calcValue(values.messageD_opacity_in);
                objects.messageD.style.opacity = value;

                value = calcValue(values.messageD_translateY_in);
                objects.messageD.style.transform = `translateY(${value}%)`;
            }
            else
            {
                objects.messageA.style.opacity = 0;
                objects.messageB.style.opacity = 0;
                objects.messageC.style.opacity = 0;
                objects.messageD.style.opacity = 0;

                objects.messageA.style.transform = 'translateY(0%)';
                objects.messageB.style.transform = 'translateY(0%)';
                objects.messageC.style.transform = 'translateY(0%)';
                objects.messageD.style.transform = 'translateY(0%)';
            }

            break;
        
        case 1:
            
            break;
        
        default:
            break;
        
        
    }
}

////////////////////////////////////////////////////////
// ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ

    // ์Šคํฌ๋กค์€ system์ด๊ธฐ ๋•Œ๋ฌธ์— ์ƒ์œ„ window์— ์ด๋ฒคํŠธ์ถ”๊ฐ€
    window.addEventListener('scroll', () => {
        yOffset = window.scrollY;

        // ํ˜„์žฌ ์„น์…˜๊ฐ’ ๋ฐ ์„น์…˜๋‚ด์—์„œ์˜ yoffset๊ฐ’์„ ๊ตฌํ•œ๋‹ค.
        currentSection = getCurrentSection();       // ์Šคํฌ๋กคํ• ๋•Œ๋งˆ๋‹ค ์„น์…˜์œ„์น˜๋ฅผ ์—…๋ฐ์ดํŠธ
        sectionYOffset = yOffset - getPrevSectionHeight();


        setBodyID(currentSection);  // currentSection์„ ํ†ตํ•ด ๋ฐ›์•„์˜จ ์„น์…˜ ์œ„์น˜๋ฅผ ํ†ตํ•ด body์˜ id๊ฐ’ ์ƒˆ๋กœ๊ณ ์นจ

        playAnimation();
    });

    // load : ๋ชจ๋“  ์ž‘์—…์ด ๋๋‚ฌ๋‹ค.
    window.addEventListener('load', () => {
        // ์„น์…˜์— ๋Œ€ํ•œ ๋†’์ด๋ฅผ ์„ค์ •ํ•œ๋‹ค.
        setLayout();
    })



})();

 


๊ฒฐ๊ณผํ™”๋ฉด

 

 

๋ฐ˜์‘ํ˜•

๋Œ“๊ธ€