position: sticky;로 스크롤 이펙트 만들기

wkdgusrhkd·2021년 2월 27일
0

Mini

목록 보기
2/3
post-thumbnail

이번에 작업한 것은 position: sticky;를 활용하여 스크롤 애니메이션을 만드는 것입니다.
추상적으로 떠돌던 아이디어를 유튜브 ✨1분코딩✨님의 영상을 보고 제가 찾던 것이라는 생각이 들어 참고하여 공부해봤습니다🎉.

첫 단계

html

<h1>position: sticky;를 이용한 스크롤 애니메이션</h1>
<h2>유튜브 '1분코딩'님을 참고하여 만들었습니다.</h2>

<div class="scroll-area">
    <div class="scroll-img">
        <div class="img1">1</div>
        <div class="img2">2</div>
        <div class="img3">3</div>
        <div class="img4">4</div>
        <div class="img5">5</div>
        <div class="img6">6</div>
        <div class="img7">7</div>
        <div class="img8">8</div>
    </div>
    <div class="scroll-txt">
        <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Perspiciatis officiis, modi molestias optio autem repellat facere maiores, sunt libero odio expedita alias itaque rerum inventore quam voluptatum voluptate sequi impedit?</p>
        <p>Voluptatem commodi doloremque incidunt odio neque, nisi blanditiis ipsam voluptas quis necessitatibus. Nam, vel aliquam fugit, cum ad aperiam sint quisquam id obcaecati veritatis dolores similique, non neque dignissimos rerum.</p>
        <p>Quo in suscipit ipsam cupiditate perferendis facilis magnam voluptate unde, quos impedit voluptates incidunt architecto vel nisi alias? Consequuntur culpa neque ratione veritatis nihil maxime sequi est! Eius, nam voluptatum.</p>
        <p>Et dolor facere beatae veritatis. Corporis, perferendis et? Ab id accusantium adipisci neque beatae eveniet delectus laboriosam reprehenderit magni, accusamus architecto commodi non sapiente labore dignissimos mollitia excepturi. Dolorum, possimus.</p>
        <p>Similique mollitia aliquam vel atque, quo aliquid voluptate quos praesentium expedita in cum illo, aspernatur perferendis. Impedit aliquid inventore totam mollitia at quas tenetur tempore fuga, illo asperiores delectus harum!</p>
        <p>Earum, veniam repellendus in officia, placeat id deleniti culpa enim corporis repudiandae quos facere ab ipsum, iste vero? Quos, totam nobis. Unde, perferendis sit est esse suscipit cupiditate dicta recusandae?</p>
        <p>Magni eveniet rem, at nostrum blanditiis eum eos quo ratione. Cupiditate, rem dolorum! Laborum iusto expedita ducimus dolor repellat cumque facere alias inventore vitae. Hic nam voluptates ut pariatur? Maxime.</p>
        <p>Deserunt modi suscipit delectus ipsam eius dolore maxime cumque quaerat! Odit commodi incidunt vel impedit quibusdam, magnam voluptatibus labore eveniet rem? Quod architecto quia quas beatae cum id praesentium modi!</p>
    </div>
</div>
    
<div class="footer">'1분코딩'님의 영상을 참고하시면 더 좋은 설명이 나와 있습니다.</div>
<div class="footer2">감사합니다 :D</div>

css

*{margin: 0; padding: 0; box-sizing: border-box; font-family: sans-serif;}

h1{padding: 32px; background-color: palegoldenrod; font-size: 48px; font-weight: 700; color: #2d2d2d; text-align: center;}
h2{padding: 16px; background-color: palegreen; font-size: 32px; color: #2d2d2d; text-align: center;}

.scroll-area{}
.scroll-img{}
.scroll-img div{width: 100%; padding: 16px 0 0 16px; font-size: 48px; color: #fff;}
.scroll-img .img1{background-color: #e77b6f;}
.scroll-img .img2{background-color: #e4a36b;}
.scroll-img .img3{background-color: #f1dd8d;}
.scroll-img .img4{background-color: #6dca94;}
.scroll-img .img5{background-color: #6bacd8;}
.scroll-img .img6{background-color: #878ad8;}
.scroll-img .img7{background-color: #a57bb6;}
.scroll-img .img8{background-color: #4d5964;}

.scroll-txt{}
.scroll-txt p{max-width: 640px; padding: 8px; background-color: rgba(149, 165, 166, .5); border: 1px solid #2d2d2d; border-radius: 5px; font-size: 18px; color: #fff;}

.footer{padding: 32px 0;background-color: cornflowerblue; font-size: 48px; font-weight: 600; color: #2d2d2d; text-align: center;}
.footer2{padding: 16px 0; background-color: darkkhaki; font-size: 32px; font-weight: 600; color: #2d2d2d; text-align: center;}

작업을 위한 기본적인 디자인입니다.

두 번째

1. .scroll-img

제가 원하는 것은 txt박스가 올라가는 동안 1~8의 번호를 매겨놓은 컬러박스들이 배경으로 붙어있는 것입니다.

이를 위해서 .scroll-imgposition: sticky;를 부여합니다.
배경으로 만들기 위해서 width: 100vh;도 부여하구요.

2. .scroll-img div

순서대로 놓여있는 1~8의 박스들을 겹쳐놓도록 할 필요가 있습니다.
부모 요소를 기준으로 위치를 잡아주는 position: absolute;를 사용하면 됩니다.

참고할 것은, 자식 요소에 postion: absolute;가 있다면 부모 요소에도 임의의 postion이 부여되어 있어야 합니다.

지금은 부모요소인 scroll-imgposition: sticky;가 부여되어 있으니 문제가 없습니다.

.scroll-img div에 아래의 css를 추가 해 봅시다.
height: 100%; position: absolute; top: 0; left: 0;

위 이미지처럼 모든 컬러박스들이 부모 요소의 top: 0; left: 0;을 기준으로 겹쳐집니다.
원래는 빨간 부분가지 채워져 있는데, 캡쳐 툴에 버그가 있는지 공백이 생기네요. (⊙_⊙;)

3. .scroll-txt

텍스트 박스들을 만질 차례입니다.

  • 여백 없이 붙어있는 모습이 보기 좋지 않으니 margin-bottom을 줍니다.
  • 자신을 기준으로 위치를 잡는 position: relative;를 부여합니다.

.scroll-textposition: relative;를 부여하세요.
.scroll-text p에는 margin: 0 auto 60vh auto;를 추가 해주십다.

JavaScript

  • .scroll-txt<p> 태그마다 data-index라는 attribute를 만들어줍니다.
  • getBoundingClientRect()를 사용하여 요소의 정보를 가져옵니다.
  • <p>의 위치를 기준으로 img1 ~ 8visible 클래스를 활성화/비활성화 시켜 배경을 전환합니다.
const txtElems = document.querySelectorAll('p')
const bgElems = document.querySelectorAll('.jsBg')

let currnetItem = bgElems[0]    // 현재 `.visible`이 활성화 된 클래스

data-index 만들어주기

for(let i = 0; i < txtElems.length; i++) {
    txtElems[i].dataset.index = i
    bgElemsn[i].dataset.index = i
}

요소들을 한번씩 돌아가면서 data.index = i를 만들어주었습니다.

getBoudingClientRect()

잠시 getBoundingClientRect()log에 띄우면 어떤 정보들이 나오는지 보겠습니다.

<style>
    .box{
        width: 100px;
        height: 100px;
        background-color: cadetblue;
    }
</style>

<body>
    <h1>getBoundingClientRect</h1>
    <div class="box"></div>

    <script>
        const box = document.querySelector('.box')
        const rect = box.getBoundingClientRect()
        console.log(rect);
    </script>
</body>

감이 오시나요? 직접 박스를 하나 만들어보고 비교해보시면 확실한 감이 오실겁니다.

scroll event 걸어주기

function activate() {
    currentItem.classList.add('visible')
}

function inactivate() {
   currentItem.classList.remove('visible')
}

window.addEventListener('scroll', () => {
    let step, boundingRect
    
    for(let i = 0; i < txtElems.length; i++) {
        step = txtElems[i]
        boundingRect = step.getBoundingClientRect()
        
        if(boundingRect.top > window.innerHeight * 0.1 &&
           boundingRect.top < window.innerHeight * 0.8) {
            inactivate()
            currentItem = bgElems[step.dataset.index]
            activate()
        }
    }
})

activate()

스크롤 이벤트가 발생하면 txtElems[0]부터 시작하여 텍스트의 위치가 일정 범위 안에 들어옴에 따라 배경색이 바뀌는 이벤트 함수입니다.

여기서 일정 범위는 window.innerHeight * 0.1 < boundingRect.top < window.innerHeight * 0.8 인데, window.innerHeight는 윈도우 화면의 높이를 뜻합니다.

전체 script

즉시 실행 함수로 모든 script를 감싸주면 완성입니다.

(() => {
    const txtElems = document.querySelectorAll('p')
    const bgElems = document.querySelectorAll('.jsBg')

    let currentItem = bgElems[0]

    for(let i = 0; i < txtElems.length; i++) {
        txtElems[i].dataset.index = i
        bgElems[i].dataset.index = i
    }
    
    function activate() {
        currentItem.classList.add('visible')
    }
    
    function inactivate() {
       currentItem.classList.remove('visible')
    }
    
    window.addEventListener('scroll', () => {
        let step, boundingRect
        
        for(let i = 0; i < txtElems.length; i++) {
            step = txtElems[i]
            boundingRect = step.getBoundingClientRect()
            
            if(boundingRect.top > window.innerHeight * 0.1 &&
               boundingRect.top < window.innerHeight * 0.8) {
                inactivate()
                currentItem = bgElems[step.dataset.index]
                activate()
            }
        }
    })
    
    activate()
})()

전체 css

*{margin: 0; padding: 0; box-sizing: border-box; font-family: sans-serif;}

h1{padding: 32px; background-color: palegoldenrod; font-size: 48px; font-weight: 700; color: #2d2d2d; text-align: center;}
h2{padding: 16px; background-color: palegreen; font-size: 32px; color: #2d2d2d; text-align: center;}

.scroll-area{}
.scroll-img{height: 100vh; position: sticky; top: 0;}
.scroll-img div{width: 100%; height: 100%; padding: 16px 0 0 16px; position: absolute; top: 0; left: 0; font-size: 48px; color: #fff; opacity: 0; transition: .5s;}
.scroll-img .img1{background-color: #e77b6f;}
.scroll-img .img2{background-color: #e4a36b;}
.scroll-img .img3{background-color: #f1dd8d;}
.scroll-img .img4{background-color: #6dca94;}
.scroll-img .img5{background-color: #6bacd8;}
.scroll-img .img6{background-color: #878ad8;}
.scroll-img .img7{background-color: #a57bb6;}
.scroll-img .img8{background-color: #4d5964;}

.scroll-txt{padding-bottom: 1px; position: relative;}
.scroll-txt p{max-width: 640px; margin: 0 auto 60vh auto; padding: 8px; background-color: rgba(149, 165, 166, .5); border: 1px solid #2d2d2d; border-radius: 5px; font-size: 18px; color: #fff;}

.footer{padding: 32px 0;background-color: cornflowerblue; font-size: 48px; font-weight: 600; color: #2d2d2d; text-align: center;}
.footer2{padding: 16px 0; background-color: darkkhaki; font-size: 32px; font-weight: 600; color: #2d2d2d; text-align: center;}

.scroll-img div.visible{opacity: 1;}

visible 클래스를 추가 하시고, .scroll-img divopacity: 0; transition: .5s;를 추가해주세요.

최종 완성본입니다.


소스코드는 이 곳을 참고하세요.

유튜브 1분코딩님을 참고하였습니다.

profile
프론트!

관심 있을 만한 포스트

0개의 댓글