myMBTI 테스트 만들어보기

sohyun·2022년 8월 8일
3

구름에듀 사이트의 판다코딩의 HTML/JS/CSS로 나만의 MBTI 사이트 만들기 강의를 보고 만들었습니다.

01. HTML

각 3개의 섹션 레이아웃하기

총 3개의 섹션으로 나누어 한 페이지로 레이아웃하여
1️⃣ 이 보일 때 display:block 나머지 2️⃣,3️⃣은 display:none으로 처리할 것이다.
=> SPA(Single Page Application)

  • 1️⃣ 사용자가 처음 들어왔을 때 보여줄 Main page id="main"
  • 2️⃣ 스타트 버튼을 눌렀을 시 보여줄 QnA page id="qna"
  • 3️⃣ 질문 응답의 결과로 보여줄 Result page id="result"

부트스트랩 이용하기

부트스트랩의 그리드 시스템과 css를 활용하여 편리하게 반응형 페이지의 골격과 스타일을 적용한다.


HTML 코드와 이미지

1️⃣ Main HTML

<body>
<div id="container">
  
        <!--01. Main page -->
        <section id="main" class="mx-auto my-5 py-5 px-3" style="display:block;">
            <h2> 제목 입력 </h2>
          
            <div class="col-lg-4 col-md-8 col-sm-12 mx-auto">
                <img src="./img/main.png" class="img-fluid" alt="mainImage"/>
            </div>
          
            <p>테스트 소개</p>
          
            <button class="btn btn-outline-primary mt-3">시작하기</button>
        </section>
  </div>
</body>

1️⃣ main 이미지

2️⃣ QnA HTML

 <!-- 02. QnA page -->
        <section id="qna">
          <!-- 상태바 -->
            <div class="status mx-auto mt-5">
                <div class="statusBar"></div>
            </div>
          <!-- 질문과 대답 -->
            <div class="qBox mx-auto my-5 py-3"></div>
            <div class="answerBox"></div>
        </section>

2️⃣ QnA 이미지

3️⃣ Result HTML

<!-- 03. Result page -->
        <section id="result" class="mx-auto my-5 py-5 px-3">
            <h2>결과 제목</h2>
          
            <div class="resultname"> 결과 이름 </div>
            <div id="resultImg" class="my-3 col-lg-6 col-md-8 col-sm-10 col-12 mx-auto"></div>
            <div class="resultDesc"> 결과 설명</div>

            <button type="button" class="kakao mt-3 py-2 px-3">공유하기</button>
        </section>

3️⃣ Result 이미지


02. CSS

default.css

index.html의 모든 선택자가 공통적으로 들어가는 스타일 속성을 지정해준 디폴트 css도 따로 만들어 링크한다.

main.css / qna.css / result.css

1️⃣,2️⃣,3️⃣ 의 각 css 파일을 만들고 index.html에 링크한다.

animation.css

정교하고 직관적인 애니메이션 기능을 보여주기 위한 css 파일을 별도로 만들어 링크한다.

  • 1️⃣에서 2️⃣로 넘어갈 때 1️⃣은 서서히 사라지고 2️⃣는 서서히 나타나는 애니메이션을 만든다.
  • @keyframes 이용하여 CSS 애니메이션의 중간 절차를 제어한다. 자세한설명
  • 투명도를 제어하는 키프레임 애니메이션 fadeIn fadeOut을 정의한뒤 html DOM객체의 style.animation에 적용한다.
  • 해당 객체에 정의한 애니메이션 이름과 지속시간을 정해주어야한다.
    main.style.animation = 'fadeOut 1s';
    qna.style.animation = 'fadeIn 1s';

@keyframes로 정의한 애니메이션 코드

/* 서서히 생기는 효과*/
@keyframes fadeIn {
    from {opacity : 0;}
    to {opacity : 1;}
}

/* 서서히 사라지는 효과*/
@keyframes fadeOut {
    from {opacity : 1;}
    to {opacity : 0;}
}

/* @-webkit-keyframes : 크롬에서 애니메이션 적용하기 */
@-webkit-keyframes fadeIn {
    from {opacity : 0;}
    to {opacity : 1;}
}
@-webkit-keyframes fadeOut {
    from {opacity : 1;}
    to {opacity : 0;}
}


/* 돔객체에 넣어주는것이 아닌 클래스 선택자로 css animation 속성값 지정해줘도 됨*/
/* 
.fadeIn {
  animation: fadeIn; <- 위에서 만들어둔 애니메이션 지정
  animation-duration: 0.5s;
}

.fadeOut {
  animation: fadeOut;
  animation-duration: 0.5s;
} 
*/

03.Javascript

data.js

qnaList : 질문과 응답

  • qnaList : 12개{qna} 객체가 담긴 Q&A 리스트 배열
    • q : 질문,
    • a : 3개{answer,type} 객체가 담긴 answer 리스트 배열
      • answer : 답변,
        type : 해당 답변에 가까운 4가지 타입이 담긴 배열

infoList : 결과와 설명

  • infoList : 12개{name,desc} 객체가 담긴 결과리스트 배열
    • name : 결과,
    • desc : 설명

start.js

메인페이지에서 질문페이지로 넘어가기

function begin() {
  //1️⃣ 1초동안 서서히 사라지기 적용
  main.style.WebkitAnimation = 'fadeOut 1s';
  main.style.animation = 'fadeOut 1s';
  
  //2️⃣ 0.45초후에 1초동안 서서히 나타나기 적용
  setTimeout(() => {
    qna.style.WebkitAnimation = 'fadeIn 1s';
    qna.style.animation = 'fadeIn 1s';
    // + 0.45초후에 1️⃣ 완전히 안보이게,2️⃣ 완전히 나타남  
    setTimeout(() => {
      main.style.display = 'none';
      qna.style.display = 'block';
    }, 450);
    
    //질문나오는 goNext() 호출
    let qIdx = 0;
    goNext(qIdx);
    
  }, 450);
}

질문페이지의 첫번째 질문과 응답 나오게하기

function goNext(qIdx) {
  let q = document.querySelector('.qBox');
  
  //`q`의 value 처리
  //`qnaList`의 qIdx번째 인덱스의 `q`가 들어옴
  q.innerHTML = qnaList[qIdx].q;
  
  
  // `a` 의 value 처리
  //`qnaList`객체의 qIdx번째 인덱스의 `a`의 배열의 i번 반복
  for (let i in qnaList[qIdx].a) {
    //addAnswer()에 `qnaList`의 qIdx번째 인덱스번째의 `a`의 i번째 인덱스의 answer와 qIdx를 인자로 전달
    addAnswer(qnaList[qIdx].a[i].answer, qIdx);
  }

3가지 응답 선택지 반복문과 다음 질문으로 넘어가기


function addAnswer(answerText, qIdx) {
  				//answerText = qnaList[qIdx].a[i].answer
  
  //응답 전체를 담는 박스
  let a = document.querySelector('.answerBox');
  
  //각 3가지 응답 버튼
  let answer = document.createElement('button');
  answer.classList.add('answerList');
  //부트스트랩 css 및 애니메이션 적용
  answer.classList.add('my-3');
  answer.classList.add('py-3');
  answer.classList.add('mx-auto');
  answer.classList.add('fadeIn');
  
  //응답전체 박스에 개별응답버튼 순서대로넣기
  a.appendChild(answer);
  //개별응답버튼에 파라미터로 받은 answerText( = qnaList[qIdx].a[i].answer) 넣기
  answer.innerHTML = answerText;

  //버튼 클릭할때마다 발생하는 이벤트 적용
  answer.addEventListener(
    'click',
    function () {
      //모든 응답버튼 children에 담기
      let children = document.querySelectorAll('.answerList');
      
      //모든 응답버튼 반복
      for (let i = 0; i < children.length; i++) {
        // 모든 응답버튼 비활성화,0.5초 동안 서서히 사라지기
        children[i].disabled = true;
        children[i].style.WebkitAnimation = 'fadeOut 0.5s';
        children[i].style.animation = 'fadeOut 0.5s';
      }
      
      //0.45초 뒤에 
      setTimeout(function(){
           
        //해당 질문의 응답지를 숨긴다.
        // ✨만약 이 처리를 해주지 않는다면 응답이 숨겨지지 않고 쌓이게 된다.
            for(let i=0;i<children.length;i++){
                children[i].style.display='none';
            }
        //다음 질문으로 넘어간다.
            goNext(++qIdx);
        },450)
    });
}

진행률 나타내기

  let status = document.querySelector('.statusBar');

  //const endPoint=12; 
 //전체 12가지 질문에서 질문하나 할때마다 채워짐 
  status.style.width = (100 / endPoint) * (qIdx + 1) + '%';
}

마지막 질문에서 응답결과로 넘어가기

function goNext(qIdx){
  //endPoint 12와 같아진다면 goResult() 호출 후 return으로 구문 종료
    if(qIdx === endPoint ){
        goResult();
        return;
    }

  //결과페이지로 넘어가게한다.
function goResult() {
  // qna 1초동안 서서히 사라짐
  qna.style.webkitAnimation = 'fadeOut 1s';
  qna.style.animation = 'fadeOut 1s';
  
  //0.5초뒤에
  setTimeout(function () {
    //결과가 서서히 1초동안 나타남
    result.style.webkitAnimation = 'fadeIn 1s';
    result.style.animation = 'fadeIn 1s';

    //그리고 0.5초뒤에
    setTimeout(function () {
      //qna는 완전히 사라지고, 결과는 완전히 나타남
      qna.style.display = 'none';
      result.style.display = 'block';
    }, 500);
  },500);
  //결과를 구현하는 함수 호출
  setResult();
}

최종결과를 보여주기 위한 알고리즘 구현

// 고른 응답의 최대 점수를 매기기위한 변수 설정
const select = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];

//addAnswer()에서 answer.addEventListener가 발생할때마다
//선택한 응답의 type의 프로퍼티값을 target에 담기
let target = qnaList[qIdx].a[idx].type;
	//type의 프로퍼티값 즉 해당 배열길이만큼 다음 구문 반복
	for(let i=0;i<target.length; i++){
      
   //점수 매기기위한 12개의 배열을 담고 있는 select변수에 
   //선택한 응답의 type의 해당 인덱스가 담고있는 값을 넣어
  //select의 해당 인덱스의 숫자에 +1 해준다.     
   select[target[i]] += 1;
            }

//최종 응답 계산 함수
function calResult() {
  // 고른 응답의 점수를 매기기위한 select변수를 전개구문한다
  // Math메서드에 넣어 점수가 매겨진 배열의 최대값을 구함(동일 점수라면 맨 처음 점수로 반환)
  //indexOf메스드의 인수로 전달된 요소를 검색하여 
  let result = select.indexOf(Math.max(...select));
  // 검색된 해당 인덱스 번호 반환
  return result;
}

최종 응답 계산 함수를 통해 해당 결과 화면에 나타내기

function setResult() {
  //return된 result값 point에 담기
  //result : 최대값이 발견된 해당 인덱스 번호
  let point = calResult();
  
  //결과이름 담기
  const resultName = document.querySelector('.resultname');
  // infoList의 최대값이 발견된 인덱스 번호의 결과이름을 담는다
  resultName.innerHTML = infoList[point].name;

  //결과 이미지
  let resultImg = document.createElement('img');
  const imgDiv = document.querySelector('#resultImg');
  //최대값이 발견된 인덱스 번호 이미지 번호로 담기
  let imgURL = 'img/image-' + point + '.png';
  resultImg.src = imgURL;
  resultImg.alt = point;
  //부트스트랩 .img-fluid
  //max-width: 100%; height: auto; 부모 너비에 맞게 크기가 조정되도록 이미지에 추가됨
  resultImg.classList.add('img-fluid');
  //dom객체에 자식요소로 넣어주기
  imgDiv.appendChild(resultImg);

  //결과 설명
  const resultDesc = document.querySelector('.resultDesc');
  //infoList의 최대값이 발견된 인덱스의 desc 담기
  resultDesc.innerHTML = infoList[point].desc;
}

04.배포 및 공유하기

netify 배포하기

카카오 공유하기 버튼 및 기능구현

html 공유하기 버튼


 <script src="https://developers.kakao.com/sdk/js/kakao.js"></script>
    <script>
        Kakao.init('Javascript key');
        Kakao.isInitialized();
    </script>

  <button onclick="js:setShare()" type="button" class="kakao mt-3 py-2 px-3">공유하기</button>

 <script src="./js/share.js"></script>

js 공유하기 기능 구현

//netlify 로 배포한 url 
const url = 'https://mymbtitwelvelovetype.netlify.app/';

function setShare(){
  let resultImg = document.querySelector('#resultImg');
  //alt = point 
  let resultAlt = resultImg.firstElementChild.alt;
  const shareTitle = '십이간지 연애유형 결과';
  const shareDes = infoList[resultAlt].name;
  const shareImage = url + 'img/image-' + resultAlt + '.png';
  const shareURL = url + 'page/result-' + resultAlt + '.html';

//Kakao.Share.sendDefault() 함수
  //카카오톡 공유하기 버튼을 추가하지 않고, 메시지 보내기 요청만 합니다. 

  Kakao.Link.sendDefault({
    objectType: 'feed',
    content: {
      title: shareTitle,
      description: shareDes,
      imageUrl: shareImage,
      link: {
        mobileWebUrl: shareURL,
        webUrl: shareURL
      },
    },

    buttons: [
      {
        title: '결과확인하기',
        link: {
          mobileWebUrl: shareURL,
          webUrl: shareURL,
        },
      },
    ]
  });
}

profile
냠소현 개발일지

0개의 댓글