Skip to content

PLUTO-NIX/Motion

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

7 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

Motion - ๋ชจ๋‹ฌ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์‹œ์Šคํ…œ ๊ฐœ๋ฐœ ๊ธฐํš์„œ

๐ŸŽฏ ๋ผ์ด๋ธŒ ๋ฐ๋ชจ

๐Ÿ‘‰ Motion ๋ผ์ด๋ธŒ ๋ฐ๋ชจ ๋ฐ”๋กœ๊ฐ€๊ธฐ

๋ ˆํฌ๋ฅผ ํด๋ก ํ•˜์ง€ ์•Š๊ณ ๋„ ๋ฐ”๋กœ ๋ธŒ๋ผ์šฐ์ €์—์„œ ๋ชจ๋“  ๋ชจ๋‹ฌ ํƒ€์ž…์„ ํ…Œ์ŠคํŠธํ•ด๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ“ฑ ๋ชจ๋ฐ”์ผ์—์„œ ํ…Œ์ŠคํŠธํ•˜๊ธฐ

  • iOS Safari: ์ƒ๋‹จ ๋งํฌ ํด๋ฆญ ํ›„ ๊ฐœ๋ฐœ์ž๋„๊ตฌ โ†’ ๋””๋ฐ”์ด์Šค ์‹œ๋ฎฌ๋ ˆ์ด์…˜
  • Android Chrome: ์ƒ๋‹จ ๋งํฌ ํด๋ฆญ ํ›„ F12 โ†’ ๋ชจ๋ฐ”์ผ ๋ทฐ ํ† ๊ธ€
  • ์‹ค์ œ ๋ชจ๋ฐ”์ผ: QR ์ฝ”๋“œ๋กœ ์ ‘๊ทผ (QR ์ƒ์„ฑ๊ธฐ์— ์œ„ URL ์ž…๋ ฅ)

๐Ÿ“‹ ํ”„๋กœ์ ํŠธ ๊ฐœ์š”

Motion์€ ๋‹ค์–‘ํ•œ ๋””๋ฐ”์ด์Šค์™€ ํ™”๋ฉด ํฌ๊ธฐ์— ์ตœ์ ํ™”๋œ 7๊ฐ€์ง€ ๋ชจ๋‹ฌ ํƒ€์ž…์„ ์ œ๊ณตํ•˜๋Š” ๋ฐ˜์‘ํ˜• ๋ชจ๋‹ฌ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์‹œ์Šคํ…œ์ž…๋‹ˆ๋‹ค. ๋ชจ๋ฐ”์ผ๊ณผ PC ํ™˜๊ฒฝ์—์„œ ๊ฐ๊ฐ ๋‹ค๋ฅธ UX ํŒจํ„ด์„ ์ ์šฉํ•˜์—ฌ ์ตœ์ ์˜ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

๐ŸŽฏ ์ฃผ์š” ๋ชฉํ‘œ

  • ๋ฐ˜์‘ํ˜• ๋””์ž์ธ: ๋ชจ๋ฐ”์ผ/PC ํ™˜๊ฒฝ๋ณ„ ์ตœ์ ํ™”๋œ ๋ชจ๋‹ฌ ๋ ˆ์ด์•„์›ƒ
  • ๋ถ€๋“œ๋Ÿฌ์šด ์• ๋‹ˆ๋ฉ”์ด์…˜: CSS3์™€ JavaScript๋ฅผ ๊ฒฐํ•ฉํ•œ ๊ณ ์„ฑ๋Šฅ ์• ๋‹ˆ๋ฉ”์ด์…˜
  • ์Šค๋งˆํŠธ ๋ ˆ์ด์•„์›ƒ: ์ฝ˜ํ…์ธ  ์–‘์— ๋”ฐ๋ฅธ ๋™์  ๋ ˆ์ด์•„์›ƒ ์กฐ์ •
  • ์ ‘๊ทผ์„ฑ: ํ‚ค๋ณด๋“œ ๋‚ด๋น„๊ฒŒ์ด์…˜ ๋ฐ ESC ํ‚ค ์ง€์›

๐Ÿงพ Motion Spec Sheet (๋ชจ์…˜ ์ŠคํŽ™ ์ •์˜์„œ)

์ด ๋ ˆํฌ์—๋Š” ์‹คํ–‰ ์ค‘์ธ CSS/WAAPI ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ๊ด€์ฐฐํ•ด์„œ ์ฒจ๋ถ€ ์ด๋ฏธ์ง€ ๊ฐ™์€ โ€œ๋ชจ์…˜ ์ŠคํŽ™ ์‹œํŠธโ€๋ฅผ ์›น์—์„œ ๋ Œ๋”๋งํ•˜๋Š” ๋„๊ตฌ๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

  • ํด๋”: motion-spec-sheet/
  • ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ: motion-spec-sheet/motion-spec-sheet.js
  • ๋ฌธ์„œ: motion-spec-sheet/README.md

ํ”Œ๋Ÿฌ๊ทธ์•คํ”Œ๋ ˆ์ด(๊ถŒ์žฅ)

HTML์— ์•„๋ž˜ ์Šคํฌ๋ฆฝํŠธ 1์ค„๋งŒ ์ถ”๊ฐ€ํ•˜๋ฉด Specs ๋ฒ„ํŠผ/์„ค์ • UI๊ฐ€ ์ž๋™ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.

<script src="motion-spec-sheet/motion-spec-sheet.js"></script>

์‚ฌ์šฉ ์š”์•ฝ

  • Specs ๋ฒ„ํŠผ: ์šฐ์ธก ํ•˜๋‹จ์— ์ž๋™ ์ƒ์„ฑ (๋‹จ์ถ•ํ‚ค: Ctrl+Shift+M)
  • ์„ค์ •: ๊ทธ๋ฃน(์ด๋ฆ„/์…€๋ ‰ํ„ฐ) ๋“ฑ๋ก ํ›„ ์–ด๋–ค ํ”„๋กœ์ ํŠธ์—์„œ๋„ ๋™์ผํ•œ ๋ฐฉ์‹์œผ๋กœ ์บก์ฒ˜ ๊ฐ€๋Šฅ
  • Capture: ์ˆ˜๋™ ์บก์ฒ˜ ๋ฒ„ํŠผ
  • Auto capture: ํด๋ฆญ/ํ‚ค ์ž…๋ ฅ ์ดํ›„ ์‹คํ–‰ ์ค‘ ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ๊ฐ์ง€ํ•ด ์ž๋™ ์บก์ฒ˜(์˜ต์…˜)
  • ํƒ€์ž„๋ผ์ธ ๋ทฐ: Auto/Custom ๋ฒ”์œ„ ์ „์—ญ ์ ์šฉ + ๋ฒ”์œ„ ์ดˆ๊ณผ ์‹œ โ€œ์ž„์‹œ ํ™•์žฅโ€ ์•ˆ๋‚ด

์ž์„ธํ•œ ์‚ฌ์šฉ๋ฒ•์€ motion-spec-sheet/README.md๋ฅผ ์ฐธ๊ณ ํ•˜์„ธ์š”.


๐Ÿ—๏ธ ์‹œ์Šคํ…œ ์•„ํ‚คํ…์ฒ˜

๋ชจ๋‹ฌ ํƒ€์ž… ๊ตฌ์กฐ

๋ชจ๋‹ฌ ์‹œ์Šคํ…œ
โ”œโ”€โ”€ ๋ชจ๋ฐ”์ผ ๋ชจ๋‹ฌ (4์ข…)
โ”‚   โ”œโ”€โ”€ mo-small-center    โ†’ ์ž‘์€ ์„ผํ„ฐ ๋ชจ๋‹ฌ
โ”‚   โ”œโ”€โ”€ mo-small-bottom    โ†’ ์ž‘์€ ๋ฐ”ํ…€ ์‹œํŠธ
โ”‚   โ””โ”€โ”€ mo-medium-bottom   โ†’ ์ค‘๊ฐ„ ๋ฐ”ํ…€ ์‹œํŠธ
โ”‚   โ””โ”€โ”€ mo-large-full      โ†’ ์ „์ฒด ํ™”๋ฉด ๋ชจ๋‹ฌ
โ””โ”€โ”€ PC ๋ชจ๋‹ฌ (3์ข…)  
    โ”œโ”€โ”€ pc-small-center    โ†’ ์ž‘์€ ์„ผํ„ฐ ๋ชจ๋‹ฌ
    โ”œโ”€โ”€ pc-medium-center   โ†’ ์ค‘๊ฐ„ ์„ผํ„ฐ ๋ชจ๋‹ฌ
    โ””โ”€โ”€ pc-large-center    โ†’ ํฐ ์„ผํ„ฐ ๋ชจ๋‹ฌ

๐Ÿš€ ๋ชจ๋‹ฌ ๋™์ž‘ ์‹œ๋‚˜๋ฆฌ์˜ค

1. ์‹œ์Šคํ…œ ์ดˆ๊ธฐํ™”

์–ธ์ œ: DOM ๋กœ๋“œ ์™„๋ฃŒ ์‹œ (DOMContentLoaded) ์–ด๋–ป๊ฒŒ:

// ๋ชจ๋“  ๋ชจ๋‹ฌ ํƒ€์ž…์„ ์‚ฌ์ „ ์ƒ์„ฑํ•˜์—ฌ ์„ฑ๋Šฅ ์ตœ์ ํ™”
Object.keys(modalConfigs).forEach(type => {
    // ํ…œํ”Œ๋ฆฟ ๋ณต์ œ ๋ฐ ๊ฐ ํƒ€์ž…๋ณ„ ์ปค์Šคํ„ฐ๋งˆ์ด์ง•
    const container = document.createElement('div');
    container.id = `modal-container-${type}`;
    // ... DOM ๊ตฌ์กฐ ์ƒ์„ฑ
});

๊ฒฐ๊ณผ: 7๊ฐ€์ง€ ๋ชจ๋‹ฌ์ด hidden ์ƒํƒœ๋กœ ๋ฉ”๋ชจ๋ฆฌ์— ์ค€๋น„๋จ

2. ๋ชจ๋‹ฌ ์—ด๊ธฐ ํ”„๋กœ์„ธ์Šค

2.1 ํŠธ๋ฆฌ๊ฑฐ ์ด๋ฒคํŠธ

์–ธ์ œ: ์‚ฌ์šฉ์ž๊ฐ€ ๋ชจ๋‹ฌ ํŠธ๋ฆฌ๊ฑฐ ๋ฒ„ํŠผ ํด๋ฆญ ์–ด๋–ป๊ฒŒ:

triggerButtons.forEach(button => {
    button.addEventListener('click', () => openModal(button.dataset.modalType));
});

2.2 ๋ชจ๋‹ฌ ํ‘œ์‹œ ์ค€๋น„

๋ฌด์—‡์„: Body ์Šคํฌ๋กค ๋ฐฉ์ง€ ๋ฐ ๋ชจ๋‹ฌ ์ปจํ…Œ์ด๋„ˆ ํ™œ์„ฑํ™”

const openModal = (requestedType) => {
    // 1. ์Šคํฌ๋กค ๋ฐฉ์ง€
    document.body.classList.add('overflow-hidden');
    
    // 2. ๋ชจ๋‹ฌ ํ‘œ์‹œ
    modalContainer.classList.remove('hidden');
    
    // 3. ๋‹ค์Œ ํ”„๋ ˆ์ž„์—์„œ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์‹œ์ž‘
    requestAnimationFrame(() => {
        // ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ ์šฉ...
    });
};

2.3 ์Šค๋งˆํŠธ ๋ ˆ์ด์•„์›ƒ ์กฐ์ •

์–ธ์ œ: ๋ชจ๋ฐ”์ผ ๋ชจ๋‹ฌ์—์„œ ์ฝ˜ํ…์ธ ๊ฐ€ ํ™”๋ฉด์„ ์ดˆ๊ณผํ•  ๋•Œ ์–ด๋–ป๊ฒŒ:

const checkAndAdjustModalType = (modalContainer) => {
    const totalContentHeight = modalHeader.offsetHeight + 
                               mainContentElement.scrollHeight + 
                               modalFooter.offsetHeight;
    const availableHeight = modalContainer.clientHeight;
    
    if (totalContentHeight > availableHeight) {
        // ์ „์ฒด ํ™”๋ฉด ๋ชจ๋‹ฌ๋กœ ์ž๋™ ๋ณ€๊ฒฝ
        modalContainer.dataset.layoutType = 'mo-large-full';
        applyLayoutOnly(modalContainer, 'mo-large-full');
    }
};

2.4 ์• ๋‹ˆ๋ฉ”์ด์…˜ ์‹คํ–‰

CSS ์• ๋‹ˆ๋ฉ”์ด์…˜ ์กฐํ•ฉ:

/* ์˜ˆ: ๋ชจ๋ฐ”์ผ ์ž‘์€ ์„ผํ„ฐ ๋ชจ๋‹ฌ */
.modal-mo-small-center-open {
    animation: modal-mo-small-center-open 300ms ease-out,
               modal-opacity-open 100ms linear;
}

@keyframes modal-mo-small-center-open {
    from { transform: translateY(50%); }
}

3. ๋ชจ๋‹ฌ ๋‹ซ๊ธฐ ํ”„๋กœ์„ธ์Šค

3.1 ๋‹ซ๊ธฐ ํŠธ๋ฆฌ๊ฑฐ ๊ฐ์ง€

๋‹ค์ค‘ ๋‹ซ๊ธฐ ๋ฐฉ๋ฒ• ์ง€์›:

modalWrapper.addEventListener('click', (e) => {
    // 1. ์˜ค๋ฒ„๋ ˆ์ด ํด๋ฆญ
    if (e.target.classList.contains('modal-overlay')) {
        closeModal(e.target.parentElement);
    }
    
    // 2. ๋‹ซ๊ธฐ/ํ™•์ธ ๋ฒ„ํŠผ ํด๋ฆญ
    const button = e.target.closest('.modal-close-btn, .modal-ok-btn');
    if (button) closeModal(modalContainer);
});

// 3. ESC ํ‚ค ์ž…๋ ฅ
window.addEventListener('keydown', (e) => {
    if (e.key === 'Escape') {
        closeModal(visibleModal);
    }
});

3.2 ์ˆœ์ฐจ์  ๋‹ซ๊ธฐ ์• ๋‹ˆ๋ฉ”์ด์…˜

๋ชจ๋‹ฌ ํฌ๊ธฐ๋ณ„ ์ฐจ๋“ฑ ๋”œ๋ ˆ์ด:

const OVERLAY_DELAYS = {
    'small': '50ms',    // ์ž‘์€ ๋ชจ๋‹ฌ: ๋น ๋ฅธ ๋ฐฐ๊ฒฝ ํŽ˜์ด๋“œ
    'medium': '150ms',  // ์ค‘๊ฐ„ ๋ชจ๋‹ฌ: ์ ๋‹นํ•œ ๋”œ๋ ˆ์ด
    'large': '250ms'    // ํฐ ๋ชจ๋‹ฌ: ์ปจํ…์ธ  ํฌ์ง€์…˜ ์ข…๋ฃŒ ์‹œ์ ๊ณผ ๋งž์ถฐ ์ž์—ฐ์Šค๋Ÿฌ์šด ํšจ๊ณผ
};

์• ๋‹ˆ๋ฉ”์ด์…˜ ์™„๋ฃŒ ๋Œ€๊ธฐ:

const closeModal = (modalContainer) => {
    // 1. ๋‹ซ๊ธฐ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์‹œ์ž‘
    modalOverlay.classList.add('animated', 'modal-overlay-close-dynamic');
    modalContent.classList.add('animated', `modal-${type}-close`);
    
    // 2. ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ง€์†์‹œ๊ฐ„ ๋™์  ๊ณ„์‚ฐ
    const contentDuration = parseDuration(contentComputedStyle.animation);
    const overlayDuration = parseDuration(overlayComputedStyle.animation);
    const timeoutDuration = Math.max(contentDuration, overlayDuration);
    
    // 3. ์• ๋‹ˆ๋ฉ”์ด์…˜ ์™„๋ฃŒ ํ›„ ์ •๋ฆฌ
    setTimeout(() => {
        modalContainer.classList.add('hidden');
        // ๋ ˆ์ด์•„์›ƒ ์›์ƒ ๋ณต๊ตฌ...
    }, timeoutDuration);
};

๐ŸŽจ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ƒ์„ธ ์ŠคํŽ™

CSS ๋ณ€์ˆ˜ ์‹œ์Šคํ…œ

:root {
    /* ํƒ€์ด๋ฐ ๊ฐ’ */
    --motion-timing-100: 100ms;
    --motion-timing-300: 300ms;
    --motion-timing-500: 500ms;
    
    /* ์ด์ง• ํ•จ์ˆ˜ */
    --motion-easing-easeOut: cubic-bezier(0.2, 0, 0, 1);
    --motion-easing-easeIn: cubic-bezier(1, 0, 0.8, 1);
    
    /* ๋ฐฐ๊ฒฝ์ƒ‰ */
    --modal-overlay-default: rgba(0, 0, 0, 0.5);
}

๋ชจ๋‹ฌ๋ณ„ ์• ๋‹ˆ๋ฉ”์ด์…˜ ํŒจํ„ด

๋ชจ๋ฐ”์ผ ๋ชจ๋‹ฌ

ํƒ€์ž… ์—ด๊ธฐ ์• ๋‹ˆ๋ฉ”์ด์…˜ ๋‹ซ๊ธฐ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ง€์†์‹œ๊ฐ„
mo-small-center translateY(50%) โ†’ translateY(0) translateY(0) โ†’ translateY(50%) 300ms / 200ms
mo-small-bottom translateY(100%) โ†’ translateY(0) translateY(0) โ†’ translateY(100%) 300ms / 200ms
mo-medium-bottom translateY(100%) โ†’ translateY(0) translateY(0) โ†’ translateY(100%) 400ms / 300ms
mo-large-full translateX(100%) โ†’ translateX(0) translateX(0) โ†’ translateX(100%) 500ms / 400ms

PC ๋ชจ๋‹ฌ

ํƒ€์ž… ์—ด๊ธฐ ์• ๋‹ˆ๋ฉ”์ด์…˜ ๋‹ซ๊ธฐ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ง€์†์‹œ๊ฐ„
pc-small-center translateY(50%) โ†’ translateY(0) translateY(0) โ†’ translateY(50%) 300ms / 200ms
pc-medium-center translateY(30%) โ†’ translateY(0) translateY(0) โ†’ translateY(30%) 400ms / 300ms
pc-large-center translateY(15%) โ†’ translateY(0) translateY(0) โ†’ translateY(15%) 500ms / 400ms

ํŠน์ˆ˜ ํšจ๊ณผ

ํŒจ๋Ÿด๋ ‰์Šค ํšจ๊ณผ

์–ธ์ œ: ์ฒ˜์Œ๋ถ€ํ„ฐ mo-large-full ํƒ€์ž…์œผ๋กœ ์„ค์ •๋œ ๋ชจ๋‹ฌ์—์„œ๋งŒ ํ™œ์„ฑํ™” ์ค‘์š”: ์ฝ˜ํ…์ธ  ์˜ค๋ฒ„ํ”Œ๋กœ์šฐ๋กœ ์ธํ•ด ๋™์ ์œผ๋กœ ํ’€์‚ฌ์ด์ฆˆ๋กœ ๋ณ€๊ฒฝ๋œ ๊ฒฝ์šฐ์—๋Š” ํŒจ๋Ÿด๋ ‰์Šค ํšจ๊ณผ๊ฐ€ ์ ์šฉ๋˜์ง€ ์•Š์Œ

์–ด๋–ป๊ฒŒ:

#main-content.parallax-effect {
    transform: translateX(-20%);
    transition: transform 600ms ease-out;
}

์ ์šฉ ์กฐ๊ฑด:

// ์›๋ณธ ํƒ€์ž…(originalType)์ด mo-large-full์ธ ๊ฒฝ์šฐ์—๋งŒ ํŒจ๋Ÿด๋ ‰์Šค ์ ์šฉ
const originalType = modalContainer.dataset.originalType;
if (originalType === 'mo-large-full') {
    mainContent.classList.add('parallax-effect');
}

ํ•ด์ œ ์กฐ๊ฑด:

// ๋ชจ๋‹ฌ ๋‹ซ์„ ๋•Œ ์›๋ณธ ํƒ€์ž…์ด mo-large-full์ธ ๊ฒฝ์šฐ์—๋งŒ ํŒจ๋Ÿด๋ ‰์Šค ํ•ด์ œ
if (modalContainer.dataset.originalType === 'mo-large-full') {
    mainContent.classList.remove('parallax-effect');
}

๐Ÿ”ง ํ•ต์‹ฌ ๊ธฐ๋Šฅ ๊ตฌํ˜„

1. ๋™์  ์ฝ˜ํ…์ธ  ์ƒ์„ฑ

๋ชฉ์ : ๋ชจ๋‹ฌ ํฌ๊ธฐ๋ณ„ ์ ์ ˆํ•œ ์ฝ˜ํ…์ธ  ์–‘ ์ œ๊ณต

const getParagraphCount = (type) => {
    const sizeMap = { 'small': 1, 'medium': 2, 'large': 10 };
    const size = Object.keys(sizeMap).find(s => type.includes(s)) || 'small';
    return sizeMap[size];
};

// Lorem Ipsum ๋ฌธ๋‹จ์„ ํฌ๊ธฐ๋ณ„๋กœ ๋™์  ์ƒ์„ฑ
loremContent.innerHTML = Array(paragraphCount)
    .fill(baseLoremText)
    .map(text => `<p class="lorem-paragraph">${text}</p>`)
    .join('');

2. ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ง€์†์‹œ๊ฐ„ ํŒŒ์‹ฑ

๋ชฉ์ : CSS ์• ๋‹ˆ๋ฉ”์ด์…˜ ์™„๋ฃŒ ์‹œ์  ์ •ํ™•ํ•œ ๊ฐ์ง€

const parseDuration = (animationStr) => {
    // CSS animation ์†์„ฑ์—์„œ duration + delay ์ถ”์ถœ
    const timeValues = animationStr.match(/\d+(\.\d+)?(ms|s)/g) || [];
    const timesInMs = timeValues.map(time => {
        const value = parseFloat(time);
        return time.endsWith('s') && !time.endsWith('ms') ? value * 1000 : value;
    });
    
    // ์ฒซ ๋‘ ๊ฐ’(duration, delay)์˜ ํ•ฉ ๋ฐ˜ํ™˜
    return timesInMs.slice(0, 2).reduce((sum, time) => sum + time, 0);
};

3. ๋ฐ˜์‘ํ˜• ๋ฆฌ์‚ฌ์ด์ฆˆ ํ•ธ๋“ค๋ง

๋ชฉ์ : ํ™”๋ฉด ํฌ๊ธฐ ๋ณ€๊ฒฝ ์‹œ ๋ชจ๋‹ฌ ๋ ˆ์ด์•„์›ƒ ์ž๋™ ์กฐ์ •

const handleResize = () => {
    const visibleModal = modalWrapper.querySelector('.modal-container:not(.hidden)');
    if (visibleModal) {
        checkAndAdjustModalType(visibleModal);
    }
};

// ๋””๋ฐ”์šด์‹ฑ์œผ๋กœ ์„ฑ๋Šฅ ์ตœ์ ํ™”
window.addEventListener('resize', () => {
    clearTimeout(resizeDebounceTimer);
    resizeDebounceTimer = setTimeout(handleResize, 150);
});

๐ŸŽฎ ์‚ฌ์šฉ์ž ์ธํ„ฐ๋ž™์…˜ ํ”Œ๋กœ์šฐ

๋ชจ๋‹ฌ ์—ด๊ธฐ ์‹œํ€€์Šค

graph TD
    A[๋ฒ„ํŠผ ํด๋ฆญ] --> B[openModal ํ˜ธ์ถœ]
    B --> C[body ์Šคํฌ๋กค ๋ฐฉ์ง€]
    C --> D[๋ชจ๋‹ฌ ํ‘œ์‹œ]
    D --> E[์ฝ˜ํ…์ธ  ์˜ค๋ฒ„ํ”Œ๋กœ์šฐ ์ฒดํฌ]
    E --> F{์ฝ˜ํ…์ธ  ์ดˆ๊ณผ?}
    F -->|Yes| G[ํ’€์‚ฌ์ด์ฆˆ๋กœ ๋ณ€๊ฒฝ]
    F -->|No| H[์›๋ž˜ ๋ ˆ์ด์•„์›ƒ ์œ ์ง€]
    G --> I{์›๋ณธ ํƒ€์ž…์ด<br/>mo-large-full?}
    H --> I
    I -->|Yes| J[ํŒจ๋Ÿด๋ ‰์Šค ํšจ๊ณผ ์ ์šฉ]
    I -->|No| K[ํŒจ๋Ÿด๋ ‰์Šค ํšจ๊ณผ ์—†์Œ]
    J --> L[์—ด๊ธฐ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์‹คํ–‰]
    K --> L
Loading

๋ชจ๋‹ฌ ๋‹ซ๊ธฐ ์‹œํ€€์Šค

graph TD
    A[๋‹ซ๊ธฐ ํŠธ๋ฆฌ๊ฑฐ] --> B{ํŠธ๋ฆฌ๊ฑฐ ํƒ€์ž…}
    B -->|์˜ค๋ฒ„๋ ˆ์ด ํด๋ฆญ| C[closeModal ํ˜ธ์ถœ]
    B -->|๋ฒ„ํŠผ ํด๋ฆญ| C
    B -->|ESC ํ‚ค| C
    C --> D{์›๋ณธ ํƒ€์ž…์ด<br/>mo-large-full?}
    D -->|Yes| E[ํŒจ๋Ÿด๋ ‰์Šค ํšจ๊ณผ ์ œ๊ฑฐ]
    D -->|No| F[ํŒจ๋Ÿด๋ ‰์Šค ํšจ๊ณผ ์—†์Œ]
    E --> G[๋‹ซ๊ธฐ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์‹œ์ž‘]
    F --> G
    G --> H[์˜ค๋ฒ„๋ ˆ์ด ๋”œ๋ ˆ์ด ์ ์šฉ]
    H --> I[์• ๋‹ˆ๋ฉ”์ด์…˜ ์™„๋ฃŒ ๋Œ€๊ธฐ]
    I --> J[๋ชจ๋‹ฌ ์ˆจ๊น€]
    J --> K[๋ ˆ์ด์•„์›ƒ ์›์ƒ๋ณต๊ตฌ]
    K --> L[body ์Šคํฌ๋กค ๋ณต์›]
Loading

๐Ÿ“ฑ ๋ฐ˜์‘ํ˜• ๋™์ž‘ ๊ทœ์น™

๋ชจ๋ฐ”์ผ ๋ชจ๋‹ฌ ํŠนํ™” ๊ธฐ๋Šฅ

  1. ์ฝ˜ํ…์ธ  ์˜ค๋ฒ„ํ”Œ๋กœ์šฐ ๊ฐ์ง€

    • ํ—ค๋” + ๋ณธ๋ฌธ + ํ‘ธํ„ฐ ๋†’์ด๊ฐ€ ํ™”๋ฉด ๋†’์ด ์ดˆ๊ณผ ์‹œ
    • ์ž๋™์œผ๋กœ mo-large-full ๋ ˆ์ด์•„์›ƒ์œผ๋กœ ์ „ํ™˜
  2. ๋ฐ”ํ…€ ์‹œํŠธ ํŒจํ„ด

    • mo-small-bottom, mo-medium-bottom์—์„œ ํ•˜๋‹จ์—์„œ ์˜ฌ๋ผ์˜ค๋Š” ์• ๋‹ˆ๋ฉ”์ด์…˜
    • iOS/Android ๋„ค์ดํ‹ฐ๋ธŒ UX์™€ ์ผ์น˜
  3. ์ „์ฒด ํ™”๋ฉด ๋ชจ๋‹ฌ

    • mo-large-full์—์„œ ์šฐ์ธก์—์„œ ์Šฌ๋ผ์ด๋“œ ์ธ
    • ์ฒ˜์Œ๋ถ€ํ„ฐ mo-large-full ํƒ€์ž…์ธ ๊ฒฝ์šฐ์—๋งŒ ๋ฐฐ๊ฒฝ ํŒจ๋Ÿด๋ ‰์Šค ํšจ๊ณผ๋กœ ๊นŠ์ด๊ฐ ์—ฐ์ถœ
    • ๋™์ ์œผ๋กœ ํ’€์‚ฌ์ด์ฆˆ ๋ณ€๊ฒฝ๋œ ๊ฒฝ์šฐ๋Š” ํŒจ๋Ÿด๋ ‰์Šค ํšจ๊ณผ ์—†์Œ

PC ๋ชจ๋‹ฌ ํŠนํ™” ๊ธฐ๋Šฅ

  1. ์„ผํ„ฐ ์ •๋ ฌ

    • ๋ชจ๋“  PC ๋ชจ๋‹ฌ์€ ํ™”๋ฉด ์ค‘์•™ ๋ฐฐ์น˜
    • ํฌ๊ธฐ๋ณ„ ๋‹ค๋ฅธ Y์ถ• ์ด๋™ ๊ฑฐ๋ฆฌ๋กœ ์ž์—ฐ์Šค๋Ÿฌ์šด ๋“ฑ์žฅ๊ฐ
  2. ํฌ๊ธฐ๋ณ„ ์ฐจ๋“ฑํ™”

    • Small: 50% Y ์ด๋™ (๋น ๋ฅธ ๋“ฑ์žฅ)
    • Medium: 30% Y ์ด๋™ (์ ๋‹นํ•œ ๋“ฑ์žฅ)
    • Large: 15% Y ์ด๋™ (๋ถ€๋“œ๋Ÿฌ์šด ๋“ฑ์žฅ)

๐Ÿ”ง ๊ธฐ์ˆ ์  ์ตœ์ ํ™”

์„ฑ๋Šฅ ์ตœ์ ํ™”

  1. GPU ๊ฐ€์† ํ™œ์šฉ
.modal-body, .modal-overlay {
    will-change: transform, opacity;
    transform: translateZ(0);
    backface-visibility: hidden;
}
  1. ์‚ฌ์ „ ๋ Œ๋”๋ง

    • ๋ชจ๋“  ๋ชจ๋‹ฌ DOM์„ ์ดˆ๊ธฐํ™” ์‹œ์ ์— ์ƒ์„ฑ
    • ๋Ÿฐํƒ€์ž„ DOM ์กฐ์ž‘ ์ตœ์†Œํ™”
  2. ๋””๋ฐ”์šด์‹ฑ

    • ๋ฆฌ์‚ฌ์ด์ฆˆ ์ด๋ฒคํŠธ 150ms ๋””๋ฐ”์šด์‹ฑ
    • ๋ถˆํ•„์š”ํ•œ ๋ ˆ์ด์•„์›ƒ ์žฌ๊ณ„์‚ฐ ๋ฐฉ์ง€

๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ

  1. ์ด๋ฒคํŠธ ์œ„์ž„

    • ๋‹จ์ผ ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ๋กœ ๋ชจ๋“  ๋ชจ๋‹ฌ ์ œ์–ด
    • ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ๋ฐฉ์ง€
  2. ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ƒํƒœ ์ •๋ฆฌ

// ์• ๋‹ˆ๋ฉ”์ด์…˜ ์™„๋ฃŒ ํ›„ GPU ๊ฐ€์† ํ•ด์ œ
.modal-container.hidden .modal-body,
.modal-container.hidden .modal-overlay {
    will-change: auto;
}

๐ŸŽฏ ํ™•์žฅ ๊ฐ€๋Šฅ์„ฑ

์ถ”๊ฐ€ ๋ชจ๋‹ฌ ํƒ€์ž… ์ •์˜

์ƒˆ๋กœ์šด ๋ชจ๋‹ฌ ํƒ€์ž… ์ถ”๊ฐ€ ์‹œ ํ•„์š”ํ•œ ๋‹จ๊ณ„:

  1. modalConfigs ๊ฐ์ฒด์— ์„ค์ • ์ถ”๊ฐ€
const modalConfigs = {
    // ๊ธฐ์กด ์„ค์ •...
    'new-modal-type': { 
        container: 'items-center justify-center p-8', 
        content: 'rounded-xl w-full max-w-sm' 
    }
};
  1. CSS ํ‚คํ”„๋ ˆ์ž„ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ •์˜
@keyframes modal-new-modal-type-open {
    from { transform: scale(0.8); opacity: 0; }
}

@keyframes modal-new-modal-type-close {
    to { transform: scale(0.8); opacity: 0; }
}
  1. ์• ๋‹ˆ๋ฉ”์ด์…˜ ํด๋ž˜์Šค ์Šคํƒ€์ผ ์ž‘์„ฑ
.modal-new-modal-type-open {
    animation: modal-new-modal-type-open 400ms ease-out;
}

.modal-new-modal-type-close {
    animation: modal-new-modal-type-close 300ms ease-in forwards;
}

๐Ÿšจ ์ฃผ์˜์‚ฌํ•ญ ๋ฐ ์ œ์•ฝ์‚ฌํ•ญ

๋ธŒ๋ผ์šฐ์ € ํ˜ธํ™˜์„ฑ

  • ์ตœ์†Œ ์š”๊ตฌ์‚ฌํ•ญ: ES6+ ์ง€์› ๋ธŒ๋ผ์šฐ์ €
  • CSS ์ง€์›: animation, transform, requestAnimationFrame
  • ๊ถŒ์žฅ ๋ธŒ๋ผ์šฐ์ €: Chrome 60+, Firefox 55+, Safari 12+

์„ฑ๋Šฅ ๊ณ ๋ ค์‚ฌํ•ญ

  1. ๋Œ€๋Ÿ‰ ์ฝ˜ํ…์ธ  ์ฒ˜๋ฆฌ

    • 10๊ฐœ ์ด์ƒ์˜ ๋ฌธ๋‹จ์—์„œ ์„ฑ๋Šฅ ์ €ํ•˜ ๊ฐ€๋Šฅ
    • ๊ฐ€์ƒ ์Šคํฌ๋กค๋ง ๊ตฌํ˜„ ๊ถŒ์žฅ
  2. ๋™์‹œ ๋ชจ๋‹ฌ ์ œํ•œ

    • ํ˜„์žฌ ๊ตฌ์กฐ์ƒ ๋‹จ์ผ ๋ชจ๋‹ฌ๋งŒ ์ง€์›
    • ์ค‘์ฒฉ ๋ชจ๋‹ฌ ํ•„์š” ์‹œ ์•„ํ‚คํ…์ฒ˜ ์ˆ˜์ • ํ•„์š”

์ ‘๊ทผ์„ฑ ๊ฐœ์„  ์—ฌ์ง€

  • ARIA ๋ผ๋ฒจ๋ง ๋ฏธ์ ์šฉ
  • ํฌ์ปค์Šค ํŠธ๋ž˜ํ•‘ ๋ฏธ๊ตฌํ˜„
  • ์Šคํฌ๋ฆฐ ๋ฆฌ๋” ์ตœ์ ํ™” ํ•„์š”

๐Ÿ“– ์‚ฌ์šฉ ์˜ˆ์‹œ

๊ธฐ๋ณธ ์‚ฌ์šฉ๋ฒ•

<!-- ๋ชจ๋‹ฌ ํŠธ๋ฆฌ๊ฑฐ ๋ฒ„ํŠผ -->
<button data-modal-type="pc-medium-center" class="modal-trigger">
    ์ค‘๊ฐ„ ํฌ๊ธฐ ๋ชจ๋‹ฌ ์—ด๊ธฐ
</button>

ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ฐฉ์‹ ๋ชจ๋‹ฌ ์ œ์–ด

// ๋ชจ๋‹ฌ ์—ด๊ธฐ
openModal('mo-small-center');

// ํŠน์ • ๋ชจ๋‹ฌ ๋‹ซ๊ธฐ
const modalContainer = document.getElementById('modal-container-pc-large-center');
closeModal(modalContainer);

// ํ˜„์žฌ ์—ด๋ฆฐ ๋ชจ๋‹ฌ ํ™•์ธ
const visibleModal = document.querySelector('.modal-container:not(.hidden)');

๐Ÿ”„ ์—…๋ฐ์ดํŠธ ๋กœ๋“œ๋งต

v1.1 ๊ณ„ํš ๊ธฐ๋Šฅ

  • ์ค‘์ฒฉ ๋ชจ๋‹ฌ ์ง€์›
  • ์ปค์Šคํ…€ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์„ค์ • API
  • ๋ชจ๋‹ฌ ํžˆ์Šคํ† ๋ฆฌ ๊ด€๋ฆฌ

v1.2 ๊ณ„ํš ๊ธฐ๋Šฅ

  • ์ ‘๊ทผ์„ฑ ๊ฐœ์„  (ARIA, Focus trap)
  • TypeScript ์ง€์›
  • ๋ชจ๋‹ฌ ๋“œ๋ž˜๊ทธ ์•ค ๋“œ๋กญ

v2.0 ๊ณ„ํš ๊ธฐ๋Šฅ

  • ๋ชจ๋‹ฌ ์ƒํƒœ ๊ด€๋ฆฌ (Redux/Zustand)
  • ์„œ๋ฒ„์‚ฌ์ด๋“œ ๋ Œ๋”๋ง ์ง€์›
  • ๋ชจ๋ฐ”์ผ ์ œ์Šค์ฒ˜ ์ธ์‹

์ด ๋ฌธ์„œ๋Š” Motion ๋ชจ๋‹ฌ ์‹œ์Šคํ…œ v1.0์„ ๊ธฐ์ค€์œผ๋กœ ์ž‘์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors