[smooth scroll] Lenis

J.yeonยท2024๋…„ 1์›” 3์ผ
5
post-thumbnail

Lenis๋ž€?๐Ÿค”

ํ”Œ๋žซํผ์„ ์‚ฌ์šฉํ•˜๋Š” ๋™์‹œ์— ์Šคํฌ๋กค ๊ฒฝํ—˜์„ ํ‘œ์ค€ํ™”ํ•˜๊ณ  ๋งค์šฐ ๋ถ€๋“œ๋Ÿฌ์šด ํƒ์ƒ‰ ๊ธฐ๋Šฅ์œผ๋กœ ์›น์‚ฌ์ดํŠธ๋ฅผ ๊ฐ•ํ™”ํ•˜๊ธฐ ์œ„ํ•ด ๊ตฌ์ถ•๋œ ์˜คํ”ˆ ์†Œ์Šค ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ



์‚ฌ์šฉ๋ฒ•๐Ÿ–ฑ๏ธ

์„ค์น˜

โ‘  ํŒจํ‚ค์ง€ ๊ด€๋ฆฌ์ž

$ npm i @studio-freight/lenis

import Lenis from '@studio-freight/lenis'

โ‘ก ์Šคํฌ๋ฆฝํŠธ

<script src="https://unpkg.com/@studio-freight/lenis@1.0.32/dist/lenis.min.js"></script> 


์„ค์ •

[๊ธฐ๋ณธ์„ค์ •]

const lenis = new Lenis()

lenis.on('scroll', (e) => {
  console.log(e)
})

function raf(time) {
  lenis.raf(time)
  requestAnimationFrame(raf)
}

requestAnimationFrame(raf)

GSAP ScrollTrigger๋ฅผ ๊ฐ™์ด ์‚ฌ์šฉํ•œ๋‹ค๋ฉด?

const lenis = new Lenis()

lenis.on('scroll', ScrollTrigger.update)

gsap.ticker.add((time)=>{
  lenis.raf(time * 1000)
})

gsap.ticker.lagSmoothing(0)


์‚ฌ์šฉํ•˜๋ฉด์„œ ์ƒ๊ธด ๋ฌธ์ œ๐Ÿซ 

  1. body์˜ overflow: hidden ์†Œ๋ฉธ
  2. gsap๊ณผ ๊ฐ™์ด ์‚ฌ์šฉํ•˜๋ฉด์„œ lenis ์ž๋™wrapper ๋ณ€๊ฒฝ์‹œ ๋ฌธ์ œ ๋ฐœ์ƒ

body์˜ overflow: hidden ์†Œ๋ฉธ

lenis๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด body์— ์ค€ overflow: hidden์ด ์†Œ์šฉ์—†์–ด์ง์„ ํ™•์ธํ•จ,
๋˜ํ•œ ๋ฒ„ํŠผ ํด๋ฆญ์‹œ ํ™œ์„ฑํ™”๋˜๋Š” gnb(๋„ค๋น„๊ฒŒ์ด์…˜)์˜ ๋‚ด๋ถ€์—์„œ ์Šคํฌ๋กค์ด ๋˜์ง€์•Š๋Š” ํ˜„์ƒ ๋ฐœ์ƒ.


ํ•ด๋‹น ๋ฌธ์ œ๋Š” Lenis.github ์— ๊ณ ๋ ค์‚ฌํ•ญ์œผ๋กœ ์ ์–ด๋‘” ์ค‘์ฒฉ ์Šคํฌ๋กค ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํ•ด๊ฒฐ๋œ๋‹ค.

<div data-lenis-prevent>scroll content</div>

<div data-lenis-prevent-wheel>scroll content</div>

<div data-lenis-prevent-touch>scroll content</div>

๐Ÿ‘‡
๋‚˜๊ฐ™์€ ๊ฒฝ์šฐ gnb๋‚ด๋ถ€ ์Šคํฌ๋กค์ด ๋˜์ง€ ์•Š์•„์„œ,

<nav class="gnb" data-lenis-prevent-wheel>
	<ul>
    	<li></li>
        <li></li>
        <li></li>
	</ul>
</nav>

์ด๋Ÿฐ์‹์œผ๋กœ ๋„ฃ์–ด์คฌ๋”๋‹ˆ ์Šคํฌ๋กค์ด ์ž˜ ์ž‘๋™๋˜๋Š” ๊ฑธ ํ™•์ธํ–ˆ๋‹ค.




gsap๊ณผ ๊ฐ™์ด ์‚ฌ์šฉํ•˜๋ฉด์„œ lenis ์ž๋™wrapper ๋ณ€๊ฒฝ์‹œ ๋ฌธ์ œ ๋ฐœ์ƒ

lenis๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์Šคํฌ๋กค์ด ์ ์šฉ๋˜๋Š” ์ปจํ…Œ์ด๋„ˆ(wrapper)๊ฐ€ ์ž๋™์œผ๋กœ html๋กœ ์ ์šฉ๋˜๋Š”๋ฐ,
UI์ƒ html์ด ์•„๋‹Œ ๋‚ด๋ถ€ ์ปจํ…์ธ  div(.wrapper)๋กœ ๋ณ€๊ฒฝํ•ด์•ผ ํ•˜๋Š” ์ƒํ™ฉ์ด์—ˆ์Œ.

<html lenis lenis-smooth>

Lenis.github ์„ ๋ณด๋ฉด Instance settings ์— wrapper ๋ผ๋Š” ์˜ต์…˜์ด ์žˆ๋‹ค.

const lenis = new Lenis({
    wrapper: document.querySelector('.container'),
});

๋‹จ์ˆœํžˆ ์ด๋ ‡๊ฒŒ ๋ณ€๊ฒฝํ•˜๋ฉด ๋  ์ค„ ์•Œ์•˜๋”๋‹ˆ ๋งˆํฌ์—…์ƒ ๋ณ€๊ฒฝ๋งŒ๋  ๋ฟ ์Šคํฌ๋กค์ด ์•ˆ๋จน๋Š”๋‹ค.


์ด๋ž˜์ €๋ž˜ ํ•ด๊ฒฐํ•œ ๋ฐฉ๋ฒ•์€๐Ÿ‘‡

[ JS ]

const lenis = new Lenis({
    wrapper: document.querySelector('.wrapper'),
    content: document.querySelector('.inner-wrapper'),
});

lenis.on('scroll', ScrollTrigger.update);

gsap.ticker.add(time => {
    lenis.raf(time * 1000);
});

gsap.ticker.lagSmoothing(0);

[ CSS ]

html {
    bottom: 0;
    left: 0;
    overflow: hidden;
    position: fixed;
    right: 0;
    top: 0;
}
.wrapper {
    bottom: 0;
    left: 0;
    position: fixed;
    right: 0;
    top: 0;
    overflow: hidden;
    overflow-y: auto;
    width: 100%;
}

์—ฌ๊ธฐ๊นŒ์ง€ ํ•˜๋ฉด ์Šคํฌ๋กค์€ smoothํ•˜๊ฒŒ ๋™์ž‘์ด ์ž˜๋œ๋‹ค.


but...... ์ถ”๊ฐ€์ ์œผ๋กœ ๋ฐœ์ƒํ•œ ๋ฌธ์ œ๐Ÿซ 

  • gsap scrollTrigger ์ž‘๋™์•ˆ๋จ
  • html๊ณผ .wrapper๋ฅผ fixed๋กœ ๋„์–ด์„œ๊ทธ๋Ÿฐ์ง€ js ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ(scroll) ๊ฐ€ ๋™์ž‘ํ•˜์ง€ ์•Š์Œ
    (scroll์œ„์น˜๋ฅผ ๋ชป์žก์Œ)

gsap scrollTrigger ์ž‘๋™์•ˆ๋จ

์ด๊ฑด gsap scrollTrigger์˜ ์†์„ฑ์œผ๋กœ ํ•ด๊ฒฐํ–ˆ๋‹ค.
๋‚ด๊ฐ€ scrollTrigger ๋„ฃ์–ด์ค€ ๊ณณ๋งˆ๋‹ค scroller์†์„ฑ๊ฐ’์„ ๋„ฃ์–ด์ค˜์•ผํ•œ๋‹ค.

ํ•ด๊ฒฐ๐Ÿ‘‡

scrollTrigger: {
        trigger: 'section',
        scroller: '.wrapper',       
}        

scroller ์„ค๋ช…




js ์Šคํฌ๋กค ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ ๋™์ž‘์•ˆ๋จ

window์ž์ฒด ์Šคํฌ๋กค์„ ํ•˜๊ณ ์žˆ๋Š”๊ฒŒ ์•„๋‹Œ, lenis์™€ gsap์œผ๋กœ ๊ฐ•์ œ ์Šคํฌ๋กค ์ค‘์ด๋ผ scroll์œ„์น˜๊ฐ’์„ ๋ชป์ฝ์Œ.
์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ๋Œ€์‹  gsap์œผ๋กœ ๋Œ€์ฒด ํ•ด๊ฒฐ๊ฐ€๋Šฅ


ํ•ด๊ฒฐ๐Ÿ‘‡

์„น์…˜๋ณ„๋กœ ํ•„์š”ํ–ˆ๋˜ ๊ธฐ๋Šฅ๋“ค์€ ์•„๋ž˜์™€ ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ๋ณ€๊ฒฝํ•ด์ฃผ๋Š” ๊ฑธ๋กœ ๋Œ€์ฒดํ•จ.

ScrollTrigger.create({
    trigger: 'section',
    scroller: '.wrapper',
    start: '0% 0%',
    onEnter: function () {
        $('section .con').addClass('on');
    },
    onLeaveBack: function () {
        $('section .con').removeClass('on');
    },
});

ex) scrollTop๊ฐ’์ด 0์ดˆ๊ณผ์ผ๋•Œ๋งŒ ์ ์šฉ๋˜๋Š” header์š”์†Œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด,

ScrollTrigger.create({
    trigger: '.wrapper',
    scroller: '.wrapper',
    onUpdate: self => {
    	// console.log('progress:', self.progress.toFixed(3));
        if (self.progress.toFixed(3) > 0.5) {
            $('.header').addClass('on');
            
        } else {
            $('.header').removeClass('on');
        }
    },
});

gsap.update ์„ค๋ช…
gsap.progress ์„ค๋ช…


๋ฌธ์ œ ํ•ด๊ฒฐ ๋...๐Ÿซ 

profile
๊ฐœ๋ธ”๋ฆฌ์…” ๊ฟˆ๊พธ๋Š” ํผ๋ธ”์˜ ๊ฐœ๋ฐœ๊ณต๋ถ€๐Ÿšถโ€โ™€๏ธ ์ฒœ์ฒœํžˆ ๊ฑธ์–ด๊ฐ€๋Š” ์ค‘

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

๊ด€๋ จ ์ฑ„์šฉ ์ •๋ณด