純前端實現 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-width480px。然後定義卡片的大小,--card-width 定義爲 420px--card-height 定義爲 280px

再定義圖片的高度,未展開的圖片高度 --img-height 定義爲 226px,展開後的圖片高度 --img-height-expanded 定義爲 320px。最後,背景顏色設定爲深灰色。

加入 body 選擇器,設定寬度爲 var(--body-width),背景顏色設定爲淺灰色,margin 設定爲 auto,這樣就會將 body 置中。

然後將內容左右置中,設定 displayflexflex-direction 設定爲 columnalign-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-height2rem,然後將字距縮小一點,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 的部份,加入 .cardon('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 除以 420transform-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.activetransformtranslateY() 裏面的 -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。

這是由於我們的卡片,通過 transformscale() 放大了,所以在計算圖片與標題高度的時候,要乘以放大比例。

定義一個變量名爲 ratio,賦值爲 480/420,分別將圖片與標題高度乘以 ratio,而最後,又要將總數除以這個 ratio,再測試一下:

今次高度就正確了。

再做最後的一些微調,捲動的時候會發現 APP OF THE DAY 的下方 padding 稍爲少了一點,在 .card.active h4 內加入 padding-bottom: .8rem

然後內文出現的時候加一些漸入效果,在 .content-wrapper 內加入透明度設定 opacity: .8transition: .3s all ease-out

再在展開的 .content-wrapper 內加入 opacity: 1transition: .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