8. Tamburins 반응형 WEB 제작

wj·2022년 9월 26일
1

포트폴리오 작업

목록 보기
1/7
post-thumbnail

📌
사이트 명: Tamburins 반응형 (Redesign)
제작 기간: 22.09.16 ~ 22.09.20 (5일 소요)
사용 언어: html, scss, jQuery, java script

[Tamburins 반응형 WEB 제작 작업]

GSAP ScrollTrigger 라이브러리를 활용하여 다양한 스크롤 애니메이션 기능들을 구현한 반응형 웹 페이지입니다.

💡Learning point

  • 메뉴 hover 애니메이션
  • 메인 텍스트 효과
  • 동영상 모달 창
  • gsap 애니메이션
  • 좌우 슬라이드 영역

1. 메뉴 hover 애니메이션

  • 해당 li영역 hover시, 하위 영역이 슬라이드 다운 되는 구조의 효과를 구현했다. 해당 영역 말고 다른 영역으로 마우스가 이동하면 열렸던 영역은 닫히고, 마우스가 hover된 영역이 active되도록 구현

먼저, closeSub()이라는 함수에 hover 해제 시에 실행될 모션들을 담아두고, 해당 영역의 hover가 해제될 때 호출해서 전체 영역을 닫은 후에 해당 영역만 이벤트를 지정해서 실행시켰다.

function closeSub() {
  $(".hover-wrap").removeClass("active");
  $(".btn-search").removeClass("open");
  $(".btn-packaging").removeClass("open");
  $("body").removeClass("hide");
  $(".group-sub").removeClass("active");
  $(".header .border").removeClass("active");
  $(".dimmed").css("display", "none");
}

1-1. gnb메뉴

상단 gnb 메뉴에는 products 항목에만 하위 영역이 있어서
해당 영역에만 active 클래스가 추가되도록 적용했다.

//gnb 메뉴 열고 닫기
$(".gnb-item a").mouseover(function (e) {
  //전체 hover영역 닫기
  closeSub();

  //해당 영역 data값이 있는 경우에만 active 클래스 추가
  target = $(this).data("hover");
  $(target).addClass("active").siblings().removeClass("active");
  utilMenuMotion.play();

  //active 클래스 있을 때
  if ($(target).hasClass("active")) {
    $(".dimmed").css("display", "block");
    $(".header .border").addClass("active");
    $("body").addClass("hide");

    //active 클래스 없을 때
  } else {
    $(".dimmed").css("display", "none");
    $(".header .border").removeClass("active");
    $("body").removeClass("hide");
  }
});

//해당 영역에서 mouseleave 되면 전체 영역 닫기
$(".group-sub").mouseleave(function (e) {
  closeSub();
  $(".dimmed").css("display", "none");
  utilMenuMotion.reverse();
});

1-2. util 영역

좌측 유틸 영역 버튼 하버 시, 해당 하위 영역 슬라이드 다운 되도록 설정

$(".util-area button").mouseover(function (e) {
  e.preventDefault();
  //먼저 전체 영역 닫아주기
  closeSub();
  target = $(this).data("target");
  $(target).addClass("active").siblings(".hover-wrap").removeClass("active");
  $("body").addClass("hide");
  utilMenuMotion.play();

  //discover 영역에 mouseover 됐을 때
  if (target == "#hover01") {
    $(".btn-packaging").addClass("open");
    $(".dimmed").css("display", "block");
    //search 영역에 mouseover 됐을 때
  } else if (target == "#hover02") {
    $(".btn-search").addClass("open");
    $(".dimmed").css("display", "block");
    //둘 다 아닐 경우엔 둘 다 해제
  } else {
    $(".btn-packaging").removeClass("open");
    $(".btn-search").removeClass("open");
  }
});

1-3. 스크롤 후 버튼 hover 시, header 영역 배경 해제

스크롤 시 변경되는 header영역 디자인을 메뉴 hover시에는 해제 시키기

$(".util-area button").mouseover(function (e) {
  e.preventDefault();

  target = $(this).data("target");

//해당 버튼 영역이 open 상태일 때,
  if ($(this).hasClass("open")) {
  //header 배경 제거
    $(".header-inner").removeClass("background");
  //아닌 경우엔 배경 추가
  } else {
    $(".header-inner").addClass("background");
  }
});

2. 메인 텍스트 효과

  • perspective로 원근값을 주고, preserve-3d를 적용하여 3D 텍스트를 구현했다.

  • 같은 구조의 영역을 두 개 만들어서 스크롤 시, 고정된 뒷 영역이 나타나도록 구현했고, 위에 있는 영역은 height값을 조절해서 자연스럽게 스크롤 되도록 설정했다.

[css]

.visual-wrap {
  height: 100vh;
  width: 100%;
  border-radius: 0 0 40px 40px;
  
  //하얀색 글씨 영역
  .front-cont {
    position: absolute;
    z-index: 10;
    top: 0;
    left: 0;
    margin: 0 auto;
    width: 100%;
    height: calc(100vh - 20px);
    border-radius: 0 0 40px 40px;
    overflow: hidden;
    ..이하 생략
  }
  
  //검정색 글씨 영역
  .back-cont {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: calc(100vh - 20px);
    overflow: hidden;
    border-radius: 0 0 40px 40px;
    
    //스크롤을 내렸을 때 active되면서 검정색 글씨가 보이게
    &.active {
      position: sticky;
    }
  }
  .title {
    position: absolute;
    z-index: 20;
    top: 0;
    left: 0;
    width: 100%;
    height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;

    .main-txt {
      position: absolute;
      top: 25%;
      left: 50%;
      transform: translateX(-50%);
      font-size: 128px;
      width: 1040px;
      padding: 40px;
      line-height: 1;
      color: #fff;
      font-weight: 300;
      
      //글자가 누운 상태에서 등장하도록 원근값을 설정
      perspective: 400px;
      text-align: center;
      font-family: "DMserif";
      span {
        display: block;
      }
      ..이하 생략
    }
  }
}

[js]

//메인 비주얼 영역
gsap.to(".visual-wrap .front-cont", {
  scrollTrigger: {
    trigger: ".visual-wrap",
    start: "top top",
    end: "bottom top",
    // markers: true,
    scrub: 1,
    pin: true
  },
  //높이값이 0이 되도록
  height: 0
});

//3d 텍스트 효과
gsap.set(".main-txt span", {
  yPercent: 110,
  transformStyle: "preserve-3d",
  rotationX: 90,
  
  //transform을 적용할 x,y,z축 값 설정
  transformOrigin: "0% 80% -100%"
});

gsap.to(".main-txt span", 1, {
  delay: 0.2,
  yPercent: 0,
  rotationX: 0
});

3. 동영상 모달 창

  • 영상 재생 버튼 클릭 시, html에 해당 구조가 추가되고, 닫기 버튼을 누르면 해당 구조가 사라지도록 스크립트를 작성했다.

[html]

//재생 버튼에 영상 data값 작성, 모달창은 스크립트에서 동작하도록 마크업 생략
<div class="group-video">
  <div class="txt">
    <strong class="title" data-opacity>Our<br>commitment</strong>
  </div>
  <div class="btn-wrap"><a href="#" class="btn-play" data-video="XDhsibRT9rU"><span class="blind">영상 재생하기</span></a></div>
</div>
//video 모달 팝업

$(".group-video .btn-play").click(function (e) {
  e.preventDefault();
  
  //해당 영상 data 주소 값 받아오기
  url = $(this).data("video");
  
  //추가될 html 영역
  html = `<div class="video-modal">
             <div class="dimmed1"></div>
             <a href="" class="btn-close" role="button"><i class="bi bi-x"><span class="blind">닫기</span></i></a>
             <div class="video-wrapper">
                <iframe width="560" height="315" src="https://www.youtube.com/embed/${url}" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
             </div>
          </div>`;

//해당 영역에 작성된 구조 추가
  $(".sc-expertise").append(html);
});

//닫기 버튼과 dimmed영역 클릭시 모달 창 닫히도록
$(document).on("click", ".btn-close, .dimmed1", function (e) {
  e.preventDefault();
  $(".video-modal").remove();
});

4. gsap 애니메이션

텍스트는 스크롤 시 fade up되고, line은 width값이 조절되도록 모션 적용

//sc-approach 영역
$("[data-fade]").each(function (i, l) {
  gsap.from(".sc-approach .txt-wrap", {
    scrollTrigger: {
      trigger: l,
      start: "top 50%",
      end: "bottom 70%",
      // markers: true,
      scrub: 1
    },
    opacity: 0,
    yPercent: 20
  });
});

gsap.from("[data-line]", 1, {
  scrollTrigger: {
    trigger: ".sc-approach .txt-wrap",
    start: "top 50%",
    end: "bottom 60%",
    scrub: 1
    // markers: true,
  },
  width: 0
});

스크롤 시, 두 개의 텍스트가 좌우 양 끝에서 각각 등장하도록 모션 적용

[html]
data값으로 이동시킬 x축의 값을 담는다

<h2 class="txt-scroll">
  <span class="txt left" data-x="-100">Discover</span>
  <span class="txt right" data-x="80">our brands</span>
</h2>

[js]

//텍스트 스크롤 영역
const slideTxt = document.querySelectorAll("[data-x]");
slideTxt.forEach((el) => {

//해당 텍스트의 x값을 변수에 담는다
  xVal = el.dataset.x;

  gsap.from(el, {
    scrollTrigger: {
      trigger: ".sc-approach .link-more",
      start: "bottom 100%",
      end: "+=200%",
      // markers: true,
      scrub: 1
    },
    
    //각 x축 값만큼 이동하도록 설정
    xPercent: xVal
  });
});

5. 슬라이드 영역

  • 왼쪽 영역은 평범한 스크롤 동작이고 오른쪽 영역은 왼쪽 영역이 바뀔 때마다 달라붙듯이 이미지가 스크롤되는..? 해당 영역마다 왼쪽 배경 색도 바껴야함

5-1. 좌우 영역 애니메이션

만들면서 제일 어려웠던.. 슬라이드 영역..ㅠ 오른쪽 영역에 position: sticky 적용하면 될 것 같았는데 도저히 모르겠어서 scrolltrigger로 제작했다.

[html]

<ul class="content-list">
//각 영역 data 값으로 적용시킬 배경 색상 설정
  <li class="content-item bg01" data-color="#7ab5cc" id="nav01">
    ..이하 생략
  </li>
  <li class="content-item bg02" data-color="#97a880" id="nav02">
    ..이하 생략
  </li>
  <li class="content-item bg03" data-color="#636363" id="nav03">
    ..이하 생략
  </li>
  <li class="content-item bg04" data-color="#e1cd5d" id="nav04">
    ..이하 생략
  </li>
</ul>

[css]

//각 이미지가 높이 꽉 찬 상태로 스크롤 되도록 높이값 설정
.content-slide {
  position: relative;
  width: 100%;
  height: 400vh;
}

[js]

//좌우 슬라이드

$(".content-left .content-item").each(function (i, el) {
  target = $(".content-right .content-item").eq(i);
  
  //해당 영역 순서에 따라 y축 스크롤 되도록 변수 값 저장
  yVal = i == 3 ? 0 : -100;
  
  ScrollTrigger.create({
    trigger: el,
    start: "0% 30%",
    end: "bottom top",
    
    //애니메이션이 진행되는 동안 함께 동작하도록 onUpdate 콜백 함수 적용
    onUpdate: function (self) {
    //data 값에 담아둔 색상 값을 호출해서 적용
      gsap.to(".sc-brands .content-left", { background: el.dataset.color });
    }
  });

  gsap.to(target, 1, {
    scrollTrigger: {
      trigger: el,
      start: "0% 0%",
      end: "bottom top",
      // markers: true,
      scrub: 0
    },
    ease: "none",
    yPercent: yVal
  });
});

5-2. navigation 구현

  • 해당 nav 클릭 시, 해당 href 값으로 이동하도록
  • 해당 영역 도달 시, 해당 nav active

[html]

<nav id="navbar">
  <ul class="nav-list">
    <li class="nav-item"><a id="navigation" href="#nav01" data-target="nav01" class="active">CANDLE</a></li>
    <li class="nav-item"><a id="navigation" href="#nav02" data-target="nav02">PERFUME</a></li>
    <li class="nav-item"><a id="navigation" href="#nav03" data-target="nav03">BODY</a></li>
    <li class="nav-item"><a id="navigation" href="#nav04" data-target="nav04">HAND</a></li>
  </ul>
</nav>

[js]

// 네비게이션
$(".content-right .nav-item a").click(function (e) {
  e.preventDefault();

//클릭된 target의 href값 변수에 저장
  target = $(this).attr("href");
  //클릭된 a에 active 클래스 주기
  $(this).addClass("active");
  //해당되지 않은 영역에는 active 클래스 제거
  $(".nav-item a").removeClass("active");

//클릭된 href값 영역으로 이동
  gsap.to(window, { duration: 0.5, scrollTo: target });
});


//스크롤 스파이 제작
var link = $("#navbar a");

$(window).on("scroll", function () {
  findPosition();
});

function findPosition() {
  $(".content-left .content-item").each(function () {
  
  	//해당 스크롤 영역 도달하면 
    if ($(this).offset().top - $(window).scrollTop() < 10) {
    //전체 요소 active 해제 후,
      link.removeClass("active");
    //해당 nav의 target값과 왼쪽 li영역 id값이 일치하는 nav에 active 클래스 부여
      $("#navbar")
        .find('[data-target="' + $(this).attr("id") + '"]')
        .addClass("active");
    }
  });
}
findPosition();
profile
tistory로 옮겼습니다

0개의 댓글