純前端實現 App Store 卡片展開效果
今集我們會模仿 App Store 這個卡片展開效果:
本來想只用 HTML 和 CSS 去製作,但最後都要運用到 JavaScript,那我們立即開始吧。
HTML 的部分
打開 CodePen 編輯器,首先建立 HTML 結構。新增一個 <div>
,class 是 card
,裏面新增圖片,圖片來源使用 Unsplash
網站的隨機圖片。再加入標題,文字例如是 APP OF THE DAY。
然後新增一個 <div>
,class 是 content-wrapper
,裏面再新增一個 <div>
,class 是 content
。爲什麼需要兩層 <div>
呢,我們稍後會介紹。
裏面新增內文,生成一些假字,然後用 <p>
這個段落標籤整理好它們。
HTML 結構大致上是這樣,然後到 CSS 的部份。
CSS 的部分
新增 :root
選擇器,設定基礎文字大小爲 15px
。字體 font-family
設定爲 Helvetica
。
然後定義一些變量,由於這個效果適合在手機的寬度上展示,所以我會將網頁寬度設定爲 480px
,定義 --body-width
是 480px
。然後定義卡片的大小,--card-width
定義爲 420px
,--card-height
定義爲 280px
。
再定義圖片的高度,未展開的圖片高度 --img-height
定義爲 226px
,展開後的圖片高度 --img-height-expanded
定義爲 320px
。最後,背景顏色設定爲深灰色。
加入 body
選擇器,設定寬度爲 var(--body-width)
,背景顏色設定爲淺灰色,margin
設定爲 auto
,這樣就會將 body
置中。
然後將內容左右置中,設定 display
是 flex
,flex-direction
設定爲 column
,align-items
設定爲 center
,然後 min-height
設定爲 100vh
。最頂和最底加一點 padding
,設定爲 1rem 0
。
加入 .card
選擇器,寬度設定爲 var(--card-width)
,高度設定爲 var(--card-height)
,背景顏色設定爲白色。
暫時看不到白色的背景,是因爲這張圖片的大小比起 card
容器更大完全遮蓋了。新增 .card img
選擇器,將 display
設定爲 block
,寬度設定爲 100%
,然後高度設定爲 var(--img-height)
,這時候會發現圖片的比例不對,加入 object-fit: cover
這個設定就可以讓圖片按比例縮放填滿。
回到 .card
的設定,加入圓角,border-radius
設定爲 1rem
。再加點陰影,顏色是非常淺的灰色就可以了。
然後會發現圖片的左上角和右上角並沒有圓角設定,我們可以在 .card
中加入 overflow: hidden
,讓 .card
將超出的部份遮蓋,將圖片變成圓角。不過由於在打開 .card
的時候,圓角會變爲直角,而 overflow
並不能套用動畫的,所以在這裏不會用上 overflow
,而是在 img
中直接將左上角和右上角套用圓角設定。
然後設定標題字的樣式,加入 .card h4
選擇器,將 margin
設定爲 0
,文字大小設定爲 1.5rem
;字體設定爲粗體;加入 padding
,上下設定爲 0.8rem
,左右設定爲 1.2rem
;背景顏色設定爲白色。
我想文字在垂直方向更加置中,設定 line-height
爲 2rem
,然後將字距縮小一點,letter-spacing
設定爲 -0.5px
,再將 padding-bottom
設定爲 0
,就不會遮着 .card
的圓角了。
接下來是設定內文的部份,分別加入 .card .content-wrapper
與 .card .content-wrapper .content
選擇器。
爲什麼我們會分爲 .content-wrapper
和 .content
呢?因爲當點擊卡片展開的時候,會通過 JavaScript 計算並設定內文容器的高度,所以我們會將展開的動畫套用到外層,而高度的設定就會套用到內層。
這樣當關閉卡片的時候,就不用再將高度的設定還原,其實重點是可以少寫一些 JavaScript,讓 CSS 去完成它。
先設定 .content
的樣式,加入 padding
,設定爲上下 0
,左右 1.2rem
;背景顏色設定爲白色;然後 overflow
設定爲 auto
,這樣當內文高於容器所設定的高度時,頁面就可以捲動。而容器的高度會在稍後 JavaScript 那邊控制。
內文段落的樣式也調整一下,加入 .card p
選擇器,將文字大小設定爲 1.2rem
,行高設定爲 1.5rem
就可以了。
然後到 .content-wrapper
,設定高度是 0
,再加入 overflow: hidden
,這樣超出容器的部份就會隱藏。
現在卡片的樣式就差不多了,在開始做展開功能之前,先在 HTML 中再複製多幾個卡片。爲了展示不同的隨機圖片,我會在 Unsplash 的網址中加入不同的參數,在後面加上 1, 2, 3, 4,就可以了。
然後到 .card
選擇器上,加入 margin: 1rem 0
將卡片分開一點。
JavaScript 的部分
接下來處理卡片展開的部份,我會通過在點擊卡片時,加入一個 class
去改變它的樣式,從而達到展開的效果。爲了減少編寫 JavaScript 的數量,我會加入 jQuery,打開 Settings,在 JS 的部份加入 jQuery,然後點擊 Save & Close。
在 JavaScript 的部份,加入 .card
的 on('click')
事件。定義一個變量,card
賦值 e.currentTarget
,這裏獲取到的就是所點擊的 .card
的本身。
然後加入 $(card).toggleClass('active')
,這樣當 card 沒有 active
class 的時候就會加上,有的時候就會移除,達到展開與收起的效果。
CSS 樣式的調整
現在可以回到 CSS 的部份,設定 active
狀態下 card
的樣式。加入 4 個選擇器,分別是 .card.active
移動卡片的位置,.card.active h4
標題的樣式,.card.active img
圖片的樣式,以及 .card.active .content-wrapper
展開內文。
第一步想做的是展開內文,在 .content-wrapper
選擇器內,加入 height: 100vh
,然後點擊卡片試試:
可以看到內文已成功展開,再來是移動卡片,其實除了移動,我們還會放大一點卡片,以填充左右的寬度。
在 .card
選擇器內加入 transform
,先設定 translateY()
,向上移動多少呢,其實需要計算元素與頂部的距離,才能將它緊貼着頂部,我們先暫時設定爲 -300px
,稍後再計算。
在後面再加入 scale()
將卡片放大一點,放大的比例是 body
寬度除以卡片寬度,即 480
除以 420
,transform-origin
中心點設定爲 50% 0
,點擊一下,可以看到已經有八成的效果了。
先做一些樣式的微調,展開的狀態下,移除圓角的設定,同時也移除圖片的圓角設定。然後將圖片拉高一些:
再加入一些動畫過渡的設定,回到 .card
選擇器,加入 transition: .3s all
,加速度是 cubic-bezier(0, 1, 0.95, 1.05)
:
可以打開開發者工具,看到這個加速度的線性圖表:
你可以試試拉動一下這條曲線,就可以達到不同的加速度效果,我這個設定就可以做到它打開和收起時有一點回彈的效果。
計算位移
回到 JavaScript 的部份,我們要計算卡片與頂部的距離。定義變量 card_offset_scrolltop
,將卡片的 offset().top
減去 window.scrollTop()
,即捲動距離。然後將計算結果用 CSS 變量的形式設定到卡片上,變量名稱改爲 --data-offset-top
,設定值乘以 -1
並加上 px
單位。
回到 CSS 的部份,將 .card.active
內 transform
的 translateY()
裏面的 -300px
替換成 var(--data-offset-top)
,再點擊測試一下:
可以成功定位到頁頂了。
繼續優化
你以爲已經做完了?不要停,繼續優化餘下的部份。
現在在展開卡片後,捲動頁面的話會穿幫,所以要在打開卡片後,暫停 body
的捲動。
在 JavaScript 的部份,判斷如果卡片有 active
這個 class,即是打開的狀態下,在 body
上加入 noscroll
這個 class;反之就移除 noscroll
這個 class。
打開 CSS 的部份,加入 body.noscroll
選擇器,設定 overflow: hidden
,這樣就可以達到防止捲動的效果了。
然後再處理內文捲動的部份,在 JavaScript 那邊,定義 height
變量,賦值爲 window.height()
。要計算內文部份的高度,就將 window
高度減去圖片高度,再減去標題高度就可以了。
然後將這個高度套用到 .content
容器上,測試一下:
爲什麼會卷不到底的呢?這樣就代表高度的計算有錯誤,但計算有什麼錯呢?
給三秒鐘時間你想一想,3、2、1。
這是由於我們的卡片,通過 transform
的 scale()
放大了,所以在計算圖片與標題高度的時候,要乘以放大比例。
定義一個變量名爲 ratio
,賦值爲 480/420
,分別將圖片與標題高度乘以 ratio
,而最後,又要將總數除以這個 ratio
,再測試一下:
今次高度就正確了。
再做最後的一些微調,捲動的時候會發現 APP OF THE DAY 的下方 padding
稍爲少了一點,在 .card.active h4
內加入 padding-bottom: .8rem
:
然後內文出現的時候加一些漸入效果,在 .content-wrapper
內加入透明度設定 opacity: .8
,transition: .3s all ease-out
:
再在展開的 .content-wrapper
內加入 opacity: 1
,transition: .3s all ease-in
,這樣在打開與收起的時候就會有不同的加速度效果。
我們來看看這個案例的完成效果
以上,就是今集要介紹的全部內容。
這個案例的源代碼:https://codepen.io/stevenlei/pen/poyNGNP
轉自:CodingStartup 起碼課
https://juejin.cn/post/6917479303572226061
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/y40GbnG4r5fuzKMHT6MX6w