盤踞在大台南市區的四大天王「火爆浪子」最近在經營網路形象, 他們需要一個頁面來一段魅力的展現, 要求如下:
這次任務若滿足「火爆浪子」, 以後在大台南地區就能橫著走了! 但如果失敗, 以後就只能橫屍街頭. 該怎麼化解危機呢? 讓我們看下去...
下面是HTML框架.
<div class="panels">
<div class="panel panel1">
<p>玩</p>
<p>火</p>
<p>人</p>
</div>
<div class="panel panel2">
<p>鬍</p>
<p>爆</p>
<p>哥</p>
</div>
<div class="panel panel3">
<p>衝</p>
<p>浪</p>
<p>孩</p>
</div>
<div class="panel panel4">
<p>種</p>
<p>子</p>
<p>男</p>
</div>
</div>
四大天王的照片存在各個panel的背景中
.panel1 { background-image:url(https://source.unsplash.com/asytdeogTDE/1500x1500); }
.panel2 { background-image:url(https://source.unsplash.com/XhMSz5I1kn8/1500x1500); }
.panel3 { background-image:url(https://source.unsplash.com/i4OHxtxiMtk/1500x1500); }
.panel4 { background-image:url(https://source.unsplash.com/kPZwI56RbkY/1500x1500); }
第一步, 必須讓包著四大天王照片的panel
從左至右排列, 並且平均分配欄寬, 這時候可以用到Flex Box Module!
Flex Box顧名思義為具有彈性的盒子模型.
只要將元素的display
屬性設為flex
, 該元素的子元素就會具有適應容器空間的能力. 此時該元素成為Flex Box的作用容器, 稱做flex-container
, 而容器內的子元素們稱作flex-item
.
借用MDN文件上這張精美的圖, Flex Box內有兩個軸, 一個是主軸(main-axis), 一個是和主軸垂直的交叉軸(cross-axis). 預設情況下, 主軸都是左右橫向, 交叉軸是上下直向. 成為flex-item
的元素們會依主軸方向, 從起點(main-start)往終點(main-end)排列.
因此要處理四大天王的照片, 我們可以在panels
類別將display
設成flex
.
.panels {
/* 前略 */
display: flex;
}
照片們的確依照主軸方向由左到右排列了. 但是卻沒有塞滿整個畫面.
此時就要說到flex
屬性了.
flex
屬性flex
屬性是作用在容器內的元素, 也就是flex-item
的屬性. flex
屬性是flex-grow
, flex-shrink
, flex-basis
三個屬性的依序簡寫, 用來定義各個flex-item
的預設寬度與容器空間的關係. 下面個別說明.
flex-basis
屬性可以想成是flex-item
的預設寬度, flex-basis
預設值為auto
, 意思就是去參考元素本身的width
或height
值.
flex-grow
屬性flex-grow
屬性值不為0時, 該屬性啟動, 表示當容器有多餘空間時, flex-item
會自動填滿剩餘空間, flex-grow
的數字代表分到多餘空間的比例.
舉例說明:
<div id="outer">
<div id="bar1"></div>
<div id="bar2"></div>
</div>
#outer {
width: 300px;
height: 50px;
display: flex;
border: 1px solid blue;
}
#bar1 {
flex-basis: 100px;
flex-grow: 1;
background: gold;
}
#bar2 {
flex-basis: 100px;
flex-grow: 2;
background: lightblue;
}
bar1 = document.getElementById('bar1').offsetWidth;
bar2 = document.getElementById('bar2').offsetWidth;
console.log(bar1, bar2) // 133 , 167
容器outer
扣掉bar1
, bar2
的寬度後, 還有100px
的剩餘空間, 因為bar1
和bar2
的flex-grow
為1: 2, 因此剩下的100px
, bar1
只能分到三分之一, bar2
能分到三分之二, 所以最後寬度分別為133px
, 和167px
flex-shrink
屬性flex-shrink
和flex-grow
相反, 屬性值不為0時, 該屬性啟動, 表示當容器空間不足時, flex-item
會依照屬性值的比例自動縮減寬度.
大概如上述, 當一個元素變成flex-item
時, flex
屬性會有基本預設值flex: 0 1 auto
, 也就是不會自動填滿多餘空間, 空間不足會自動縮減寬度, 基本寬度值參考width
屬性.
說了這麼多, 要讓四大天王的照片填滿版面, 只需要將子元素panel
的flex
屬性更改即可:
.panel {
flex: 1; /* 意思同等於 flex-grow: 1 */
}
四大天王們的名字由panel
內的三個子元素p
所組成, 我們希望三個子元素能夠
看起來又可以用到Flex Box的特性了!
沒錯, Flex Box是可以嵌套的, 一個元素可以是flex-item
, 同時又是別人的flex-container
.
因此可以讓.panel
成為p
的flex-container
.panel {
flex: 1; /* 意思同等於 flex-grow: 1 */
display: flex;
}
有提到flex-item
預設沿著主軸, 也就是左右向排列, 該怎麼實現垂直排列呢? 可以透過flex-direction
屬性改變主軸方向, 預設flex-direction: row
, 只要flex-direction: column
, 主軸會變成上下方向, 交叉軸變成左右方向. flex-item
自然由上到下排列. 別忘了設定panel
內子元素們的flex
, 讓他們自動擴張.
.panel {
flex: 1; /* 意思同等於 flex-grow: 1 */
display: flex;
flex-direction: column;
}
.panel > * {
flex: 1 0 auto;
}
p
元素內的字此時尚未置中, 這可是四大天王的名字, 這對天王們是大不敬. 得趕快修正. 在flex-container
上設定align-items: center
可以讓子元素們上下置中, justify-content: center
可以左右置中.
.panel > * {
flex: 1 0 auto;
display: flex;
justify-content: center;
align-items: center;
}
對p
來說, 該子元素為裡面的字, 於是字就對齊了.
接下來我們希望四大天王的名字最上面跟最下面的字一開始是在畫面外, 點擊後會滑進來. 中間的字則會放大. 然後畫面會擴張.
利用transform: translate()
將字先移到畫面外待命, 當給panel
加上open-active
類別時會滑進來.
.panel > *:first-child {
transform: translateY(-100%);
}
.panel.open-active > *:first-child {
transform: translateY(0);
}
.panel > *:last-child {
transform: translateY(100%);
}
.panel.open-active > *:last-child {
transform: translateY(0);
}
讓panel
加上open
類別時, flex-grow
會擴張比較多.
.panel.open {
font-size:40px;
flex: 5;
}
接下來加上JS程式碼, 讓點擊時可以讓open-active
跟open
類別被切換, 利用style.classList.toggle()
可以做到這點. 程式碼如下:
const panels = document.querySelectorAll('.panel');
panels.forEach(panel => panel.addEventListener('click', toggleOpen));
panels.forEach(panel => panel.addEventListener('transitionend', toggleActive));
function toggleOpen() {
this.classList.toggle('open');
}
function toggleActive(e) {
console.log(e.propertyName)
if(e.propertyName.includes('flex')) {
this.classList.toggle('open-active');
}
}
這裡要注意的是toggleActive()
函數的內容, 程式預設在transitionend
事件觸發時呼叫toggleActive
函式來回應, 由於Safari瀏覽器和Chrome/Firefox瀏覽器對flex-grow屬性儲存在e.propertyName的名稱並不相同, 一個為flex
, 一個為flex-grow
, 為了支援全部瀏覽器, 程式內寫只要屬性名稱"包含"flex四個字, 才執行切換類別的動作.
大台南四大天王非常滿意.
以上就是JS30 第五篇!
大台南四大天王, 友情贊助.
「玩火人」戴著紅帽, 擅使火技
「鬍爆哥」脾氣暴躁, 滿臉鬍渣
「衝浪孩」渾身濕透, 性喜衝浪
「種子男」一雙妙手, 四處播種