๐ ์๊ตฌ์ฌํญ
- panel๋ค์ ๋ด๊ณ ์๋ div๋ ํ๋ฉด์ ๊ฝ ์ฑ์ฐ๊ณ , ์ฒ์์๋ ๊ฐ๊ฐ์ panel๋ค์ด ํ๋ฉด์ ์ฐจ์งํ๋ ๋น์จ์ด ๋์ผํ๋ค.
- ๊ฐ panel๋ค์ ํด๋ฆญํ๋ฉด ํด๋ฆญ๋ ์์๊ฐ ์ฐจ์งํ๋ ๋น์จ, ํฐํธ ๋ฑ์ด ๋ณํํ๊ณ ์์๋ ํ ์คํธ๊ฐ ๋ํ๋๋ค.
- ํด๋ฆญ๋ panel์ ๋ค์ ํ ๋ฒ ํด๋ฆญํ๋ฉด ์ ์ฉ๋์๋ ํจ๊ณผ๋ค์ด ์ด๊ธฐํ ๋๋ค.
- ์์ ๋์๋ค์ ๊ณ์ํด์ ๋ฐ๋ณตํ๋ค.
โจ ๊ฒฐ๊ณผํ๋ฉด
๐ป ์์ค์ฝ๋
๐ ์ฃผ์ด์ง HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Flex Panels ๐ช</title>
<link
href="https://fonts.googleapis.com/css?family=Amatic+SC"
rel="stylesheet"
type="text/css"
/>
</head>
<body>
<div class="panels">
<div class="panel panel1">
<p>Hey</p>
<p>Let's</p>
<p>Dance</p>
</div>
<div class="panel panel2">
<p>Give</p>
<p>Take</p>
<p>Receive</p>
</div>
<div class="panel panel3">
<p>Experience</p>
<p>It</p>
<p>Today</p>
</div>
<div class="panel panel4">
<p>Give</p>
<p>All</p>
<p>You can</p>
</div>
<div class="panel panel5">
<p>Life</p>
<p>In</p>
<p>Motion</p>
</div>
</div>
</body>
</html>
๐ ๋ด๊ฐ ์์ฑํ ์ฝ๋
CSS
html { box-sizing: border-box; background: #ffc600; font-family: "helvetica neue"; font-size: 20px; font-weight: 200; }
body { margin: 0; }
*,
*:before,
*:after { box-sizing: inherit; }
.panels { width: 100vw; display: flex; flex-direction: row; min-height: 100vh; overflow: hidden; }
.panel { flex-grow: 1; display: flex; flex-direction: column; justify-content: space-between; background: #6b0f9c; box-shadow: inset 0 0 0 5px rgba(255, 255, 255, 0.1); color: white; text-align: center; align-items: center;
/* Safari transitionend event.propertyName === flex */
/* Chrome + FF transitionend event.propertyName === flex-grow */
transition: font-size 0.7s cubic-bezier(0.61, -0.19, 0.7, -0.11),
flex 0.7s cubic-bezier(0.61, -0.19, 0.7, -0.11), background 0.2s; font-size: 20px; background-size: cover; background-position: center; }
.panel1 { background-image: url(https://source.unsplash.com/gYl-UtwNg_I/1500x1500); }
.panel2 { background-image: url(https://source.unsplash.com/rFKUFzjPYiQ/1500x1500); }
.panel3 { background-image: url(https://images.unsplash.com/photo-1465188162913-8fb5709d6d57?ixlib=rb-0.3.5&q=80&fm=jpg&crop=faces&cs=tinysrgb&w=1500&h=1500&fit=crop&s=967e8a713a4e395260793fc8c802901d); }
.panel4 { background-image: url(https://source.unsplash.com/ITjiVXcwVng/1500x1500); }
.panel5 { background-image: url(https://source.unsplash.com/3MNzGlQM7qs/1500x1500); }
/* Flex Children */
.panel > * { margin: 0; width: 100%; transition: transform 0.5s; }
.panel p { text-transform: uppercase; font-family: "Amatic SC", cursive; text-shadow: 0 0 4px rgba(0, 0, 0, 0.72), 0 0 14px rgba(0, 0, 0, 0.45); font-size: 2em; }
.panel p:nth-child(odd) { visibility: hidden; opacity: 0; }
.panel p:nth-child(2) { font-size: 4em; }
.panel.open { font-size: 40px; flex-grow: 2; }
.panel.open p:nth-child(odd) { visibility: visible; opacity: 1; }
JS
// ๋ชจ๋ panel๋ค ๊ฐ์ ธ์์ click ์ด๋ฒคํธ ์ถ๊ฐ
const panels = document.querySelectorAll(".panel");
panels.forEach((panel) => panel.addEventListener("click", clickMotion));
panels.forEach((panel) => panel.addEventListener("mouseleave", endMotion));
function clickMotion() {
// open class๋ฅผ ์ถ๊ฐํด์ ํจ๊ณผ ์ ์ฉ
this.classList.add("open");
}
function endMotion() {
// visible์ฒ๋ฆฌ ๋์ด์๋ ํ์๋ฒ์งธ p๋ค์ ๋ชจ๋ ์ ํ
const p = this.querySelectorAll("p:nth-child(odd)");
this.classList.remove("open");
p.forEach((item) => {
item.style.animation = `bottom-to-top 0.7s`;
});
}
ํด๋น ์์๊ฐ click๋๋ฉด clickMotion()์ ํตํด open ํด๋์ค๋ฅผ ์ถ๊ฐํ๊ณ , ํด๋น ์์์์ ๋ง์ฐ์ค๊ฐ ํฌ์ปค์ค ์์๋๋ฉด endMotion()์ ํตํด open ํด๋์ค๋ฅผ ์ ๊ฑฐํด ์ฃผ์๋ค. ๊ทธ๋ฆฌ๊ณ endMotion()์์๋ forEach๋ฅผ ํตํด ์ ์ธํ animation์ ํ ๋นํด์ ์์๋์ ํ ์คํธ๊ฐ ์ฌ๋ผ์ด๋ฉ๋์ด ๋ํ๋๊ณ ์์ด์ง๋ ํจ๊ณผ๋ฅผ ์ฃผ๋ ค๊ณ ํ๋๋ฐ ์ ๋์ง๋ ์์๋ค๐..
๐ค ๊ฐ์ ๋ ๊ฐ์ ์ฝ๋
CSS
.panels { min-height: 100vh; overflow: hidden; display: flex; }
.panel { background: #6b0f9c; box-shadow: inset 0 0 0 5px rgba(255, 255, 255, 0.1);
color: white; text-align: center; align-items: center;
/* Safari transitionend event.propertyName === flex */
/* Chrome + FF transitionend event.propertyName === flex-grow */
transition: font-size 0.7s cubic-bezier(0.61, -0.19, 0.7, -0.11),
flex 0.7s cubic-bezier(0.61, -0.19, 0.7, -0.11), background 0.2s; font-size: 20px; background-size: cover;
background-position: center;
flex: 1; justify-content: center; align-items: center; display: flex; flex-direction: column; }
/* Flex items */
.panel > * { margin: 0; width: 100%; transition: transform 0.5s;
flex: 1 0 auto; display: flex; justify-content: center; align-items: center; }
.panel > *:first-child { transform: translateY(-100%); }
.panel.open-active > *:first-child { transform: translateY(0); }
.panel > *:last-child { transform: translateY(100%); }
.panel.open-active > *:last-child { transform: translateY(0); }
.panel.open { flex: 5; font-size: 40px; }
๋๋ ์์๋ ํ ์คํธ๋ฅผ ์ฌ๋ผ์ด๋ฉํด์ ๋ํ๋๊ฒ ํ๋ ํจ๊ณผ๋ฅผ ์ด๋ ต๊ฒ ์๊ฐํ์๋๋ฐ, ์๊ฐ๋ณด๋ค ๊ฐ๋จํ๋ค. ๋จ์ํ๊ฒ open-active ํด๋์ค๋ฅผ ํ์ฉํ๊ณ , translateY(0)๊ณผ translateY(100%), translateY(-100%)๋ง ์ฌ์ฉํ๋ฉด ์ฝ๊ฒ ํํํ ์ ์๋ค.
JS
const panels = document.querySelectorAll(".panel");
function toggleOpen() {
this.classList.toggle("open");
}
function toggleActive(e) {
if (e.propertyName.includes("flex")) {
this.classList.toggle("open-active");
}
}
panels.forEach((panel) => panel.addEventListener("click", toggleOpen));
panels.forEach((panel) =>
panel.addEventListener("transitionend", toggleActive)
);
๐ TIL
flex
flex๋ CSS ์์ฑ ์ค ํ๋๋ก, ํ๋์ ํ๋ ์ค ์์ดํ ์ด ์์ ์ ์ปจํ ์ด๋๊ฐ ์ฐจ์งํ๋ ๊ณต๊ฐ์ ๋ง์ถ๊ธฐ ์ํด ํฌ๊ธฐ๋ฅผ ํค์ฐ๊ฑฐ๋ ์ค์ด๋ ๋ฐฉ๋ฒ์ ์ค์ ํ๋ ์์ฑ์ด๋ค. flex๋ flex-grow, flex-shrink, flex-basis์ ๋จ์ถ ์์ฑ์ด๋ค.
- flex-grow: flex-item ์์๊ฐ flex-container ์์ ๋ด๋ถ์์ ํ ๋น ๊ฐ๋ฅํ ๊ณต๊ฐ์ ์ ๋๋ฅผ ์ ์ธํ๋ค.
- flex-shrink: ์ค์ ๋ ์ซ์๊ฐ์ ๋ฐ๋ผ flex-container ์์ ๋ด๋ถ์์ flex-item ์์์ ํฌ๊ธฐ๊ฐ ์ถ์๋๋ค.
- flex-basis: flex-item ์์์ ์ด๊ธฐ ํฌ๊ธฐ๋ฅผ ์ง์ ํ๋ค.
flex ์์ฑ์ 1~3๊ฐ์ ๊ฐ์ ์ฌ์ฉํด์ ์ง์ ํ ์ ์๋ค.
1๏ธโฃ ๊ฐ์ด ํ ๊ฐ์ผ ๋
- <number>๋ฅผ ์ง์ ํ๋ฉด flex-grow
- <length>๋๋ <percentage>๋ฅผ ์ง์ ํ๋ฉด flex-basis
- none, auto, initial์ค ํ๋๋ฅผ ์ง์ ํ ์ ์๋ค.
2๏ธโฃ ๊ฐ์ด ๋ ๊ฐ์ผ ๋
- ์ฒซ ๋ฒ์งธ ๊ฐ์ <number>์ฌ์ผ ํ๋ฉฐ ์ด๋ ๊ณง flex-grow๊ฐ ๋๋ค.
- ๋ ๋ฒ์งธ ๊ฐ์ ๋ค์ ์ค ํ๋์ฌ์ผ ํ๋ค.
- <number>๋ฅผ ์ง์ ํ๋ฉด flex-shrink
- <length>, <percentage>, ๋๋ auto๋ฅผ ์ง์ ํ๋ฉด flex-basis
3๏ธโฃ ๊ฐ์ด ์ธ ๊ฐ์ผ ๋
- 1. flex-grow์ ์ฌ์ฉํ <number>
- 2. flex-shrink์ ์ฌ์ฉํ <number>
- 3. flex-basis์ ์ฌ์ฉํ <length>, <percentage>, ๋๋ auto
๊ฐ์ ์ฝ๋์์๋ flex ์์ฑ์ ๋ค์๊ณผ ๊ฐ์ด ์ฌ์ฉํ์๋ค.
/* flex-grow */
.panel { flex: 1; }
/* flex-grow | flex-shrink | flex-basis */
.panel > * { flex: 1 0 auto; }
๊ฐ CSS ์์๋ค์ flex item์ด ๋ ์ ์์๊ณผ ๋์์, flex container ๋ํ ๋ ์ ์๋ค. (๋ด๊ฐ ์ด๋๊น์ง flex๋ฅผ ์ฌ์ฉํ๋ฉด์๋ ๊ฐ์ฅ ๊ถ๊ธํ์๊ณ , ํด๊ฒฐํ๊ณ ์ถ์๋ ๋ถ๋ถ์ด๋ค!)
toggle()
toggle()์, on/off switch์ ๊ฐ๋ ์ด๋ค. ๊ทธ๋์ ์ด๋ค ๊ธฐ๋ฅ A๋ฅผ ๋ถ์ฌํ๋ค๊ณ ํ์ ๋, A๊ฐ ์ผ์ก๋ค ๊บผ์ก๋ค๋ฅผ ๋ฐ๋ณตํ๊ฒ ๋๋ค. ์ด๊ฒ์ add(), remove() ๋ฉ์๋๋ฅผ ํตํด์๋ ๊ตฌํํ ์ ์๋ค. ์ฆ, toggle()์ add()์ remove() ๋ฉ์๋๊ฐ ํฉ์ณ์ง ๊ฐ๋ ์ด๋ผ๊ณ ์๊ฐํ๋ฉด ๋๋ค. ๋ณดํต์ click์ด๋ฒคํธ์ classList๋ฅผ ์ด์ฉํ์ฌ toggle๋ก css style์ ์ค ํด๋์ค๋ช ์ on/off ์ฒ๋ฆฌํ๋ค.
๊ฐ์ ์ฝ๋์์๋ toggle()์ ๋ค์๊ณผ ๊ฐ์ด ํ์ฉํ์๋ค.
function toggleOpen() {
this.classList.toggle("open");
}
function toggleActive(e) {
if (e.propertyName.includes("flex")) {
this.classList.toggle("open-active");
}
}
toggle์ ํตํด์ ๊ฐ๊ฐ open๊ณผ open-active ํด๋์ค๋ฅผ addํ๊ณ removeํ๋ ํ๋์ ๋ฐ๋ณตํ๊ฒ ๋ง๋ค์๋ค. ๊ทธ๋์ ์ด๋ ๊ฒ ํ๋ฉด, open๋๊ณ active๋๊ณ deactive๋๊ณ close๋๋ ๋ฑ์ ํจ์๋ค์ ๋ค ๋ฐ๋ก ๋ง๋ค ํ์ ์์ด, ๊ฐ๊ฒฐํ๊ฒ ์ฝ๋๋ฅผ ์์ฑํ ์ ์๋ค.
๐ ์ฐธ๊ณ
youtube Wes Bos
https://www.youtube.com/watch?v=9eif30i26jg&list=PLu8EoSxDXHP6CGK4YVJhL_VWetA865GOH&index=5
https://developer.mozilla.org/ko/docs/Web/CSS/flex
https://goddino.tistory.com/129