作者:?緝熙Soyaine
簡介:JavaScript30 是 Wes Bos 推出的一個 30 天挑戰(zhàn)财边。項目免費提供了 30 個視頻教程挤庇、30 個挑戰(zhàn)的起始文檔和 30 個挑戰(zhàn)解決方案源代碼。目的是幫助人們用純 JavaScript 來寫東西奥溺,不借助框架和庫,也不使用編譯器和引用。現(xiàn)在你看到的是這系列指南的第 5 篇赠制。完整指南在 GitHub,喜歡請 Star 哦?(?*)
實現(xiàn)效果
點擊任意一張圖片挟憔,圖片展開钟些,同時從圖片上下兩方分別移入文字。點擊已經(jīng)展開的圖片后绊谭,圖片被壓縮政恍,同時該圖片上下兩端的文字被擠走。若圖片加載不出來达传,請點鏈接看更完整的演示圖片篙耗,在線效果請點這里迫筑。
初始文檔的 DOM 結(jié)構(gòu):以 .panels
為父 div
之下,有 5 個類名為 .panel
的 div
鹤树,這 5 個各含有 3 個子 p
標(biāo)簽铣焊。而相應(yīng)的 CSS 樣式中,動畫時間等特性已經(jīng)設(shè)定好罕伯,只需要完成不同狀態(tài)下的頁面布局以及事件監(jiān)聽即可曲伊。
涉及特性
- display: flex
- flex-direction
- justify-content
- align-items
- transform: translateX/translateY
- element.classList.toggle()
- transitionend 事件
過程指南
CSS 部分
CSS 在這個過程中占了重點,運用 flex
可以使各個元素按一定比例占據(jù)頁面追他。在調(diào)試的時候坟募,可以把邊框顯示出來方便查看效果。(border: 1px solid #f00;
)
- 將
.panels
設(shè)置為display:flex
- 設(shè)定每個子 panel 的
flex
值為 1 - 針對每個子 panel邑狸,設(shè)為
display:flex
懈糯,設(shè)置其 flex 主軸方向 - 控制
.panle
的子元素<p>
中的文字垂直、水平居中(單獨看每個 panel单雾,其中的文字也可以用 flex 的思路來使其三等分后居中)- 設(shè)置為
display:flex
- 設(shè)置
flex
值 - 設(shè)置其子元素的布局方式:垂直水平居中(沿主軸赚哗、側(cè)軸居中)
- 設(shè)置為
- 設(shè)定點擊圖片后文字移動的樣式
- 設(shè)定點擊圖片展開后的圖片的
flex
值
JS 部分
- 獲取所有類名為
panel
的元素 - 為其添加
click
事件監(jiān)聽,編寫觸發(fā)事件調(diào)用的函數(shù)(給觸發(fā)的 DOM 元素添加/去掉樣式硅堆,實現(xiàn)拉伸/壓縮的效果) - 為其添加
transitionend
事件監(jiān)聽屿储,編寫調(diào)用的函數(shù)(添加/去掉樣式,實現(xiàn)文字的飛入/飛出效果)
相關(guān)知識
Flexbox
這一個挑戰(zhàn)的關(guān)鍵部分就在于理解如何使用 Flexbox渐逃,挑戰(zhàn)的文檔里嵌套了三個 flex 容器够掠,作為彈性盒子,它們各自的作用是:
-
.panels
:使其中的.panel
按橫向的 flex 等分排布(此處為五等分) -
.panel
:使其中的<p>
按縱向的 flex 等分排布(此處為三等分) -
p
:借用 flex 相對于主軸及側(cè)軸的對齊方式茄菊,使其中的文字垂直水平居中
這里容易混淆的是不同 CSS 屬性的應(yīng)用對象疯潭,想?yún)^(qū)分很簡單,只需記住針對容器內(nèi)子元素的特性較少(只有 5 個)面殖,可以這樣聯(lián)想:針對某一個具體的小元素進行設(shè)置竖哩,可供發(fā)揮的空間比較少,而針對 Flex 容器本身脊僚,有統(tǒng)籌大局的責(zé)任期丰,故特性多一些。下面簡單介紹一些基本的特性(沒有完全列出)吃挑。
針對 Flex items 的特性(Children)
-
flex-growth
:伸展值 -
flex-shrink
:可接受的壓縮值 -
flex-basis
:元素默認的尺寸值 -
flex
:以上三個值按順序的縮寫
針對 Flex container 的特性(Parent)
-
display: flex
:將這個元素設(shè)置成彈性盒子 -
flex-direction
:主軸方向-
row
:橫向 -
column
:縱向
-
-
justify-content
:沿主軸的的對齊方式 -
align-items
:沿側(cè)軸的對齊方式 -
align-content
:子元素中文本沿側(cè)軸的對齊方式(只有一行時無效)
可以在下面幾個地方試一下 Flex 的各種特性:
- http://demo.agektmr.com/flexbox/
- http://the-echoplex.net/flexyboxes/
- http://codepen.io/justd/pen/yydezN
延伸思考
在 index-FINISHED.html 的解決方案中,用了兩種 class
值來分別控制 div
元素和 p
元素的動畫街立,這就會造成一個問題舶衬,當(dāng)快速點擊兩下時,會出現(xiàn)相反的組合(圖片縮小 + 上下文字出現(xiàn))赎离。
那為什么還要將文字的移動動畫用 .open-actived
這個類來控制逛犹,同時還多加上了一個 transitionend
的事件監(jiān)聽,而不是直接用 .open
控制文字的移動,并且只采用一個 click
事件監(jiān)聽呢虽画?
我試了一下舞蔽,發(fā)現(xiàn)如果將要觸發(fā)的文字移動(transform
)用 .open
來控制,那么會出現(xiàn)有點不協(xié)調(diào)的狀況码撰。
要找到問題所在渗柿,可以先研究一下動畫效果,由于錄 GIF 很容易掉幀脖岛,最好打開網(wǎng)頁來看細節(jié)朵栖。
當(dāng)拉伸圖片時,首先往里壓縮(階段①)柴梆,然后再展開(階段②)陨溅,而文字是階段②出現(xiàn)的;而當(dāng)壓縮圖片時绍在,也是同樣的道理门扇,先微微拉開一點(階段①),然后再往里縮(階段②)偿渡,文字也是在階段②才往上移動的臼寄,這樣就形成了一種被 pia 飛的效果。
這樣也就可以回答我最開始的疑問卸察,為何要多添加一個 transitioned
的事件監(jiān)聽脯厨,這個事件會在 transition
結(jié)束之后被觸發(fā),所以目的是先讓圖片的壓縮拉伸完成坑质,再移動文字合武。
也就是說,如果除去字體大小的變化涡扼,具體的動畫細節(jié)其實是這樣的:
- 圖片展開:微微壓縮一段距離 -> 展開圖片 -> 文字向中心移動
- 圖片壓縮:微微展開一段距離 -> 壓縮圖片 -> 文字向上下移動
這就解釋了為什么我改動之后出現(xiàn)了不協(xié)調(diào)稼跳,此時看到的動畫,像是文字主導(dǎo)了圖片的壓縮伸展吃沪,原因就是文字動畫的時機不太對汤善,找到了這個原因,就很好解決了票彪。(見 index-SOYAINE2.html)
.panel > * {
/* ... */
transition:transform 0.5s 0.7s;
}
/* 修改 .open-actived -> .open*/
.panel.open p:first-child {
transform: translateY(0);
}
.panel.open p:last-child {
transform: translateY(0);
}
const panels = document.querySelectorAll('.panel');
function toggleOpen(e) {
this.classList.toggle('open');
}
panels.forEach( panel => panel.addEventListener('click', toggleOpen, false));
// 去掉對于 transitionend 的事件監(jiān)聽
解決思路是讓 p
標(biāo)簽的文字動畫效果延遲一下红淡,添加 transition
屬性的 delay
值,使其到圖片變換的階段②再發(fā)生降铸,此處我選用了圖片的動畫最長時間 0.7s在旱,圓滿解決。
挑戰(zhàn) 5 Pass (≧▽≦)/