0
\$\begingroup\$

What I'm doing

  1. Divide members into five section, and apply animation for each member to reveal at first.

  2. When I click the section, it is expanded to whole viewport and introduce detail about that member.

  3. In the expanded part, fade-in animation is applied for the detail part of member.

  4. Also, animations which is revealed from left/right are applied to the photos of that member.

In this context, I wanna effectively manage control of multiple animations for one page. However, for the shortage of my ability, I cannot judge what I'm doing in code is proper for this situation. Here is my code.

const initializeMembers = function initializeMembers() {
  const groupInside = document.getElementById('group__inside-js');
  const groupMembers = document.querySelectorAll('.group__member');
  const singles = document.querySelectorAll('.single');
  const closeBtn = document.getElementById('closeBtn-js');
  const header = document.getElementById('header-js');
  const HIDE_CLASS = 'hide';
  const SHOW_CLASS = 'show';

  const showcloseBtn = function showcloseBtn() {
    closeBtn.classList.remove(HIDE_CLASS);
    closeBtn.classList.add(SHOW_CLASS);
  };

  const hidecloseBtn = function hidecloseBtn() {
    closeBtn.classList.remove(SHOW_CLASS);
    closeBtn.classList.add(HIDE_CLASS);
  };

  const showExpandedMembers = function showExpandedMembers(index) {
    singles[index].classList.remove(HIDE_CLASS);
    singles[index].classList.add(SHOW_CLASS);
  };

  const hideExpandedMembers = function hideExpandedMembers() {
    document.querySelector('.show').classList.replace(SHOW_CLASS, HIDE_CLASS);
  };

  const hideHeader = function hideHeader() {
    header.classList.remove(SHOW_CLASS);
    header.classList.add(HIDE_CLASS);
  };

  const showHeader = function showHeader() {
    header.classList.remove(HIDE_CLASS);
    header.classList.add(SHOW_CLASS);
  };

  groupMembers.forEach((member, index) => {
    member.addEventListener('mouseenter', () => {
      groupMembers.forEach((innerMmber, innerIndex) => {
        if (innerIndex !== index) {
          innerMmber.classList.remove('group__member--active');
        } else {
          innerMmber.classList.add('group__member--active');
        }
      });
    });
    member.addEventListener('click', () => {
      groupMembers.forEach((innerMember) => {
        if (innerMember.classList.contains('group__member--shrink')) {
          innerMember.classList.remove('group__member--shrink');
        }
      });
      member.classList.add('group__member--expand');
      showExpandedMembers(index);
      showcloseBtn();
      hideHeader();
    });
  });

  groupInside.addEventListener('mouseleave', () => {
    groupMembers.forEach(member => member.classList.add('group__member--active'));
  });

  // window.addEventListener('resize', () => {
  //   if (document.documentElement.offsetHeight <= 768) {
  //     groupMembers.forEach(member => deactivateAnimationOfMember(member));
  //   }
  // });

  closeBtn.addEventListener('click', () => {
    hidecloseBtn();
    hideExpandedMembers();
    document.querySelector('.group__member--expand').classList
      .replace('group__member--expand', 'group__member--shrink');
    showHeader();
  });
};

const removeDefaultAnimations = function removeDefaultAnimations() {
  document.querySelectorAll('.group__member').forEach((member) => {
    member.addEventListener('animationend', () => { member.classList.add('no-animation'); });
  });
};

const initializePhotos = function initializePhotos() {
  const io = new IntersectionObserver((entries, observer) => {
    entries.forEach((entry) => {
      if (entry.isIntersecting) {
        entry.target.classList.add('intersect');
        observer.unobserve(entry.target);
      }
    });
  }, { rootMargin: '0px 0px 200px 0px' });
  const photos = document.querySelectorAll('.photo');
  photos.forEach(photo => io.observe(photo));
};

window.onload = () => {
  initializeMembers();
  removeDefaultAnimations();
  initializePhotos();
};
body {
  background-color: white;
}

.group__member {
  position: absolute;
  min-height: 100vh;
  width: 20vw;
  transition: all 0.5s;
  filter: grayscale(100%);
  background-position: center;
  background-size: cover;
  background-repeat: no-repeat;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  animation-duration: 0.9s;
  animation-fill-mode: backwards;
  flex-basis: 0;
  cursor: pointer;
}

.group__member h2 {
  color: rgba(0, 0, 0, 0.7);
  margin-bottom: 6rem;
  font-size: 4rem;
  font-family: 'Libre Caslon Text', serif;
  transition: font-size 0.5s;
  animation-name: fadeInFromBottom;
  animation-duration: 1s;
  animation-delay: 1.2s;
  animation-fill-mode: backwards;
}

.group__member:nth-child(1) {
  background-image: url('../images/home-irene.jpg');
  animation-name: appearFromTop;
  animation-delay: 0.2s;
  left: 0;
}

.group__member:nth-child(2) {
  background-image: url('../images/home-seulgi.jpg');
  animation-name: appearFromBottom;
  animation-delay: 0.4s;
  left: 20vw;
}

.group__member:nth-child(3) {
  background-image: url('../images/home-wendy.jpg');
  animation-name: appearFromTop;
  animation-delay: 0.6s;
  left: 40vw;
}

.group__member:nth-child(4) {
  background-image: url('../images/home-yeri.jpg');
  animation-name: appearFromBottom;
  animation-delay: 0.8s;
  left: 60vw;
}

.group__member:nth-child(5) {
  background-image: url('../images/home-joy.jpg');
  animation-name: appearFromTop;
  animation-delay: 1s;
  left: 80vw;
}

.group__member:hover {
  transform: scale(1.1);
}

.group__member:hover h2 {
  font-size: 5rem;
}

.group__member--active {
  filter: grayscale(0%);
}

.group__member.group__member--expand {
  left: 0;
  top: 0;
  width: 100%;
  z-index: 2;
  background-image: none;
  background-color: white;
  animation: expand 0.5s both;
}

.group__member.group__member--shrink {
  width: 20%;
  z-index: 1;
  background-color: white;
  animation: shrink 0.5s both;
}

.group__member.group__member--expand h2 {
  text-indent: -9999px;
}

.closeBtn {
  position: absolute;
  right: 5%;
  top: 5%;
  display: none;
  z-index: 3;
  cursor: pointer;
  font-size: 10rem;
  animation: fadeInBasic 1s both 0.5s;
}

.single {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  display: none;
  background-color: white;
  z-index: 3;
  animation: fadeInBasic 1s both 0.5s;
}

.single__contents {
  height: 100vh;
  position: relative;
  margin-bottom: 5rem;
}

.single__main {
  position: absolute;
  top: 10%;
  left: 10%;
  width: 60vw;
}

.single__main__title {
  position: absolute;
  left: -10%;
  top: -10%;
  color: rgba(0, 0, 0, 0.4);
  font-size: 10rem;
  z-index: 1;
}

.single__main__title span {
  font-size: 5rem;
}

.single__main__profile {
  position: relative;
}

.single__main__profile::after {
  content: ' ';
  z-index: -1;
  position: absolute;
  left: 5%;
  top: 5%;
  width: 100%;
  height: 100%;
  border: 5px solid #2ebce2;
}

.single__detail {
  line-height: 2;
}

.single__detail__spec {
  position: absolute;
  font-size: 3rem;
  top: 35%;
  right: 3%;
  color: rgba(0, 0, 0, 0.8);
  width: 40rem;
}

.single__detail__spec span {
  font-weight: bold;
}

.single__detail__spec .line1,
.single__detail__spec .line2,
.single__detail__spec .line3,
.single__detail__spec .line4 {
  animation-name: fadeInFromBottom;
  animation-duration: 1s;
  animation-fill-mode: both;
  display: inline-block;
  opacity: 0;
}

.single__detail__spec .line1 { animation-delay: 0.3s; }
.single__detail__spec .line2 { animation-delay: 0.6s; }
.single__detail__spec .line3 { animation-delay: 0.9s; }
.single__detail__spec .line4 { animation-delay: 1.2s; }

.single__detail__saying {
  position: absolute;
  width: 40rem;
  font-size: 2rem;
  bottom: 5%;
  right: 5%;
  color: rgba(0, 0, 0, 0.8);
  opacity: 0;
  animation: fadeInFromBottom 1s both 1.5s;
}

.single__photo {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 10px;
}

.photo {
  max-width: 35vw;
  margin-left: 10rem;
  align-self: flex-start;
  transform: translateX(20%);
  opacity: 0;
}

.photo:nth-child(even) {
  margin-right: 10rem;
  margin-top: -10rem;
  align-self: flex-end;
}

.photo.intersect:nth-child(odd) {
  animation: showPhotoFromRight 1s both 0.5s;
}

.photo.intersect:nth-child(even) {
  animation: showPhotoFromLeft 1s both 0.5s;
}

.hide { display: none; }
.show { display: block; }
.no-animation { animation: none; }

/* Animation */

@keyframes appearFromBottom {
  from { margin-top: 100vh; }
  to { margin-top: 0; }
}

@keyframes appearFromTop {
  from { margin-top: -100vh; }
  to { margin-top: 0; }
}

@keyframes appearFromRight {
  from { margin-left: 100vw; }
  to { margin-left: 0; }
}

@keyframes appearFromLeft {
  from { margin-left: -100vw; }
  to { margin-left: 0; }
}

@keyframes fadeInFromBottom {
  from {
    opacity: 0;
    margin-top: 1rem;
  }

  to {
    opacity: 1;
    margin-top: 0;
  }
}

@keyframes fadeInBasic {
  from { opacity: 0; }
  to { opacity: 1; }
}

@keyframes expand {
  from { width: 20%; }
  to { width: 100%; }
}

@keyframes shrink {
  from { width: 100%; }
  to { width: 20%; }
}

@keyframes expandMobile {
  from { height: 20%; }
  to { height: 100%; }
}

@keyframes shrinkMobile {
  from { height: 100%; }
  to { height: 20%; }
}

@keyframes showPhotoFromRight {
  from {
    transform: translateX(20%);
    opacity: 0;
  }

  to {
    transform: translateX(0);
    opacity: 1;
  }
}

@keyframes showPhotoFromLeft {
  from {
    transform: translateX(-20%);
    opacity: 0;
  }

  to {
    transform: translateX(0);
    opacity: 1;
  }
}

/* Media Query */

/* Tablet */

@media (max-width: 992px) {
  .single__main__title { top: -20%; }
  .single__detail { line-height: 1.5; }
  .single__detail__spec { top: 45%; }
}

/* Mobile */

@media (max-width: 768px) {
  .header {
    position: static;
    padding: 1rem 1rem;
    height: 10vh;
    background-color: white;
  }

  .group {
    height: 100vh;
    position: absolute;
  }

  .group__member {
    width: 100vw;
    min-height: 20%;
    background-position: 0% 30%;
    justify-content: flex-start;
  }

  .group__member h2 {
    margin-left: 4rem;
  }

  .group__member:nth-child(1) {
    top: 0%;
    left: 0;
    animation-name: appearFromLeft;
  }

  .group__member:nth-child(2) {
    top: 20%;
    left: 0;
    animation-name: appearFromRight;
  }

  .group__member:nth-child(3) {
    top: 40%;
    left: 0;
    animation-name: appearFromLeft;
  }

  .group__member:nth-child(4) {
    top: 60%;
    left: 0;
    animation-name: appearFromRight;
  }

  .group__member:nth-child(5) {
    top: 80%;
    left: 0;
    animation-name: appearFromLeft;
  }

  .group__member.group__member--expand {
    top: 0;
    width: 100vw;
    height: 100%;
    animation: expandMobile 0.5s both;
  }

  .group__member.group__member--shrink {
    height: 20%;
    width: 100vw;
    z-index: 1;
    background-color: white;
    animation: shrinkMobile 0.5s both;
  }

  .single__contents {
    height: auto;
  }

  .single__main,
  .single__main__title,
  .single__detail__spec,
  .single__detail__saying {
    position: static;
  }

  .single__main,
  .single__detail__spec,
  .single__detail__saying {
    width: 80vw;
    margin: 0 auto;
  }
  .single__main__title { font-size: 5rem; }
  .single__main__title span { font-size: 2.5rem; }

  .single__detail__spec {
    font-size: 2rem;
    margin-bottom: 2rem;
  }
  .single__detail__saying { font-size: 1.5rem; }

  .single__detail__spec,
  .single__detail__saying {
    line-height: 1.5;
  }

  .closeBtn {
    font-size: 5rem;
    top: 1%;
  }

  .photo {
    max-width: 80vw;
    margin-left: 0;
    margin-bottom: 2rem;
  }

  .photo:nth-child(even) {
    margin-right: 0;
    margin-top: 0;
  }
}
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <meta name="description" content="레드벨벳 팬사이트 소개 페이지">
  <base href="https://raw.githack.com/baeharam/Redvelvet-Fansite/master/html/">
  <link rel="stylesheet" href="../css/default.css">
  <link rel="stylesheet" href="../css/about.css">
  <title>About</title>
</head>
<body>
  <header id="header-js" class="header">
      <div class="header__inside clearfix">
          <a href="./index.html">
              <img id="header__logo-js" class="header__logo" src="../images/logo-red.png" alt="로고">
          </a>
          <div id="header__menu-js" class="header__menu dark">
              <input type="checkbox" id="menuicon">
              <label for="menuicon">
                  <span class="menu"></span>
                  <span class="menu"></span>
                  <span class="menu"></span>
              </label>
          </div>
      </div>
      <h1>소개</h1>
  </header>
  <aside id="overlay-js" class="overlay">
      <a href="javascript:void(0)" id="overlay__closeBtn-js" class="overlay__closeBtn">&times;</a>
      <nav class="overlay-menu">
          <ul>
          <li><a href="./about.html">ABOUT</a></li>
          <li><a href="./photo.html">PHOTOS</a></li>
          <li><a href="./discography.html">DISCOGRAPHY</a></li>
          <li><a href="./video.html">VIDEOS</a></li>
          </ul>
      </nav>
  </aside>
  <main class="main">
    <section id="redvelvet" class="group">
      <div id="group__inside-js">
        <div class="group__member group__member--active">
          <h2>IRENE</h2>
        </div>
        <div class="group__member group__member--active">
          <h2>SEULGI</h2>
        </div>
        <div class="group__member group__member--active">
          <h2>WENDY</h2>
        </div>
        <div class="group__member group__member--active">
          <h2>YERI</h2>
        </div>
        <div class="group__member group__member--active">
          <h2>JOY</h2>
        </div>
      </div>
    </section>
    <section id="irene" class="single">
      <div class="single__contents">
        <div class="single__main">
          <p class="single__main__title">IRENE <span>배주현</span></p>
          <div class="single__main__profile">
            <img src="../images/about-irene.jpg" alt="" class="single__main__img">
          </div>
        </div>
        <div class="single__detail">
          <p class="single__detail__spec">
            <span class="line1">생일. 1991.03.29</span><br/>
            <span class="line2">별명. 배추, 현이, 엔딩요정</span><br/>
            <span class="line3">취미. 다리미질, 빨래</span><br/>
            <span class="line4">혈액형. A형</span>
          </p>
          <p class="single__detail__saying">
            "20대는 힘들고 아파도 괜찮다는 생각을 했어요. 그래야 단단해진다고도 생각했고요. 
            그런데 어느 순간은 스스로 감당하고 있던 것들이 좀 힘들 때도 있더라고요. 
            하지만 이런 때도 있고, 저런 때도 있다고 생각하며 힘을 내요."
          </p>
        </div>
      </div>
      <div class="single__photo">
        <img class="photo" src="../images/photo-irene1.jpg" alt="아이린">
        <img class="photo" src="../images/photo-irene2.jpg" alt="아이린">
        <img class="photo" src="../images/photo-irene3.jpg" alt="아이린">
        <img class="photo" src="../images/photo-irene4.jpg" alt="아이린">
        <img class="photo" src="../images/photo-irene5.jpg" alt="아이린">
        <img class="photo" src="../images/photo-irene6.jpg" alt="아이린">
      </div>
    </section>
    <section id="seulgi" class="single">
      <div class="single__contents">
        <div class="single__main">
          <p class="single__main__title">SEULGI <span>강슬기</span></p>
          <div class="single__main__profile">
            <img src="../images/about-seulgi.jpg" alt="" class="single__main__img">
          </div>
        </div>
        <div class="single__detail">
          <p class="single__detail__spec">
            <span class="line1">생일. 1994.02.10</span><br/>
            <span class="line2">별명. 곰돌이, 곰슬기</span><br/>
            <span class="line3">취미. 노래방, 그림그리기</span><br/>
            <span class="line4">혈액형. A형</span>
          </p>
          <p class="single__detail__saying">
            "아직 부족한데도 저희를 많이 좋아해주고 기대해주는 사람이 많아져서 행복해요. 
            그때마다 속으로 다시 다짐하곤 하죠. 내가 보여주고자 하는 걸 더 잘하자고 말이에요."
          </p>
        </div>
      </div>
      <div class="single__photo">
        <img class="photo" src="../images/photo-seulgi1.jpg" alt="슬기">
        <img class="photo" src="../images/photo-seulgi2.jpg" alt="슬기">
        <img class="photo" src="../images/photo-seulgi3.jpg" alt="슬기">
        <img class="photo" src="../images/photo-seulgi4.jpg" alt="슬기">
        <img class="photo" src="../images/photo-seulgi5.jpg" alt="슬기">
        <img class="photo" src="../images/photo-seulgi6.jpg" alt="슬기">
      </div>
    </section>
    <section id="wendy" class="single">
      <div class="single__contents">
        <div class="single__main">
          <p class="single__main__title">WENDY <span>손승완</span></p>
          <div class="single__main__profile">
            <img src="../images/about-wendy.jpg" alt="" class="single__main__img">
          </div>
        </div>
        <div class="single__detail">
          <p class="single__detail__spec">
            <span class="line1">생일. 1994.02.21</span><br/>
            <span class="line2">별명. 완이, 컨셉요정</span><br/>
            <span class="line3">취미. 베이킹</span><br/>
            <span class="line4">혈액형. O형</span>
          </p>
          <p class="single__detail__saying">
            "저는 음악을 너무 좋아했어요. 힘들 때 음악을 들으면 노래도 같이 슬프게 들리긴 
            하지만 반대로 일어날 수 있는 힘이 생기기도 하더라고요. 
            음악으로부터 받은 긍정적 영향을 다른 사람들과도 공유하고 싶었어요."
          </p>
        </div>
      </div>
      <div class="single__photo">
        <img class="photo" src="../images/photo-wendy1.jpg" alt="웬디">
        <img class="photo" src="../images/photo-wendy2.jpg" alt="웬디">
        <img class="photo" src="../images/photo-wendy3.jpg" alt="웬디">
        <img class="photo" src="../images/photo-wendy4.jpg" alt="웬디">
        <img class="photo" src="../images/photo-wendy5.jpg" alt="웬디">
        <img class="photo" src="../images/photo-wendy6.jpg" alt="웬디">
      </div>
    </section>
    <section id="yeri" class="single">
      <div class="single__contents">
        <div class="single__main">
          <p class="single__main__title">YERI <span>예리</span></p>
          <div class="single__main__profile">
            <img src="../images/about-yeri.jpg" alt="" class="single__main__img">
          </div>
        </div>
        <div class="single__detail">
          <p class="single__detail__spec">
            <span class="line1">생일. 1999.03.05</span><br/>
            <span class="line2">별명. 림이, 예부기</span><br/>
            <span class="line3">취미. 게임, 일본드라마 보기</span><br/>
            <span class="line4">혈액형. O형</span>
          </p>
          <p class="single__detail__saying">
            "새벽 시간이 가장 많지만 때로는 집에서 
            침대에 누워 동생들이 떠들고 노는 소리에도 멜로디가 떠올라 
            작업을 할 때가 있어요. 그리고 음악 작업을 할 때 가사를 무척 중요하게 여기거든요. 
            아직 가사를 쓰지 않았지만 '스물에게'라고 제목만 붙여놓은 멜로디도 있어요."
          </p>
        </div>
      </div>
      <div class="single__photo">
        <img class="photo" src="../images/photo-yeri1.jpg" alt="예리">
        <img class="photo" src="../images/photo-yeri2.jpg" alt="예리">
        <img class="photo" src="../images/photo-yeri3.jpg" alt="예리">
        <img class="photo" src="../images/photo-yeri4.jpg" alt="예리">
        <img class="photo" src="../images/photo-yeri5.jpg" alt="예리">
        <img class="photo" src="../images/photo-yeri6.jpg" alt="예리">
      </div>
    </section>
    <section id="joy" class="single">
      <div class="single__contents">
        <div class="single__main">
          <p class="single__main__title">JOY <span>조이</span></p>
          <div class="single__main__profile">
            <img src="../images/about-joy.jpg" alt="" class="single__main__img">
          </div>
        </div>
        <div class="single__detail">
          <p class="single__detail__spec">
            <span class="line1">생일. 1996.09.03</span><br/>
            <span class="line2">별명. 둥둥이, 셩이</span><br/>
            <span class="line3">취미. 영화 보기</span><br/>
            <span class="line4">혈액형. A형</span>
          </p>
          <p class="single__detail__saying">
            "새벽 시간이 가장 많지만 때로는 집에서 
            침대에 누워 동생들이 떠들고 노는 소리에도 멜로디가 떠올라 
            작업을 할 때가 있어요. 그리고 음악 작업을 할 때 가사를 무척 중요하게 여기거든요. 
            아직 가사를 쓰지 않았지만 '스물에게'라고 제목만 붙여놓은 멜로디도 있어요."
          </p>
        </div>
      </div>
      <div class="single__photo">
        <img class="photo" src="../images/photo-joy1.jpg" alt="조이">
        <img class="photo" src="../images/photo-joy2.jpg" alt="조이">
        <img class="photo" src="../images/photo-joy3.jpg" alt="조이">
        <img class="photo" src="../images/photo-joy4.jpg" alt="조이">
        <img class="photo" src="../images/photo-joy5.jpg" alt="조이">
        <img class="photo" src="../images/photo-joy6.jpg" alt="조이">
      </div>
    </section>
    <div id="closeBtn-js" class="closeBtn">&times;</div>
  </main>
  <script src="../js/about.js"></script>
  <script src="../js/default.js"></script>
</body>
</html>

From former review of my code, I learned lots of effective technique to use CSS/JS. Therefore, I tried to use those concepts to this part too. However, I cannot sure whether my code is optimal and effective solution. So, I want to review for my above code.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.