iT邦幫忙

2022 iThome 鐵人賽

DAY 19
0
Modern Web

Have fun! 新手也能打造的Javascript微型專案系列 第 19

Have fun! 新手也能打造的Javascript微型專案! Day19: 觀念跟語法老是忘記? 那寫個flash-card程式來幫助自己記憶吧!(上)

  • 分享至 

  • xImage
  •  
tags: ItIron2022 Javascript

前言

要準備面試前總是需要複習各種基本概念,就像以前高中考試前背單字一樣,既然都是一樣的行為,那麼以前常用的單字卡應該也能用在程式學習上吧? 這幾天我們就來一起打造一個記憶卡app,雖說會有些複雜,但許多應用到的技巧都在之前的練習中提過,不用太過於擔心!

預期成果

我們這次要做的是一個模擬flash card的頁面,可以手動新增想練習的字卡並有著基本的翻頁動畫,如同下圖所示。

Getting started

Step1: 專案結構

同樣請你先準備好三個基本的檔案,index.html, style.css & script.js檔案,並先在index.html & style.css中寫入以下的內容,量有點多但不用擔心,我會額外做需要的說明。

另外有些人可能會發現html/css的有些寫法相當奇怪,you got me. 其實這個專案是我剛開始學程式不久後寫的東西,現在回頭看確實很粗糙,但為了快速示範我就直接拿以前寫的東西做基底,沒有再額外做一些優化,暫時先把重點放js的處理就好了,css/html的部分就挑需要的看,暫時放我一馬吧。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Flash Cards</title>
    <link rel="stylesheet" href="./style.css" />
    <script
      src="https://kit.fontawesome.com/fb6ef03ea0.js"
      crossorigin="anonymous"
      defer
    ></script>
    <!-- <script src="./script.js" defer></script> -->
  </head>

  <body>
    <!-- clear btn -->
    <button id="clear" class="clear btn">
      <i class="fas fa-trash"></i> Clear Cards
    </button>
    <!-- title -->
    <h1>
      Memory Cards
      <button id="show" class="btn btn-small">
        <i class="fas fa-plus"></i> Add new cards
      </button>
    </h1>
    <!-- card-container -->
    <div id="cards-container" class="cards">
      <div class="card active">
        <div class="inner-card">
          <div class="inner-card-front">
            <p>What is Javascript?</p>
          </div>
          <div class="inner-card-back">
            <p>A programming language</p>
          </div>
        </div>
      </div>
    </div>

    <!-- navigations -->
    <div class="navigation">
      <button id="prev" class="nav-button">
        <i class="fas fa-arrow-left"></i>
      </button>

      <p id="current">1/1</p>

      <button id="next" class="nav-button">
        <i class="fas fa-arrow-right"></i>
      </button>
    </div>

    <!-- add card -->
    <div class="add-container" id="add-container">
      <h1>
        Add New Card
        <button class="btn btn-small btn-ghost" id="hide">
          <i class="fas fa-times"></i>
        </button>
      </h1>

      <div class="form-group">
        <label for="question">Question</label>
        <textarea
          name="question"
          id="question"
          placeholder="Enter question"
        ></textarea>
      </div>

      <div class="form-group">
        <label for="answer">Answer</label>
        <textarea
          name="answer"
          id="answer"
          placeholder="Enter answer"
        ></textarea>
      </div>

      <button id="add-card" class="btn">
        <i class="fas fa-plus"></i> Add Card
      </button>
    </div>
  </body>
</html>
@import url("https://fonts.googleapis.com/css?family=Lato&display=swap");

* {
  box-sizing: border-box;
}

body {
  padding: 0;
  background: #3e92cc;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  height: 100vh;
  margin: 0;
  overflow: hidden;
  font-family: "Lato", sans-serif;
}

h1 {
  position: relative;
}

h1 button {
  position: absolute;
  right: 0;
  transform: translate(120%, -50%);
  z-index: 2;
}

.btn {
  border-radius: 3px;
  font-size: 14px;
  margin-top: 20px;
  padding: 10px 15px;
  text-transform: uppercase;
  background: #27ae60;
  color: #fff;
  border: none;
  font-weight: bold;
  cursor: pointer;
}

.btn:hover {
  letter-spacing: 1px;
  transition: all 0.4s ease 0s;
  background: #434343;
}

.btn-small {
  font-size: 12px;
  padding: 5px 10px;
}

.btn-ghost {
  border: 0;
  background: transparent;
}

.clear {
  position: absolute;
  bottom: 30px;
  left: 30px;
  color: #fff;
  background-color: #ff6666;
}

.cards {
  perspective: 1000px;
  position: relative;
  height: 300px;
  width: 500px;
  max-width: 100%;
  overflow: hidden;
}

.cards .card {
  position: absolute;
  opacity: 0;
  font-size: 1.5em;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  transform: translateX(50%) rotateY(-10deg);
  transition: transform 0.4s ease, opacity 0.4s ease;
}

.cards .card .inner-card {
  box-shadow: 0 1px 10px rgba(0, 0, 0, 0.3);
  border-radius: 4px;
  height: 100%;
  width: 100%;
  position: relative;
  transform-style: preserve-3d;
  transition: transform 0.4s ease;
}

.cards .card .inner-card .inner-card-front,
.cards .card .inner-card .inner-card-back {
  backface-visibility: hidden;
  position: absolute;
  top: 0;
  left: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  width: 100%;
  background: #fff;
  border-radius: 16px;
  word-break: break-word;
  padding: 20px;
}

.card.active {
  opacity: 1;
  cursor: pointer;
  z-index: 10;
  transform: translateX(0%) rotateY(0deg);
}

.card.left {
  transform: translateX(-50%) rotateY(+10deg);
}

.card.show-answer .inner-card {
  transform: rotateX(180deg);
}

.card .inner-card-front {
  transform: rotateX(0deg);
}

.card .inner-card-back {
  transform: rotateX(180deg);
}

.navigation {
  display: flex;
  margin: 20px 0;
}

.navigation .nav-button {
  border: none;
  background-color: transparent;
  cursor: pointer;
  font-size: 16px;
}

.navigation p {
  margin: 0 25px;
}

.add-container {
  opacity: 0;
  z-index: -1;
  background-color: #95a5a6;
  border: 2px solid #eee;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 10px 0;
  margin: 0;
  position: absolute;
  top: 0;
  bottom: 0;
  width: 100%;
  transition: 0.4s ease;
}

.add-container .form-group label {
  display: block;
  margin: 20px 0 10px;
  font-size: 1.5em;
  font-weight: bold;
}

.add-container .form-group textarea {
  border: 1px solid #aaa;
  border-radius: 3px;
  font-size: 16px;
  padding: 12px;
  min-width: 500px;
  max-width: 100%;
}

.add-container.show {
  opacity: 1;
  z-index: 2;
}

寫入以上的內容後,你這時候應該會看到以下的畫面

畫面挺正常但沒有任何的按鈕有作用,點選卡片本身也不會翻動,這是非常正常的!

特別注意一下這次為了示範最初的畫面呈現,我有先將一些假資料寫進html中,之後需要清空,因為所有的資料都需要透過js動態載入。

假資料的部分

Step2: 卡片結構分析

進入js事前規劃前我們必須要先理解,到底要怎麼樣實現卡片翻動的效果? 這個問題也困擾著當時的我,好在W3school有個相當基礎的教學讓我有了基本概念。
先仔細看一下我們在html的結構,你會發現實際上前後的元素都已經渲染出來了,有確實的出現在DOM tree,只是背面的元素被藏著沒有顯示而已。

要達成這樣的效果需要兩個css屬性配合

  1. transform: rotate
    rotate屬性可以讓元素以X/Y軸為軸心翻轉,在這次的程式碼中我用了rotateX,因此會以X軸為基準翻轉,做出垂直旋轉的效果,rotateX(0deg)表示沒有翻轉,元素面向正面;rotateX(180deg)則表示轉了180度,元素面向背面。
  2. backface-visibility: hidden
    backface-visibility屬性則是可以決定元素在面向背面時是否顯示,預設值為visible,也就是總是顯示。聽起來有點抽象我知道,我們看個例子吧!
    當我今天把.inner-card-back元素的backface-visibility: hidden拿掉,你看到的就會是這樣個樣子。

元素垂直翻轉了180度並蓋在原本的What is Javascript字串上,這就是因為此時由於翻轉的關係,元素已不是面向正面但我們又沒有加上backface-visibility: hidden屬性。理解這一點之後我想你大概知道該怎麼樣呈現兩者間的切換了,阿不就再翻個180度就好了嗎? 沒錯! 我們只要讓整個元素再次翻動180度就可以輕鬆達到這樣的效果了,目前在css中我們已經寫好一個class

.card.show-answer .inner-card {
  transform: rotateX(180deg);
}

只要視情況給card元素加上.show-answer就可以讓inner-card再次翻轉,我們馬上來實驗下想法是否正確吧,請你偷偷的在html檔案下方加入以下的程式碼,這僅是作為實驗,今天結束後要記得刪掉:D

<script>
  const card = document.querySelector(".card");
  card.addEventListener("click", () => {
    card.classList.toggle("show-answer");
  });
</script>

加入後重整你的頁面並試著點擊卡片,我們朝思暮想的翻轉效果不就出現了?

總結

我們今天完成了基本的UI畫面並了解了卡片翻轉的基礎原理,礙於篇幅的關係我今天必須停在這邊,明天開始我們會一步步的實踐剩餘的功能,今天可以先試著消化以上的內容即可,放輕鬆一些!

文章中的範例程式碼可以在這邊取得,歡迎自行取用

轉職Q & A

Danny,面試結束後HR聯繫我,要求我提供一些額外的個人資料,看起來與工作完全無關,那麼我應該怎麼辦?

這個問題剛好之前有在社群被問到,當時有人給了很詳細的回應,在這邊紀錄一下。通常遇到這樣的情況,很可能是面試的公司稍微有些傳統,台灣很多求職員工資料表都要填一大堆完全無用的資訊,舉凡你之前高中的社團到你哥有幾個孩子這樣,書面的東西就這樣了,許多額外的證明更是如此,我有聽過的像是良民證、個人信用資料或是貸款記錄等,根據台灣勞工局的解釋,勞工是有權利拒絕提供這類與工作無關的資訊,要是真的擔心的話你可以主動詢問一下為什麼需要這些資料,就像我之前提到的,面試求職是雙向的行為,真的不用把自己的地位放這麼低,很多需求都是可以討論的!

本文章同步發布於個人部落格,有興趣的朋友也可以來逛逛~!


上一篇
Have fun! 新手也能打造的Javascript微型專案! Day18: 總是心浮氣躁嗎? 利用垂直置中技巧做個簡單的冥想頁面吧!
下一篇
Have fun! 新手也能打造的Javascript微型專案! Day20: 觀念跟語法老是忘記? 那寫個flash-card程式來幫助自己記憶吧!(中)
系列文
Have fun! 新手也能打造的Javascript微型專案30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言