[JavaScript30] 5. Flexbox + JavaScript Image Gallery
language/javascript

[JavaScript30] 5. Flexbox + JavaScript Image Gallery

๐Ÿ“ƒ ์š”๊ตฌ์‚ฌํ•ญ

  1. panel๋“ค์„ ๋‹ด๊ณ ์žˆ๋Š” div๋Š” ํ™”๋ฉด์„ ๊ฝ‰ ์ฑ„์šฐ๊ณ , ์ฒ˜์Œ์—๋Š” ๊ฐ๊ฐ์˜ panel๋“ค์ด ํ™”๋ฉด์„ ์ฐจ์ง€ํ•˜๋Š” ๋น„์œจ์ด ๋™์ผํ•˜๋‹ค.
  2. ๊ฐ panel๋“ค์„ ํด๋ฆญํ•˜๋ฉด ํด๋ฆญ๋œ ์š”์†Œ๊ฐ€ ์ฐจ์ง€ํ•˜๋Š” ๋น„์œจ, ํฐํŠธ ๋“ฑ์ด ๋ณ€ํ™”ํ•˜๊ณ  ์œ„์•„๋ž˜ ํ…์ŠคํŠธ๊ฐ€ ๋‚˜ํƒ€๋‚œ๋‹ค.
  3. ํด๋ฆญ๋œ panel์„ ๋‹ค์‹œ ํ•œ ๋ฒˆ ํด๋ฆญํ•˜๋ฉด ์ ์šฉ๋˜์—ˆ๋˜ ํšจ๊ณผ๋“ค์ด ์ดˆ๊ธฐํ™” ๋œ๋‹ค.
  4. ์œ„์˜ ๋™์ž‘๋“ค์„ ๊ณ„์†ํ•ด์„œ ๋ฐ˜๋ณตํ•œ๋‹ค.

 

โœจ ๊ฒฐ๊ณผํ™”๋ฉด

์šฉ๋Ÿ‰์ด ์ปค์„œ ์ตœ๋Œ€ ์ƒ‰์ƒ ๋น„ํŠธ ์ˆ˜๋ฅผ ๋‚ฎ์ถฐ์„œ ์ €์žฅํ–ˆ๋”๋‹ˆ ์–ด๋”˜๊ฐ€ ๋‚ก์€ ์›€์งค์ด ๋๋‹ค..ใ„ทใ„ท

 

๐Ÿ’ป ์†Œ์Šค์ฝ”๋“œ

๐Ÿ“Œ ์ฃผ์–ด์ง„ 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

 

https://velog.io/@hxyxneee/JS-practice-%ED%86%A0%EA%B8%80-%EB%B2%84%ED%8A%BC-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0