์ผ | ์ | ํ | ์ | ๋ชฉ | ๊ธ | ํ |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 31 |
- ์คํฌ๋กค์ ๋๋ฉ์ด์
- ์๋ฐ์คํฌ๋ฆฝํธ
- ๋ก์ปฌ์คํ ๋ฆฌ์ง
- ui์ปดํฌ๋ํธ
- js์คํฌ๋กค์ด๋ฒคํธ
- uiux๋์์ธ
- ๋ฐ์ํ์น
- preferscolorscheme
- ์นui๋์์ธ
- ์ ๊ทผ์ฑ
- ์น๋์์ธ
- ๋ฐ๋๋ผ์คํฌ๋ฆฝํธ
- ๋คํฌ๋ชจ๋ํ ๊ธ
- ์ธํฐ๋ ํฐ๋ธui
- ์น์ ๊ทผ์ฑ
- UX๋์์ธ
- ์คํฌ๋ฆฐ๋ฆฌ๋์ง์
- css๋ณ์
- ์นUI
- ์๋ฐ์คํฌ๋ฆฝํธ์ปดํฌ๋ํธ
- JavaScript
- ํ๋ก ํธ์๋
- css์ ๋๋ฉ์ด์
- UIUX
- ๋ฐ๋๋ผjs
- ์น๊ฐ๋ฐ
- ์๋ฐ์คํฌ๋ฆฝํธui
- ๋คํฌ๋ชจ๋
- ํ ๋ง์๋์ ํ
- ์ฝ๋ฉ์์
- Today
- Total
UI Code Lab
๐ฏ Fullpage ์น์ฌ์ดํธ๋ฅผ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์์ด ๊ตฌํํ๋ ๋ฐฉ๋ฒ – ์์ JavaScript๋ก ์คํํ๋ ์คํฌ๋กค ์ธํฐํ์ด์ค ๋ณธ๋ฌธ
๐ฏ Fullpage ์น์ฌ์ดํธ๋ฅผ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์์ด ๊ตฌํํ๋ ๋ฐฉ๋ฒ – ์์ JavaScript๋ก ์คํํ๋ ์คํฌ๋กค ์ธํฐํ์ด์ค
๐ฏ๊ฟ์์ด๐ 2025. 7. 16. 08:34โ ๊ฐ์
๋ง์ ์น์ฌ์ดํธ์์ ์ฌ์ฉ๋๋ ํํ์ด์ง(Fullpage) ์ธํฐํ์ด์ค๋
์ฌ์ฉ์ ๊ฒฝํ์ ์ง๊ด์ ์ผ๋ก ๋ง๋ค๊ณ , ์ฝํ ์ธ ์ ๋ชฐ์ ํ ์ ์๋ ๊ตฌ์กฐ๋ฅผ ์ ๊ณตํฉ๋๋ค.
๋ํ์ ์ผ๋ก fullPage.js์ ๊ฐ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ๋๋ฆฌ ์ฌ์ฉ๋์ง๋ง,
์์ JavaScript๋ง์ผ๋ก๋ ์ถฉ๋ถํ ๊ฐ๋ณ๊ณ ์ ์ฐํ๊ฒ ๊ตฌํํ ์ ์์ต๋๋ค.
์ด ๊ธ์์๋ ๋ฐ๋๋ผ JS ๊ธฐ๋ฐ์ fullpage ์น์ฌ์ดํธ UI๋ฅผ ์ง์ ๋ง๋๋ ๊ณผ์ ์
์์ ์ค์ฌ์ผ๋ก ์์ธํ ์๊ฐํฉ๋๋ค.
๐ฆ ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์์ด ์ง์ ๊ตฌํํ ๊น?
- ์ฑ๋ฅ ์ต์ ํ: ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ถํฌํจ์ผ๋ก ํ์ด์ง ๋ก๋ฉ ์๋ ๊ฐ์
- ์ปค์คํฐ๋ง์ด์ง ์์ ๋: ๋ด๊ฐ ์ง์ ๋ง๋ ์ฝ๋๋ก ์ํ๋ ๋๋ก ์์ ๊ฐ๋ฅ
- ํ์ต ํจ๊ณผ: ์คํฌ๋กค ์ด๋ฒคํธ์ UI ํ๋ฆ ์ดํด๋๊ฐ ๋์์ง
๐งฑ HTML ๋ฐ CSS ๊ธฐ๋ณธ ๊ตฌ์กฐ
<div class="section-container">
<section class="section">Section 1</section>
<section class="section">Section 2</section>
<section class="section">Section 3</section>
</div>
html, body {
margin: 0;
height: 100%;
overflow: hidden;
}
.section-container {
transition: transform 0.7s ease;
}
.section {
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
font-size: 2rem;
}
โ๏ธ JavaScript๋ก ๋ถ๋๋ฌ์ด ์น์ ์ ํ ๊ตฌํํ๊ธฐ
const container = document.querySelector('.section-container');
let currentIndex = 0;
let isScrolling = false;
const sectionCount = document.querySelectorAll('.section').length;
window.addEventListener('wheel', (e) => {
if (isScrolling) return;
isScrolling = true;
const direction = e.deltaY > 0 ? 1 : -1;
currentIndex = Math.min(Math.max(currentIndex + direction, 0), sectionCount - 1);
container.style.transform = `translateY(-${currentIndex * 100}vh)`;
setTimeout(() => {
isScrolling = false;
}, 700);
});
๐๏ธ UX ํฅ์์ ์ํ ๊ณ ๊ธ ๊ธฐ๋ฅ ์์ด๋์ด
- Dot Navigation: ํ์ฌ ์์น๋ฅผ ๋ณด์ฌ์ฃผ๋ ์ฌ์ด๋ ์ธ๋์ผ์ดํฐ
- Mobile Touch Event ๋์: touchstart, touchend ์ด๋ฒคํธ๋ก ๋ชจ๋ฐ์ผ ์นํ UX
- Active ํด๋์ค ๊ด๋ฆฌ: ํ์ฌ ์น์ ์๋ง .active ๋ถ์ฌํ์ฌ ์คํ์ผ ๋ถ๊ธฐ
- requestAnimationFrame ๊ธฐ๋ฐ ๋ถ๋๋ฌ์ด ์ ๋๋ฉ์ด์
๐ฑ ๋ชจ๋ฐ์ผ ๋์ ์์ ์ฝ๋
let touchStartY = 0;
window.addEventListener('touchstart', (e) => {
touchStartY = e.touches[0].clientY;
});
window.addEventListener('touchend', (e) => {
const touchEndY = e.changedTouches[0].clientY;
const deltaY = touchStartY - touchEndY;
if (Math.abs(deltaY) < 50 || isScrolling) return;
isScrolling = true;
currentIndex = deltaY > 0
? Math.min(currentIndex + 1, sectionCount - 1)
: Math.max(currentIndex - 1, 0);
container.style.transform = `translateY(-${currentIndex * 100}vh)`;
setTimeout(() => {
isScrolling = false;
}, 700);
});
๐ง Fullpage UI์ ๋์ ์๋ฆฌ – ์ฌ์ฉ์์ ์คํฌ๋กค ์ ์ดํ๊ธฐ
fullpage ์ธํฐํ์ด์ค๋ ๊ธฐ๋ณธ์ ์ผ๋ก ํ ๋ฒ์ ์คํฌ๋กค ์ ๋ ฅ์ ๋ฐ๋ผ
ํ๋ฉด ์ ์ฒด๊ฐ ํ ์น์ ๋จ์๋ก ์์ง์ด๋ ํํ์ผ.
์ด๊ฑธ ์ํด ๊ฐ์ฅ ํต์ฌ์ ์ผ๋ก ์ฒ๋ฆฌํด์ผ ํ ๊ฑด:
- ์ฌ์ฉ์์ ์คํฌ๋กค์ ๊ฐ์งํ๋ ๋ก์ง
- ํ์ฌ ์น์ ์ ๊ธฐ์ค์ผ๋ก ๋ค์ ์น์ ์ผ๋ก ๋ถ๋๋ฝ๊ฒ ์ด๋
- ์คํฌ๋กค ์ด๋ฒคํธ๊ฐ ๊ฒน์น์ง ์๋๋ก ์ ์ด(throttle ๋๋ flag ์ฒ๋ฆฌ)
let currentIndex = 0;
let isScrolling = false;
window.addEventListener('wheel', (e) => {
if (isScrolling) return;
isScrolling = true;
const direction = e.deltaY > 0 ? 1 : -1;
currentIndex = Math.min(Math.max(currentIndex + direction, 0), sectionCount - 1);
container.style.transform = `translateY(-${currentIndex * 100}vh)`;
setTimeout(() => isScrolling = false, 700); // ์ ํ ์๊ฐ๊ณผ ์ผ์น
});
โจ ์ฌ๊ธฐ์ ์ค์ํ ์ ์ isScrolling์ด๋ผ๋ ํ๋๊ทธ๋ฅผ ํ์ฉํด์ ์ฐ์ ์ ๋ ฅ์ ๋ง๋ ๊ฑฐ์ผ.
์ด ๋ฐฉ์์ Debounce๋ Throttle๋ณด๋ค ์ง๊ด์ ์ผ๋ก ๊ตฌํํ ์ ์๊ณ , UX ์์ ์ฑ์ ๋์ฌ์ค.
๐งฉ transition๊ณผ requestAnimationFrame ์ฐจ์ด์
๋๋ถ๋ถ CSS์์ transition: transform 0.7s ease;๋ฅผ ์ฌ์ฉํ์ง๋ง,
์ข ๋ ๋ฌผ๋ฆฌ์ ์ด๊ณ ๋ถ๋๋ฌ์ด ์์ง์์ ์ํ๋ค๋ฉด
requestAnimationFrame์ผ๋ก ์ปค์คํฐ๋ง์ด์งํ๋ ๋ฐฉ๋ฒ๋ ์์ด:
function smoothScroll(targetY) {
const startY = container.getBoundingClientRect().top;
const diff = targetY - startY;
let start;
function step(timestamp) {
if (!start) start = timestamp;
const progress = timestamp - start;
const easing = Math.min(progress / 700, 1); // 700ms ๊ธฐ์ค
container.style.transform = `translateY(${startY + diff * easing}px)`;
if (easing < 1) requestAnimationFrame(step);
}
requestAnimationFrame(step);
}
๐ ์ด ๋ฐฉ์์ ์ฑ๋ฅ ์ ์ด๊ฐ ๋ ํ์ํ์ง๋ง,
๋ณต์กํ ์ ๋๋ฉ์ด์ ์ด๋ ์ปค์คํ easing ์ปค๋ธ๋ฅผ ์ ์ฉํ๊ธฐ์ ์ ๋ฆฌํด.
๐ฑ ๋ชจ๋ฐ์ผ ํฐ์น ๋์์ ๋ฐ๋ฅธ UX ๊ฐ์
์คํฌ๋กค๊ณผ ํฐ์น๋ฅผ ๋ชจ๋ ์ง์ํ๋ ค๋ฉด ๋ฐ์คํฌํ๊ณผ ๋ชจ๋ฐ์ผ ์ด๋ฒคํธ๋ฅผ ๋ณํ ์ฒ๋ฆฌํด์ผ ํ๊ณ ,
๊ฐ๊ฐ์ ํน์ง์ ๋ฐ์ํด์ผ UX๊ฐ ๊นจ์ง์ง ์์:
- ํฐ์น๋ touchstart์ touchend๋ฅผ ์กฐํฉํด์ ์ฌ์ฉ์์ ์ค์์ดํ ๋ฐฉํฅ์ ํ๋จ
- ํฐ์น ๊ฐ๋ ์กฐ์ ์ด UX์ ํฐ ์ํฅ์ ๋ฏธ์นจ
- ์์ ์์ง์(delta < 50)์ ๋ฌด์ํ์ฌ ์ค์๋ ๋ฐฉ์ง
let touchStartY = 0;
window.addEventListener('touchstart', (e) => {
touchStartY = e.touches[0].clientY;
});
window.addEventListener('touchend', (e) => {
const touchEndY = e.changedTouches[0].clientY;
const deltaY = touchStartY - touchEndY;
if (Math.abs(deltaY) < 50 || isScrolling) return;
isScrolling = true;
currentIndex = deltaY > 0 ? Math.min(currentIndex + 1, sectionCount - 1) : Math.max(currentIndex - 1, 0);
container.style.transform = `translateY(-${currentIndex * 100}vh)`;
setTimeout(() => isScrolling = false, 700);
});
๐๏ธ Dot Navigation์ UX์ ๊ฐ์น
์ ๋ด๋น๊ฒ์ด์ ์ ์ฌ์ฉ์๊ฐ ํ์ฌ ์ด๋ ์น์ ์ ์๋์ง ์๊ฐ์ ์ผ๋ก ์ ์ ์๊ฒ ํด์ค.
๋ํ ํด๋ฆญ์ผ๋ก ํด๋น ์น์ ์ผ๋ก ์ ํํ ์ ์์ด:
<div class="dot-nav">
<div class="dot active" data-index="0"></div>
<div class="dot" data-index="1"></div>
<div class="dot" data-index="2"></div>
</div>
document.querySelectorAll('.dot').forEach(dot => {
dot.addEventListener('click', (e) => {
currentIndex = Number(e.target.dataset.index);
container.style.transform = `translateY(-${currentIndex * 100}vh)`;
updateActiveDot();
});
});
function updateActiveDot() {
document.querySelectorAll('.dot').forEach(dot => dot.classList.remove('active'));
document.querySelector(`.dot[data-index="${currentIndex}"]`).classList.add('active');
}
๐จ ๋ด๋น๊ฒ์ด์ ์ ์ ๊ทผ์ฑ, ์ฌ์ฉ์ฑ, ์๊ฐ์ ํผ๋๋ฐฑ๊น์ง
๋ชจ๋ ์ฑ๊ธธ ์ ์์ด์ “์์ง๋ง ํฐ” UX ์์์ผ.
๐ ๋ง๋ฌด๋ฆฌํ๋ฉฐ
ํํ์ด์ง ์น์ฌ์ดํธ๋ ์๊ฐ์ ์ผ๋ก ๊ฐ๋ ฌํ๊ณ
UX์ ๊ฐํ ์ธ์์ ๋จ๊ธธ ์ ์๋ UI ํจํด์ ๋๋ค.
๊ผญ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ์์กดํ์ง ์์๋
์ง์ ๊ตฌํํ๋ฉด์ ํ์ต๊ณผ ํจ์จ์ ๋์์ ์ก์ ์ ์์ต๋๋ค.
์์ผ๋ก ์ด ๋ฐฉ์์ ํ์ฅํด ๋ชจ๋ฌ, ์ฌ๋ผ์ด๋, ๋ด๋น๊ฒ์ด์ ๋ฑ
๋ค๋ฅธ ์ปดํฌ๋ํธ์๋ ์ฐ๊ฒฐํด๋ณด์ธ์!