# Day5:Flex Panel Gallery [竹白記事本](https://chupainotebook.blogspot.com/),Javascript 30,紀錄。 ###### tags: `Javascript 30` ## 實現效果 點擊任意一張圖片,圖片展開,同時從圖片上下兩方分別移入文字。點擊已經展開的圖片後,圖片被壓縮,同時該圖片上下兩端的文字被擠走。 - [原始碼](https://github.com/chupai/JS30/tree/master/source_code/Day05) - [原始狀態](https://chupai.github.io/JS30/source_code/Day05/index-START.html) - [範例效果](https://chupai.github.io/JS30/source_code/Day05/index-FINISHED.html) ## 重點 1. 元件使用 `flex` 排版 2. 定義兩段狀態 3. 點擊時加上第一個狀態,當第一段狀態跑完後,接著第二個狀態 ## 基礎語法 ### CSS - `flex` - `transform` ### Event - [`addEventListener()`](https://developer.mozilla.org/zh-TW/docs/Web/API/EventTarget/addEventListener)事件監聽 - [`transitionend`](https://developer.mozilla.org/zh-CN/docs/Web/Events/transitionend) 事件 - [`TransitionEvent`](https://developer.mozilla.org/zh-CN/docs/Web/API/TransitionEvent) 介面 - [`propertyName`](https://developer.mozilla.org/zh-CN/docs/Web/API/TransitionEventt#属性) 屬性 ## 說明 ### 1. CSS 的部分 Day5 的範例 CSS 的比重比較多。基本上就是透過 `Flex` 排版完成頁面。透過 `flex: ;` 屬性控制元件的比例。 關於 CSS 的 Flex,之前有整理過一篇 **[Flex 教學整理](https://hackmd.io/@chupai/HyoyvZCqX)**,可以參考一下。 ## 實作 ### 1. 步驟 #### Step 1 改寫 HTML 與 CSS ```javascript <div class="panel"> <ul class="panel__list js-panel__list"> <li class="panel__item js-panel__item" style="background-image: url('img/bg1.jpg');" > <p>Hey</p> <p>Let's</p> <p>Dance</p> </li> <li class="panel__item js-panel__item" style="background-image: url('img/bg2.jpg');" > <p>Give</p> <p>Take</p> <p>Receive</p> </li> <li class="panel__item js-panel__item" style="background-image: url('img/bg3.jpg');" > <p>Experience</p> <p>It</p> <p>Today</p> </li> <li class="panel__item js-panel__item" style="background-image: url('img/bg4.jpg');" > <p>Give</p> <p>All</p> <p>You can</p> </li> <li class="panel__item js-panel__item" style="background-image: url('img/bg5.jpg');" > <p>Life</p> <p>In</p> <p>Motion</p> </li> </ul> </div> ``` 這裡改寫一下 HTML 與 CSS, 方便說明。 - `.panel__list` 為 `.panel__item` 元件的容器 - `.panel__item` 為 `<p>` 的容器 - `<p>` 為文字的容器 因此要將 `.panel__item` 水平置中排列,就需要在容器加上: ```css .panel__list { display: flex; justify-content: center; } ``` 而要使元件等比寬,就需要在元件上加上 `flex-grow` 來控制擴展比例: ```css .panel__item { flex: 1; } ``` `flex` 為簡寫屬性,當只有一個參數等同 `flex-grow`。 接下來排序 `<p>` 元素,我要們使它水平垂直置中,並且垂直排列,就要在它的容器 `.panel__item` 加上: ```css .panel__item { display: flex; flex-direction: column; align-items: center; justify-content: center; } ``` 接下來處理`<p>` 元素的大小與文字的排版: ```css .panel__item > p { display: flex; align-items: center; flex: 1; } ``` #### Step 2 狀態 原始狀態: ```css .panel__item { flex: 1; } .panel__item > p:nth-child(1) { transform: translateY(-100%); } .panel__item > p:nth-child(2) { font-size: 3em; } .panel__item > p:nth-child(3) { transform: translateY(100%); } ``` 加上狀態: ```css .panel__item.is-active { flex: 5; font-size: 2em; } .panel__item.is-open > p:nth-child(1) { transform: translateY(-100%); } .panel__item.is-open > p:nth-child(2) { font-size: 3em; } .panel__item.is-open > p:nth-child(3) { transform: translateY(100%); } ``` 這裡分成了兩段動畫,當 `.is-active` 狀態的動畫完成後,會接著執行 `.is-open`。當然這樣子做有點麻煩,之後會試著將其改寫再一起。 #### Step 3 END ```javascript const panelList = document.querySelector('.js-panel__list'); panelList.addEventListener('click', toggleOpen); panelList.addEventListener('transitionend', toggleActive); function toggleOpen(e) { if(!e.target.classList.contains('js-panel__item')){return;} e.target.classList.toggle('is-open'); } function toggleActive(e) { if(!e.target.classList.contains('js-panel__item')){return;} if (e.propertyName.includes('flex')) { e.target.classList.toggle('is-active'); } } ``` 這邊就直接使用事件委派來監聽底下元件,並使用 Day01 有用到的方式來監聽動畫完成後的要執行動作。 ### 2. 進階 #### 結合兩段狀態 使用 `transition-delay` 延遲第二段動畫執行。 `.panel__item` 的 `transition` 是 `0.7s` 因此裡面內容的 `<p>` 可以加上 `transition-delay: 0.7s` 讓它跑完動畫後再執行。 ```css .panel__item > p { transition-delay: 0.7s; } .panel__item.is-active { flex: 5; font-size: 2em; } .panel__item.is-active > p:nth-child(1) { transform: translateY(-100%); } .panel__item.is-active > p:nth-child(3) { transform: translateY(100%); } ``` 這樣就不用監聽動畫結束,直接延遲它的動畫觸發時間。另外在加上一點小功能,就是只允許一個畫面是展開狀態。 ```javascript const panelList = document.querySelector('.js-panel__list'); panelList.addEventListener('click', toggleActive); function resetActive() { const items = document.querySelectorAll('.js-panel__item'); items.forEach(function(key) { key.classList.remove('is-active'); }); } function toggleActive(e) { if (!e.target.classList.contains('js-panel__item')) { return; } resetActive(); e.target.classList.toggle('is-active'); } ``` 這邊一樣使用事件委派,並加上點選一個就重置其他 `.panel__item` 狀態的函式。 ### 3. 實作連結 - [兩段狀態](https://chupai.github.io/JS30/source_code/Day05/index1.html) - [實作連結](https://chupai.github.io/JS30/source_code/Day05/index.html)
×
Sign in
Email
Password
Forgot password
or
Sign in via Google
Sign in via Facebook
Sign in via X(Twitter)
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
Continue with a different method
New to HackMD?
Sign up
By signing in, you agree to our
terms of service
.