iT邦幫忙

2022 iThome 鐵人賽

DAY 13
0

今天要來做的主題還蠻有趣滴~ 如圖中所示,我們的目標水量是要把整個大杯子填滿(2L),點選下方的小杯子(250ml),可以把水加入大杯子中,並且顯示目前水量以及剩餘水量,跳杯子點選會把水量加再一起


運用知識點羅列

  • CSS
知識點 使用說明
flexbox 設定水杯的排版
transition 水量增加和減少的動畫
variable 設定全域變數
  • JS
知識點 使用說明
querySelectorAll(selectors) 取得每個 class 為 cup-small的杯子
forEach( ) 迭代每個小杯子
addEventListener( ) 為小杯子加入事件監聽
classList.add() / remove() 新增與移除class="full"
contains() 回傳布林值,判斷A節點是否為B節點的後裔
nextElementSibling 回傳下一個相鄰的element node(唯讀)

流程講解

  • HTML
    我們預先寫好一些類別和標籤內的文字,目的是讓之後寫 CSS 的時候可以先針對預先寫好的部分去做設定並且看到效果,之後我們會把預先寫好的部分刪掉,直接用javaScript去控制,這跟Day 11 Event KeyCodes 鍵盤輸入代碼這篇的操作有點類似
 <h1>Drink Water 沒事多喝水</h1>
    <h3>今日目標:2L</h3>
    
    <!-- 大杯子 -->
    <div class="cup">
    
        <!-- 剩餘量 -->
        <div class="rest" id="rest">
            <span id="liters">1.5L</span>
            <small>剩餘量</small>
        </div>
        
        <!-- 飲水量-->
        <div class="percentage" id="percentage">
            20%
        </div>
    </div>
    <p class="text">請選擇你喝了多少水</p>

    <!-- 小杯子 -->
    <div class="cups">
        <div class="cup cup-small full">250 ml</div>
        <div class="cup cup-small full">250 ml</div>
        <div class="cup cup-small">250 ml</div>
        <div class="cup cup-small">250 ml</div>
        <div class="cup cup-small">250 ml</div>
        <div class="cup cup-small">250 ml</div>
        <div class="cup cup-small">250 ml</div>
        <div class="cup cup-small">250 ml</div>
    </div>
  • CSS

全域變數
全域變數是以 -- 開頭,後面自定義變數名稱,並寫在:root中,使之作用範圍為全域
在此我們設置兩個全域變數,以便之後要重複利用

:root {
  --border-color: #144fc6;  /*杯子的邊框顏色*/
  --fill-color: #6ab3f8;  /*水填滿的顏色*/
}

大局配置以及標題

* {
  box-sizing: border-box;
}
body {
  background-color: #3494e4;
  margin: 0;
  padding: 0;
  display: flex; /*讓內容水平垂直置中*/
  justify-content: center;
  align-items: center;
  flex-direction: column;
  margin-bottom: 40px;
}
h1 {
  margin: 10px 0 0;
}
h3 {
  margin: 10px 0;
}

大杯子、小杯子

/* 大杯子以及小杯子的通用屬性 */
.cup {
  background-color: #fff;
  border: 4px solid var(--border-color);
  color: var(--border-color);
  border-radius: 0 0 40px 40px;
  width: 150px;
  height: 360px;
  margin: 30px 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
/* 小杯子 */
.cup.cup-small {
  width: 50px;
  height: 95px;
  border-radius: 0 0 15px 15px;
  background-color: rgba(255, 255, 255, 0.9);
  cursor: pointer;
  font-size: 14px;
  margin: 5px;
  align-items: center;
  justify-content: center;
  text-align: center;
  transition: all 0.3s ease;
}
/* 裝小杯子的容器 */
.cups {
  display: flex;
  flex-wrap: wrap;
  width: 250px;
  align-items: center;
  justify-content: center;
}
/* 填滿小杯子的水 */
.cup.cup-small.full {
  background-color: var(--fill-color);
  color: white;
}

剩餘的水量

/* 大杯子內剩餘的水量 */
.rest {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  flex: 1; /*flex-grow*/
  transition: all 0.3s ease;
}
/* 1.5L */
#liters {
  font-size: 20px;
  font-weight: bold;
}
.rest small {
  font-size: 12px;
}

紀錄你喝了多少水

/* 喝了多少水的 % 數 */
.percentage {
  background-color: var(--fill-color);
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: bold;
  font-size: 30px;
  height: 0; /*初始水量的高度設為0*/
  transition: all 0.3s ease;
}

/* "請選擇你喝了多少水"字樣 */
.text {
  text-align: center;
  margin: 0 0 5px;
  font-weight: bold;
}

以上都設定好,呈現會如下圖
https://ithelp.ithome.com.tw/upload/images/20220919/20149362ixyXmoC2Rj.png

那我們要把HTML中的部分程式碼刪除,因為要利用javaScript去控制
https://ithelp.ithome.com.tw/upload/images/20220919/20149362hfVOkRnunG.png

  • JS
    取得目標元素
let cupSmall = document.querySelectorAll("cup-small"); /*回傳NodeList*/
let liters = document.getElementById("liters");
let percentage = document.getElementById("percentage");
let rest = document.getElementById("rest");

為每個小杯子加入事件監聽

cupSmall.forEach((cup, index) => {
  cup.addEventListener("click", () => {
    hightlightCups(index);
  });
});
function hightlightCups(index) {
  console.log(index);   /*點按不同的小杯子會出現不同的數字,從0開始*/
  
   // 減少水量
  if (
    cupSmall[index].classList.contains("full") &&
    !cupSmall[index].nextElementSibling.classList.contains("full")
  ) {
    index--;
  }

  cupSmall.forEach((cup, index2) => {
    if (index2 <= index) {
      cup.classList.add("full");
    } else {
      cup.classList.remove("full");
    }
  });
  
   // 更新大杯子的水量
  updateBigCup();
}

大杯子

function updateBigCup() {
  let fullCups = document.querySelectorAll(".cup-small.full").length;
  console.log(fullCups); /*滿杯的數量*/
  let totalCups = cupSmall.length;

  if (fullCups == 0) {
    percentage.style.visibility = "hidden";
    percentage.style.height = 0;
  } else {
    percentage.style.visibility = "visible";
    percentage.style.height = `${(fullCups / totalCups) * 360}px`;
    // 360為大杯子的高度
    percentage.innerText = `${(fullCups / totalCups) * 100}%`;
  }
}

當我杯子的水是100%時,會看到了以下這個奇景,"剩餘量"占了一行在那,那可以怎麼把它消滅掉呢??
https://ithelp.ithome.com.tw/upload/images/20220919/20149362ZkZFfEmjcM.png

其實很簡單,就是操控visibility的值而已,要隱藏就是"hidden",顯現就是"visible"

// 100%的水時,不要讓"剩餘量"占了一行的空間
  if (fullCups === totalCups) {
    rest.style.visibility = "hidden";
    rest.style.height = 0;
  } else {
    rest.style.visibility = "visible";
    liters.innerText = `${2 - (250 * fullCups) / 1000}`;
    // 2為大杯子總容量(單位:公升), 250為一個小杯子的容量(單位:毫升),除以1000是要把毫升換算成公升制
  }

附上codepen連結 https://codepen.io/hangineer/pen/QWrvxww


補充

  1. CSS垂直置中技巧
  2. https://ithelp.ithome.com.tw/upload/images/20220920/20149362ijVd0QARmL.png
  3. https://ithelp.ithome.com.tw/upload/images/20220920/20149362M2rFxrheHU.png

summary 總結

此賽project比較複雜的是要在大杯子和小杯子中間做切換,並且顯示剩餘和目前的水量,要怎麼將pjoject的流程寫得更清楚明白也是自己還在練習的,若有解說不夠詳盡或是錯誤歡迎指教,感激不盡!那明天見囉


參考資料

50 Projects In 50 Days - HTML, CSS & JavaScript


上一篇
Day 12 Side Project : Dad Jokes 爸爸的冷笑話
下一篇
Day 14 Side Project : Incrementing Counter 遞增計數器
系列文
在30天利用HTML & CSS & JavaScript完成Side Project實作30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言