본문 바로가기

Starbucks

20210528 StarbucksClone03 : Notice 수직 슬라이드(Swiper.js), 반응형 요소 중간 배치, Promotion 수평 이미지 슬라이드(Pagination, Navigation Botton), 슬라이드 Toggle, Rewards 영역, 요소 비율 맞추기, 유튜브 영상 넣..

Starbucks Clone03



Notice (공지) : 수직 슬라이드


  • 슬라이드가 되는 공지사항을 구현하는 것이다.

HTML

  • 새로운 영역을 만드는 것이기 때문에 section 태그를 사용한다.
  • notice section의 경우 좌우로 나뉘어서 영역을 차지하고 있고, 6:4 비율을 가지고 있다.
    • 배경 색깔만 지정하는 bg-left class, bg-right class 요소가 있고
    • 본격적인 내용은 앞에서 정의한 inner class 요소를 사용하여 중앙 정렬을 유지한다.
      • inner class 요소 안에서도 좌우를 나누어 inner__leftinner__right가 존재함
      • inner__left 의 경우
        • 공지사항 글자 부분
        • swiper-container라는 공지가 슬라이드 되는 부분
        • notice-line__more 이라는 공지사항을 더보는 아이콘(material-icons add_circle)이 들어있는 부분이 있다.
      • inner__right 의 경우
        • 스타벅스 글자 부분
        • toggle-promotion이라는 프로모션에 대한 정보를 띄우는 아이콘(material-icons upload)이 들어 있는 부분이 있다.
<section class="notice">
  <div class="notice-line">
    <div class="bg-left"></div>
    <div class="bg-right"></div>
    <div class="inner">
      <div class="inner__left">
        <h2>공지사항</h2>
        <div class="swiper-container"></div>
        <a href="javascript:void(0)" class="notice-line__more">
          <div class="material-icons">add_circle</div>
        </a>
      </div>
      <div class="inner__right">
        <h2>스타벅스 프로모션</h2>
        <div class="toggle-promotion">
          <div class="material-icons">upload</div>
        </div>
      </div>
    </div>
  </div>
</section>

CSS

  • 여기서 작업은 배경을 지정하거나, 주로 flex로 요소를 정렬 하거나, 요소 간의 비율을 맞추는 것
  1. 배경 같은 경우에는 요소를 차지하고 있어 absolute 값을 통해서 inner와의 연결을 끊어 독립적으로 처리하였다. (inner에 영향을 주지 않음)
  2. 요소간의 비율을 맞출 때는 width 값에 %단위를 사용해서 맞출 수 있다.
  3. flex에서 사용되는 justify-content, align-items 에대한 값으로 center, flex-start, flex-end가 있다는 것을 기억하자 (rigth, left가 아니다.)
    • display: flex 의 특징으로 내부 요소에 width 값이 없으면 최소한으로 사용하므로 0이 될수도 있기에 width값을 잘 넣자!
    • 기초적인 container인 inner에서 height 값을 지정함으로써 높이를 유지하도록 한다. (주변 요소들 및 부모 요소의 크기도 inner에 의해서 같이 변하게 됨.)
    • flex 사용시 여러 요소들이 존재할 때 어느 하나의 요소가 container의 나머지를 채울수 있게 하고 싶은 경우 flex-grow: 1를 사용해서 나머지를 차지하게 할 수 있다. (여기에서는 swiper-container에 적용 되었다.)
/* 핵심 포인트만 CSS 코드 예시를 넣음 (notice css 전부가 아님 주의!) */

.notice .notice-line .inner .inner__left {
  width: 60%;
  height: 100%;
  background-color: #333;
  display: flex;
  align-items: center;
}

.notice .notice-line .inner .inner__left .swiper-container {
  background-color: orange;
  height: 62px;
  flex-grow: 1;
}
.notice .notice-line .inner .inner__left .notice-line__more {
  width: 62px;
  height: 62px;
  display: flex;
  justify-content: center;
  align-items: center;
}

.notice .notice-line .inner .inner__right {
  width: 40%;
  height: 100%;
  display: flex;
  justify-content: flex-end;
  align-items: center;
}

Swiper.JS로 수직 슬라이드 구현하기


Swiper.js

  • Swiperjs site
  • Swiperjs : installation-cdn
    • : cdn으로 제공하는 코드에는 원본이 있고 min 버전이 있는데, min 버전은 압축되어 조금더 최적화 되어 있어 min 버전을 활용하는 것을 추천
    • JS 파일뿐만 아니라 CSS 파일에 대한 연결도 필요하므로 두개 모두 코드를 연결시켜 줄것!

Swiper HTML

  • slide는 무조건 swiper-wrapper 로 묶어야 하는것 주의할 것!
  • Pagination, Navigation button, Scrollbar 같은 경우는 swiper-container에 있어도 되고 없어도 됨, 하지만 슬라이드를 가릴 것을 우려한다면, 밖으로 빼는게 좋음
    • 단, wrapper 부분에 들어가면 안됨
<!-- Slider main container -->
<div class="swiper-container">
  <!-- Additional required wrapper -->
  <div class="swiper-wrapper">
    <!-- Slides -->
    <div class="swiper-slide">Slide 1</div>
    <div class="swiper-slide">Slide 2</div>
    <div class="swiper-slide">Slide 3</div>
    ...
  </div>
  <!-- If we need pagination -->
  <div class="swiper-pagination"></div>

  <!-- If we need navigation buttons -->
  <div class="swiper-button-prev"></div>
  <div class="swiper-button-next"></div>

  <!-- If we need scrollbar -->
  <div class="swiper-scrollbar"></div>
</div>

Swiper JS

  • new Swiper(선택자, 옵션)
  • 옵션
    • direction : 슬라이드할 방향
    • autoplay : 자동 재생
    • loop : 끝나면 처음 부터
// new Swiper(선택자, 옵션)
new Swiper(".notice-line .swiper-container", {
  direction: "vertical",
  autoplay: true,
  loop: true,
});



Promotion : 요소 가운데 배치하기 (반응형, 요소 중간 지점 유지)


  • 단순히 요소를 가운데 배치하는 것은 absolute, margin, flex의 방법으로 하면 쉽게 할 수 있다.
    • 하지만, 해당 방법은 반응형 웹에서 사용할 때 절대적으로 요소의 중앙을 사용자의 화면에서 가운데를 유지하지는 못한다. (요소 자체 보다 창이 작아지는 경우)
    • margin 방법으로 하게 되면 보통 창이 줄어들면 해당 여백까지만 좌우로 줄어 들어 중간을 유지하지만 요소 보다 창이 작아지게 되면 요소의 우측 부터 줄어 들게 된다.
  • inner width 보다 큰 요소를 제어하는 경우
  • 그래서 만약 요소 중간에 중요한 정보가 있어 창이 줄어 들어도 창이 요소의 중앙을 출력하고자 하면 이 방법을 사용 해야 한다.

HTML

<div class="promotion">
  <div class="swiper-container">
    <div class="swiper-wrapper">TEST!</div>
  </div>
</div>

CSS

  • 전체적인 컨셉은 inner 보다 큰 요소에서 absolute의 기준점을 화면에 50% 부분으로 줌으로써 기본적으로 요소가 중앙에서 시작되게 한다.
  • 그리고 요소의 width 크기를 계산해서 요소의 width 의 절반 만큼 다시 margin으로 재 위치 시킴으로써 중앙에 배치할 수있다.
  • 요소의 크기를 계산할 때 편하게 쓸수 있는 calc()라는 함수가 있다.
    • calc 함수 사용의 예로 내부적으로 쓰이는 컨텐츠 크기와 margin을 합산하여 전체 width를 쉽게 계산 가능하다.
    • 또한, 여기에서는 width의 절반을 계산할 때도 사용된다.
.notice .promotion {
    height: 693px;
    background-color: #f6f5ef;
    position: relative;
}
.notice .promotion .swiper-container {
    width: calc(819px * 3 + 20px);
    height: 553px;
    background-color: orange;
    text-align: center;
    font-size: 200px;
    position: absolute;
    top: 40px;
    left: 50%;
    margin-left: calc((819px * 3 + 20px) / -2);



Promotion : 수평 이미지 슬라이드 (Pagination, Botton)


  • 위에서는 Promotion에 대한 전체적인 중앙 배치를 해보았고 이번에는, Promotion 부분에 수평 이미지 슬라이드 기능을 구현할 것이다.
  • 해당 슬라이드는 Pagination 과 Naviagtion이 존재함
    • pagination : 슬라이드들이 몇장인지, 어떤 슬라이드가 focus되어 있는지 그리고 특정 슬라이드로 이동할수 있는 UI 이다.
    • navigation : 다음 슬라이드로 이동하는 버튼
  • 위의 슬라이더 기능은 위에서와 같이 Swiper.js를 사용함

HTML

  • swiper-container
    • swiper-wrapper
      • slide * 5 (슬라이드 5개)
        • img (해당 이미지)
        • a 태그 버튼 (자세히 보기 버튼)
  • swiper-pagination (특정 슬라이드 이동 UI)
  • swiper-prev (이전 슬라이드 이동 버튼)
    • icon : arrow_bakc
  • swiper-next (다음 슬라이드 이동 버튼)
    • icon : arrow_forward
  • pagination, navigation(prev, next)
    • wrapper 범위 침범 주의
<div class="promotion">
  <div class="swiper-container">
    <div class="swiper-wrapper">
      <div class="swiper-slide">
        <img
          src="./images/promotion_slide1.jpg"
          alt="2021 뉴이어, 스타벅스와 함께 즐겁고 활기차게 시작하세요!"
        />
        <a href="javascript:void(0)" class="btn">자세히 보기</a>
      </div>
      <div class="swiper-slide">
        <img
          src="./images/promotion_slide2.jpg"
          alt="기간 내 스타벅스 카드 e-Gift를 3만원 이상 선물 시, 아메리카노 e-쿠폰을 드립니다."
        />
        <a href="javascript:void(0)" class="btn">자세히 보기</a>
      </div>
      <div class="swiper-slide">
        <img
          src="./images/promotion_slide3.jpg"
          alt="뉴이어 푸드와 제조 음료를 세트로 구매 시, 뉴이어 음료 BOGO(1+1) 쿠폰을 드립니다."
        />
        <a href="javascript:void(0)" class="btn">자세히 보기</a>
      </div>
      <div class="swiper-slide">
        <img
          src="./images/promotion_slide4.jpg"
          alt="신년 MD 상품 포함 3만원 이상 구매 고객께 아메리카노(톨사이즈) 쿠폰을 드립니다."
        />
        <a href="javascript:void(0)" class="btn">자세히 보기</a>
      </div>
      <div class="swiper-slide">
        <img
          src="./images/promotion_slide5.jpg"
          alt="2017 DIGITAL LUCKY DRAW 100% 당첨의 행운을 드립니다!"
        />
        <a href="javascript:void(0)" class="btn">자세히 보기</a>
      </div>
    </div>
  </div>
  <div class="swiper-pagination"></div>
  <div class="swiper-prev">
    <div class="material-icons">arrow_back</div>
  </div>
  <div class="swiper-next">
    <div class="material-icons">arrow_forward</div>
  </div>
</div>

CSS

Slide CSS

  • dev tool를 통해서 slide에 대한 class를 확인해 보면 active된 slide에는 swiper-slide-active 클래스가 들어 간다는 것을 알수 있다.
    • .swiper-slide에 opacity를 줘서 기본적으로 조금 투명하게 만들고, swiper-slide-active 클래스에 opacity를 1로 줌으로서 현재 active가 된 slide를 뚜렷하게 하여 차이를 줌
    • 슬라이드 안에 들어갈 자세히 버튼의 경우 .btn을 붙였기 때문에 width없이 absolute, margin으로 배치함
.notice .promotion .swiper-slide {
  opacity: 0.3;
  transition: opacity 1s;
  position: relative;
}
.notice .promotion .swiper-slide-active {
  opacity: 1;
}
.notice .promotion .swiper-slide .btn {
  position: absolute;
  /* width: 130px;  .btn에 width가 있기 때문에*/
  bottom: 0;
  left: 0;
  right: 0;
  margin: auto;
}

Pagination CSS

  • .swiper-pagination 의 경우 라이브러리에서 기본적으로 position: absolute, text-align: center이 들어가 있음
    • pagination을 구성하는 요소인 pagination-bulletdiplay: inlin-block으로 되어 있기 때문에 pagination-align: center로 pagination이 가운데 정렬이 가능하고 width 값도 추산이 되기 때문에 또 가운데 정렬이 가능함
  • 이처럼 이런 것들을 알려면, 라이브러리에서 제공하는 공식문서만 보지 말고 dev tool로 직접 CSS를 확인해 가봐면서 분석해야함
  • outline 속성은 focus 효과를 없애줌(swiper 라이브러리에서 acive에서 따로 focus를 지정하고 있기 때문에 없애도 됨)
  • swiper-pagination-bullet 클래스를 통해서 bullet 커스텀이 가능함
  • swiper-pagination-bullet-acitve로 active 상태인 경우도 커스텀이 가능함
/* 정렬 */
.notice .promotion .swiper-pagination {
  bottom: 40px;
  left: 0;
  right: 0;
  z-index: 0;
}

/* bullet 커스텀 */
.notice .promotion .swiper-pagination .swiper-pagination-bullet {
  background-color: transparent;
  background-image: url("../images/promotion_slide_pager.png");
  width: 12px;
  height: 12px;
  margin-right: 6px;
  outline: none;
}
.notice .promotion .swiper-pagination .swiper-pagination-bullet:last-child {
  margin-right: 0;
}

.notice .promotion .swiper-pagination .swiper-pagination-bullet-active {
  background-image: url("../images/promotion_slide_pager_on.png");
}

Navigation CSS

  • Navigation의 경우, Navigation요소를 기본적으로 동그라미 모양으로 만듦
  • 각 버튼 prev, next도 화면 가운데에 계속 유지하면서 배치하기 위해서 50%, margin 방법을 사용함
/* 여러개의 선택자 사용시 줄바꿈을 해주자 가독성을 위해 */
.notice .promotion .swiper-prev,
.notice .promotion .swiper-next {
  width: 42px;
  height: 42px;
  border: 2px solid #333;
  border-radius: 50%;
  position: absolute;
  top: 300px;
  z-index: 1;
  cursor: pointer;
  outline: none;
  display: flex;
  justify-content: center;
  align-items: center;
  transition: 0.4s;
}
.notice .promotion .swiper-prev {
  left: 50%;
  margin-left: -480px;
}
.notice .promotion .swiper-next {
  right: 50%;
  margin-right: -480px;
}

.notice .promotion .swiper-prev:hover,
.notice .promotion .swiper-next:hover {
  color: #fff;
  background-color: #333;
}



슬라이드 영역 토글


  • 앞서 notice 부분에 있던 promotion toggle키를 활성화 시켜 promotion 슬라이드를 접었다 폈다하고자 함
  • 해당 기능을 구현하기 위해서는 JS을 통해서 구현할 수 있다.

JS

  • 일단 promotiontoggle-promotion을 제어하기 위해서 document를 통해서 요소를 가져온다.
  • 숨기거나 안숨기는 상태를 표시하기 위해서 isHidePromotion 변수를 let으로 만들어 true, false를 왔다 갔다 할수 있게 한다.
  • 그리고 toggle-promotion에 eventListener를 달아서 클릭시 callback 함수를 실행시키킨다.
    • callback함수는 isHidePromotion의 상태를 ! 부정연산자를 통해 반대로 변경하여 재할당 해준다.
    • 그리고 if문으로 isHidePromotion의 상태에 따라 .hide 클래스를 classList에 넣거나 제거하도록 코드를 짠다.
const promotionEl = document.querySelector(".promotion");
const promotionToggleBtn = document.querySelector(".toggle-promotion");
let isHidePromotion = false;
promotionToggleBtn.addEventListener("click", function () {
  isHidePromotion = !isHidePromotion;
  if (isHidePromotion) {
    promotionEl.classList.add("hide");
  } else {
    promotionEl.classList.remove("hide");
  }
});

CSS

  • JS를 통해서 hide라는 클래스를 제어하는 데 hide가 해당 요소를 안보이게 CSS 속성을 지정해야 한다.
  • 요소를 안보이게 하는 방법은 두가지가 있다.
    • display: none;
    • hieght: 0;
      • 단, display: none의 경우 transition 속성을 지정해도 반응하지 않기 때문에 자연스러운 애니메이션을 넣고자 한다면 hieght: 0으로 넣어야 한다.
      • hieght 같은 경우 줄인다고 해서 안에 있는 요소가 넘쳐 보일 수도 있기 때문에 overflow: hidden으로 완전히 안보이게 해야한다.
.notice .promotion {
  height: 693px;
  background-color: #f6f5ef;
  position: relative;
  transition: height 0.4s;
  overflow: hidden;
}
.notice .promotion.hide {
  /* display: none; transition을 줄수 없어서 사용하지 않음*/
  height: 0;
}

Rewards 영역 구현하기


  • 시각적으로 배경을 block으로 사용하고 내용은 inner에 들어가고, 해당 inner에 버튼들이 있는 형태
  • inner에 들어가는 사진이 좌우의 색깔이 달라서 좌우여백을 해당 색깔을 이어 나가고자 함
    • 그래서 notice에서 썻던 방법인 bg-left, bg-right 요소를 넣고 absolute로 독립적으로 배경으로 활용하여 inner의 배경을 이어준다.
  • 그리고 오르쪽에 위치한 버튼들은 btn-group으로 묶어 배치한다.
    • 회원 가입 버튼의 경우, 차이점을 두어 btn--reverse class를 사용했다.

HTML

<section class="rewards">
  <div class="bg-left"></div>
  <div class="bg-right"></div>
  <div class="inner">
    <div class="btn-group">
      <div class="btn btn--reverse sign-up">회원가입</div>
      <div class="btn sign-in">로그인</div>
      <div class="btn gift">e-Gift 선물하기</div>
    </div>
  </div>
</section>

CSS

  • 해당 CSS 처리과정에서 기억해야 할 것은
    • btn-group 에서 flex-wrap: wrap을 사용해서 버튼들의 나열에서 줄을 만들게 했고
    • btn-gift 에서 flex-grow: 1을 통해서 줄에 남은 여백을 모조리 채울수 있게 했다는 것
/* REWARDS */

.rewards .btn-group {
  position: absolute;
  bottom: 24px;
  right: 0;
  width: 250px;
  display: flex;
  flex-wrap: wrap;
}

.rewards .btn-group .btn.gift {
  margin-top: 10px;
  flex-grow: 1;
}

요소 비율 맞추기

  • padding 은 % 단위 사용시 부모의 width를 기준으로 한다.
    • (부모가 height가 존재해도 width 값을 기준으로 함)
  • 그렇게 하여 padding 값때문에 부모 요소는 높이를 가지게 되어 화면에 표시가 된다.
  • 즉, 자식 padding 에 %를 넣어 부모는 width값을 입력하면 그 비율에 맞게 height 값이 계산되어 표현이 됨
  • 이를 활용해서 56.25% 값을 넣으면 일반적인 영상 비율인 9:16 비율을 유지하는 요소를 만들 수 있다.

See the Pen vYxegLe by RaccoonCode96 (@raccooncode96) on CodePen.




유튜브 영상 넣기 (Youtube Iframe API)



JS

  • var 변수 키워드는 현재 쓰는 cont 및 let을 사용해도 상관 없다.
  • script 태그의 요소를 바디에 생성해서 tag변수에 할당하고
  • tag에 src 속성에 youtube API를 넣어 연결함
  • 바디에 있는 script 태그중 첫번째 것을 가져와서 firstScriptTag에 할당함
  • firstScriptTag 요소의 부모 요소(parentNode)의 내부에서 tag요소를 firstScriptTag 앞에 삽입함
    • 부모노드.insertBefore(삽입할 노드, 부모 내부의 기준점 노드)
    • 부모노드의 내부에서 기준점 노드의 앞에 삽입할 노드를 삽입함
  • javascript가 모두 읽히면 onYouTubeIframeAPIReady() 함수가 실행되는데
    • onYouTubeIframeAPIReady 함수
      • YT클래스의 Player 함수를 통해서 해당 영상을 실행함
        • Player('player를 넣을 요소의 id', {옵션})
// This code loads the IFrame Player API code asynchronously.
var tag = document.createElement("script");

tag.src = "https://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName("script")[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

// 3. This function creates an <iframe> (and YouTube player)
//    after the API code downloads.
function onYouTubeIframeAPIReady() {
  // 이름 변경 하면 안됨
  new YT.Player("player", {
    videoId: "An6LvWQuj_8", // 최초 재생할 유튜브 영상 ID
    playerVars: {
      autoplay: true, // 자동 재생 유무
      loop: true, // 반복 재생 유무
      playlist: "An6LvWQuj_8", // 반복 재생할 유튜브 영상 ID 목록
    },
    events: {
      // ready되었을 때 실행되기 전 할 것
      onReady: function (event) {
        event.target.mute(); // mute 지정
      },
    },
  });
}

HTML

  • youtube section는 페이지에서 영상이 위치하는 것과, 영상에서 얼만큼 보이게 할것인지 정하는 영역
  • youtube__area는 실제 youtube(id player 부분) 영상이 들어 있는 부분이다.
    • 영상의 크기 제어
    • 영상의 비율 제어
    • 영상의 어떤 일부분이 youtube section에 보이게 제어
  • youtube__cover는 영상위에 filter를 씌우기 위함임
<!-- YOUTUBE VIDEO-->
<section class="youtube">
  <div class="youtube__area">
    <div id="player"></div>
  </div>
  <div class="youtube__cover"></div>
</section>

CSS

  • .youtube 의 경우 영상이 실제로 사이트에서 보일 영역의 사이즈 height를 700px, 그리고 overflow: hidden을 통해서 영상이 넘치는 부분을 안보이게함 (영상의 일부분만 디자인적으로 사용하고자 하기 때문에 이렇게 사용함)
/* YOUTUBE VIDEO */
.youtube {
  position: relative;
  height: 700px;
  background-color: #333;
  overflow: hidden;
}
  • .youtube__area 의 경우 영상의 크기를 지정하기 위해서 width:1920px로 지정하였다.
  • 그리고 해당 영상이 화면 중앙에 계속 위치하게 시키기 위해서 absolute, 50%, margin 방법을 사용하였으며 여기에서는, 상하좌우 모두 중앙에 위치시키기 위해서 top, left 둘다 썻다.
.youtube .youtube__area {
  width: 1920px;
  position: absolute;
  left: 50%;
  margin-left: calc(1920px / -2);
  top: 50%;
  margin-top: calc(1920px * 9 / 16 / -2);
}
  • 사실 영상의 비율을 제어하려면 자식요소가 필요한데, html적으로 자식을 생성하는 것보다 css적으로 해결하는 방법을 사용하는 것이 좋다.
  • 그래서 가상요소를 사용하여 내부의 가상 요소를 만들고, 가상요소의 경우 inline 이기 때문에 block요소로 지정해주고, width: 100%, height:0 처리하고 padding-top:56.25%를 통해 16:9 비율로 영상이 갖추어 지게 하였다.
.youtube .youtube__area::before {
  content: "";
  display: block;
  width: 100%;
  height: 0;
  padding-top: 56.25%;
}
  • youtube__cover는 필터의 역할을 하기 때문에 rgba(0, 0, 0, 0.3)을 통해서 투명검정의 해당 이미지 패턴을 씌운다.
.youtube .youtube__cover {
  background-image: url("../images/video_cover_pattern.png");
  background-color: rgba(0, 0, 0, 0.3);
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

#player {
  width: 100%;
  height: 100%;
  position: absolute;
  top: 0;
  left: 0;
}