iT邦幫忙

2021 iThome 鐵人賽

DAY 5
0
自我挑戰組

JS30 學習日記系列 第 5

Day 5 - Flex Panels Image Gallery

前言

JS 30 是由加拿大的全端工程師 Wes Bos 免費提供的 JavaScript 簡單應用課程,課程主打 No FrameworksNo CompilersNo LibrariesNo Boilerplate 在30天的30部教學影片裡,建立30個JavaScript的有趣小東西。

另外,Wes Bos 也很無私地在 Github 上公開了所有 JS 30 課程的程式碼,有興趣的話可以去 fork 或下載。


本日目標

利用 flexboxtransition 等 CSS 屬性,搭配 JS 監聽 transitionendclick 事件,最終實作出一個美觀的 Image Gallery。


解析程式碼

HTML 部分

由最外層的.panels包覆住內部的5個.panel所形成的一個巢狀結構。

<div class="panels">
    <div class="panel panel1">
      <p>Hey</p>
      <p>Let's</p>
      <p>Dance</p>
    </div>
    <div class="panel panel2">
      <p>Give</p>
      <p>Take</p>
      <p>Receive</p>
    </div>
    <div class="panel panel3">
      <p>Experience</p>
      <p>It</p>
      <p>Today</p>
    </div>
    <div class="panel panel4">
      <p>Give</p>
      <p>All</p>
      <p>You can</p>
    </div>
    <div class="panel panel5">
      <p>Life</p>
      <p>In</p>
      <p>Motion</p>
    </div>
</div>

CSS 部分

(僅說明影片中更動的部分)

先將最外層的 .panels 顯示類型設定為 flex,它同時也作為一個 flex-container 包覆住內部的五個 flex-items 也就是 .panel

.panels {
      /*其餘略過*/
      display: flex;
}

flexflex-growflex-shrinkflex-basis 的簡寫,只有指定一個值給 flex 時,則代表設定的是 flex-grow,其餘屬性以預設值帶入。

flex-grow 可以指定 flex-container 的剩餘空間該如何分配,下面所有的 .panelflex-grow 都是 1,也就是均勻分配剩餘空間。

接著,將每一個 .panel 的顯示類型都設定為 flex (此時的.panel 對下面的<p></p>來說就是 flex-container) 並將其下的 flex-item 在水平、鉛直方向都置中。

最後,設定 flex-directionflex-boxmain-axis 更改為直列。

.panel {
      flex: 1; /*將每個 flex-item 的大小都設為一樣並填滿*/
      display: flex;  
      justify-content: center; /*在水平方向置中*/
      align-items: center;/*在鉛直方向置中*/
      flex-direction: column; 
}

.panel (flex-container)下的 <p></p> (flex-item) 的 flex 屬性設定為 1、0、auto,也就是均勻分配 flex-container 的剩餘空間、flex-item 長度超過 flex-container 時的收縮量設為 0、flex-itemflex container 的初始大小設為自動(auto)。

接著,也將<p></p>當作是一個 flex-container,設定顯示類型為 flex。透過 justify-contentalign-items,將標籤內的文字(flex-item) 水平、鉛直置中排列。

.panel > * {
      /*其餘省略*/
      flex: 1 0 auto;
      display: flex;
      justify-content: center;
      align-items: center;
}

初始狀態,分別將在 .panel 上方和下方的 <p></p> 都各上移、下移 100% 以達到隱藏的效果。

.panel > *:first-child{
      transform: translateY(-100%);/*上移*/
}
.panel > *:last-child{
      transform: translateY(100%);/*下移*/
}

接著設定當.panel上有open-active這個 class 時,就將原本隱藏的文字分別下移和上移顯示出來。

.panel.open-active > *:first-child{
      transform: translateY(0);
}
.panel.open-active > *:last-child{
      transform: translateY(0);
}

.panel開啟時,將內部文字放大為 40px 並將 flex-container 剩餘分配的位置變為原來的5倍。

.panel.open {
      font-size: 40px;
      flex: 5;
}
補充資料:

CSS flex 屬性
圖解 Flexbox 基本屬性
FLEXBOX FROGGY-學習 flexbox 的小遊戲

JS 部分

取得所有的.panel 並放到 NodeList 'panels' 中。

const panels = document.querySelectorAll('.panel');

panels 中的每一個.panel都註冊兩個事件監聽器,當 clicktransitionend 事件發生時,就分別以 toggleOpentoggleActive 方法進行事件處理。

panels.forEach(panel => panel.addEventListener('click',toggleOpen));
panels.forEach(panel => panel.addEventListener('transitionend',toggleActive));

點擊任意一個.paneltoggleOpen() 方法會替觸發事件的.panel依照情況的不同,新增或是移除.open 這個 class,若是觸發事件的.panel原本沒有.open則新增,有的話則移除.open

transitionend 事件發生,toggleActive() 會作出如同 toggleOpen() 一樣的判斷,決定新增還是移除.open-active這個 class。因為同時會被觸發的 transitionend 事件有很多個,我們決定在觸發 transitionend 事件的 CSS 屬性是 flex 時,才採取處理。為什麼不寫 e.propertyName == 'flex-grow' 是因為在 Safari 顯示的是 flex 而 Chrome、FireFox 顯示的是 flex-grow,為避免這個差異導致錯誤,我們可以使用 include('flex'),當 propertyName 含有 flex 就進行事件處理。

function toggleOpen(){
    this.classList.toggle('open');
}

function toggleActive(e){
    if(e.propertyName.includes('flex')){
        this.classList.toggle('open-active');
    }
}
補充資料:

Element.classList
String.prototype.includes()

範例網頁請按此


上一篇
Day 4 - Array Cardio Day 1
下一篇
Day 6 - Ajax Type Ahead
系列文
JS30 學習日記30

尚未有邦友留言

立即登入留言