竹白記事本,Javascript 30,紀錄。
Javascript 30
點擊任意一張圖片,圖片展開,同時從圖片上下兩方分別移入文字。點擊已經展開的圖片後,圖片被壓縮,同時該圖片上下兩端的文字被擠走。
flex
排版flex
transform
addEventListener()
事件監聽
TransitionEvent
介面
propertyName
屬性Day5 的範例 CSS 的比重比較多。基本上就是透過 Flex
排版完成頁面。透過 flex: ;
屬性控制元件的比例。
關於 CSS 的 Flex,之前有整理過一篇 Flex 教學整理,可以參考一下。
<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
水平置中排列,就需要在容器加上:
.panel__list {
display: flex;
justify-content: center;
}
而要使元件等比寬,就需要在元件上加上 flex-grow
來控制擴展比例:
.panel__item {
flex: 1;
}
flex
為簡寫屬性,當只有一個參數等同 flex-grow
。
接下來排序 <p>
元素,我要們使它水平垂直置中,並且垂直排列,就要在它的容器 .panel__item
加上:
.panel__item {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
接下來處理<p>
元素的大小與文字的排版:
.panel__item > p {
display: flex;
align-items: center;
flex: 1;
}
原始狀態:
.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%);
}
加上狀態:
.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
。當然這樣子做有點麻煩,之後會試著將其改寫再一起。
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 有用到的方式來監聽動畫完成後的要執行動作。
使用 transition-delay
延遲第二段動畫執行。
.panel__item
的 transition
是 0.7s
因此裡面內容的 <p>
可以加上 transition-delay: 0.7s
讓它跑完動畫後再執行。
.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%);
}
這樣就不用監聽動畫結束,直接延遲它的動畫觸發時間。另外在加上一點小功能,就是只允許一個畫面是展開狀態。
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
狀態的函式。