題目除了基本 Gallery 介面樣式以外,還有點擊後的介面
上面的圖是題目,而我們要做出幾乎一樣的樣子,題目中還有附上出題官方的CodePen,也有附上給我們解題用的template,當我們真的不會的時候,還是可以參考他們的寫法,所以沒有想像中困難。
我做好的此題CSS Challeage解答
那麼我們就開始吧。
這個題目要求我們製作一個 User Gallery
我們昨天已經把最開始的四格 User Gallery 跟滑鼠指上的效果都做出來了如下圖,現在我們就來做點擊後要打開的 info 畫面。
<div class="frame">
...
<div class="info">
<div class="panel>
<div class="eachPanel" id="panel-1">
<div class="header">
<img src="..." />
</div>
<img class="profile" src="..." />
<div class="close"></div>
<div class="bottom">...</div>
</div>
</div>
...
</div>
</div>
在 gallery 下面,我們新增另一個區塊,叫做 info。
因為我希望點擊 pic-1 的時候,能夠開啟 #panel-1 ,依此類推去開啟不同的 panel(雖然題目沒有做到這個程度),那這邊我的 HTML 就以 #panel-1 來做示範,後面的其他 panel 就依此類推。
這邊我會把 #panel-1 到 #panel-4 都加上一個名為 eachPanel 的 class 並包在 .panel 內,這是為了定位的問題。這四個 div 到時候會是 panel 內的唯一物件,一次只會顯示其中一個。
讓我說明一下:info 本人會是 position: absolute 的狀態,這樣他才能蓋在 gallery 上面。panel 則會是 position: relative 的狀態,這樣他裡面的那些元素們,才能在範圍內飛來飛去。有些人會在這邊包了四個 panel 甚至是使用 display: none 來包裝他們,但使用 display 並不能製造出位移的 transition,因此必須使用 opacity: 0 來控制顯示。
當使用了 opacity 來控制顯示,就代表這四個 div 在網頁中都有佔空間,panel-4 會被擠到很下面去根本看不到,因此這邊才會需要把它們都包在同一個 panel 內,吃同一個定位。
另外,題目上可以看出來,當我點擊 Gallery 的圖片之後,info 的內容是從上下位移進來蓋住 Gallery 畫面的,因此我可以在這邊把畫面區分成上下兩塊 div,分別取名為 header 跟 bottom。
再觀察題目,可以發現飛進來的動畫分成四個部分:
header 從畫面上飛進來。bottom 資訊區塊,從畫面下面飛進來。而且這四個動畫都有些許的順序,也就是它們在 click 之後,各自有先延遲一段不同的長度才開始動畫。
因此我們可以知道,這幾個項目要做在同一層比較妥當(彼此都不要成為彼此的父項)
所以 header 的部分按照題目的,包了題目提供的圖片。
接著是照片跟右上角的關閉按鈕,然後是 bottom 內放上應該要有的資訊項目跟 action button。
這樣我們基礎架構就完成了。
這邊的按鈕 icon 一樣是使用 css challenge 官方的 font-awesome 來製作,當然你也可以用自己的。
這邊可以看到我的關閉按鈕並沒有使用 font-awesome,是因為他的按鈕的樣式不符合題目要的,他的樣式比較粗也比較小,我試著調整過,但沒有讓我滿意,我希望他能跟 gallery 上面的 "+" 按鈕一樣的樣子,所以我這邊決定把 close icon 用跟 gallery 一樣的方法重做,因此這邊沒有使用 font-awesome 的 icon 來製作。
.info {
z-index: 5;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
pointer-events: none;
overflow: hidden;
font-size: 0;
&.active {
pointer-events: all;
}
}
那我們就先來做點擊之後顯示或隱藏的部分,我想使用 info 來控制,那麼我們就先來設計展開的 info 的樣子。
z-index: 5:設置 .info 的堆疊順序,使其位於其他層之上。position: absolute top: 0 left: 0 right: 0 bottom: 0:確保 .info 元素完全覆蓋其父容器,填滿所有空間。pointer-events: none:禁用滑鼠事件,因為這個區塊其實是永遠在最上層的,只是看的到或看不到。但也因為它在上層,我們必須隱藏他的滑鼠事件,否則下面 gallery 那一層的滑鼠事件就永遠不會被點到。因此我們先把它的滑鼠事件隱藏起來,這樣元素不會干擾點擊操作。overflow: hidden:隱藏超出容器的內容。.active:這是為了之後動畫作的,我預計讓 gallery 被點擊之後,在 info 的同一個 div 上加上 .active 這個 class。當 .info 被觸發時,通過 pointer-events: all 恢復滑鼠事件。.panel {
position: relative;
width: 100%;
height: 100%;
}
這邊我就先設定好 panel 的基本樣式。
那剛做完的時候長這樣:
接下來我們就一個個拆解內部的元素
$headerH: 180px;
$bottomH: 400 - $headerH;
這邊先把一些會用到的元素計算出來:
$headerH:這是用我們第一天教的尺規小工具量出來的 header 高度。$bottomH:這是用我們的 .frame 的高度剪掉 $headerH 就能得出下半截 bottom 的高度了。.panel {
.header {
position: absolute;
height: $headerH;
z-index: 6;
overflow: hidden;
transform: translate3d(0,0,0)
transition: all .6s ease-in .4s;
img {width: 100%; height: 100%;}
}
}
position: absolute:將 .header 定位於父元素內部的絕對位置。height: $headerH:設定 .header 的高度,值取自變數 $headerH。z-index: 6:確保 .header 具有較高的堆疊順序,我這邊是讓它放在 info 之上。overflow: hidden:隱藏任何超出 .header 容器的內容。transform: translate3d(0,0,0):這邊我們看到的是最後的位置,這邊之後是要放在 .active 內的,但沒關係,我們這邊先這樣做,之後再跟 active 內的位置交換。img {width: 100%; height: 100%;}:確保 .header 內的圖片充滿整個容器並保持比例。.panel {
...
.bottom {
@extend .header;
height: $bottomH;
background: $primary;
top: $headerH;
left: 0;
right: 0;
padding-top: 50px;
box-sizing: border-box;
transform: translate3d(0,0,0);
}
}
因為 bottom 很多屬性都跟 header 重複,所以這邊我直接 @extend 了 header 的屬性,然後我們再來做局部修改。
height: $bottomH:設定 .bottom 的高度,使用變數 $bottomH。background: $primary:將 .bottom 的背景色設置為主色,取自變數 $primary。top: $headerH:將 .bottom 定位於 .header 的正下方,使用變數 $headerH 來確定位置。left: 0 right: 0:確保 .bottom 在水平方向上填滿整個容器。transform: translate3d(0,0,0):這邊我們看到的是最後的位置,這邊之後是要放在 .active 內的,但沒關係,我們這邊先這樣做,之後再跟 active 內的位置交換。padding-top:將區塊上方邊界留空,好讓名字的文字不會貼在 .bottom 的最上方box-sizing:因為設定了 padding,這邊高度會超出預期,所以使用 border-box,讓整體仍然維持在本來期望的高度,這樣等等跑動畫的時候才不會有不預期的情形發生。<div class="bottom">
<p class="name">Julia Toth</p>
<div class="action">
<button><span class="fa fa-phone"></span></button>
<button><span class="fa fa-comment"></span></button>
<button><span class="fa fa-heart"></span></button>
</div>
</div>
這邊是 bottom 內的架構,我們就在這邊先開版。
因為這些東西會跟著 bottom 一起移動,上面三個按鈕也只有滑鼠指上效果,所以相對單純。
.name {
font-size: 16px;
font-weight: 600;
text-align: center;
}
首先是姓名,剛修改好樣式的話是長這樣:
.action {
display: flex;
margin: 0 auto;
justify-content: space-between;
width: 50%;
padding-top: 15px;
}
然後是三個按鈕外的 div,我使用它來讓三顆按鈕水平對齊,並使用 padding 創造出與上面的名字的距離,也使用 justify-content: space-between 及 width 創造三顆按鈕之間的距離。
$actionIconWH: 45px;
button {
width: $actionIconWH;
height: $actionIconWH;
border-radius: 50%;
background-color: $primary;
border: 1px solid #fff;
color: #fff;
transition: all .2s ease-in-out;
cursor: pointer;
&:hover {
color: $primary;
background-color: #fff;
}
}
然後我們可以來做 button 的部分,我直接就寫在 button 上。
$actionIconWH:建立一個新的變數去控管 button 的寬高。
大小與形狀:
width: $actionIconWH height: $actionIconWH:按鈕的寬高為 45px(變數 $actionIconWH 的值)。border-radius: 50%:將按鈕設置為圓形。顏色與視覺樣式:
background-color: $primary:背景色使用 $primary 主色。border: 1px solid #fff:邊框為 1px 的白色實線。color: #fff:文字顏色為白色。滑鼠指上效果:
transition: all .2s ease-in-out:所有樣式變化在 0.2 秒內平滑過渡。&:hover:當滑鼠懸停時,按鈕背景變為白色,字體顏色變為 $primary 主色。
這樣三個 button 的樣式就差不多了,那我們就來調整裡面 icon。
button {
...
.fa {
line-height: 20px;
text-align: center;
font-size: 20px;
}
}
我們先簡單粗暴的把 .fa 這個 icon 的 style 加上字體大小,設定讓它在按鈕中間置中,也設定了他的行高,但很快的就會看出不對勁。

可以看到雖然有 icon 了,但每個 icon 的大小不一樣,高低也不太相同。
這是因為舊款的 font-awesome 產生出來的 icon 並不會每個都一樣大,而且他們其實是一種字體!
你可以去網頁上按右鍵,選檢查,點到那個 icon 就可以看到他其實是一個偽類,內容是一個字,早期的 font-awesome 是這樣給他一個字體,去讓它呈現 icon 的樣子。


這時候我們能去做的,就是修改這個字:
button {
...
.fa {
line-height: 20px;
text-align: center;
&-phone {
font-size: 22px;
padding-top: 5px;
padding-left: 1px;
}
&-comment {
font-size: 20px;
padding-bottom: 2px
}
&-heart {
font-size: 19px;
padding-top: 4px;
padding-left: 1px;
}
}
}
針對每個 icon 修改它的字型大小,在需要微調的地方加上不同方向的 padding,讓他們在視覺上儘量做到一制,在視覺上不會有大大小小或偏移的感覺就可以了。
目前剛做好的話長這樣:

由於篇幅太長了,我決定分兩篇寫,下一篇再來寫後面的照片、右上角關閉按鈕、JS開關介面及動畫的部分。
希望改變了這種按照步驟的寫法,能讓更多人看得懂,也能跟我一樣喜歡上寫CSS。
那今天就先到這裡,明天我們再繼續來玩下一集。