跳到內容
關於我 數位花園

手機

2 篇文章擁有標籤:“手機”

行動裝置上 100vh 的奇怪行爲

最近遇到一個手機瀏覽器上,在某個特定排版會發生高度有誤的問題

進而發現手機瀏覽器的特殊行爲

查了一下資料,發現這個問題已經存在許久,也有不少的解法

大致分為用 JS 及 CSS 去解決

CSS 的解決方式比較簡單明瞭

想要直接看結果,可以走傳送門:JS 解決方案CSS 解決方案

這個頁面是滿版的,所以 container 的部分佔滿整個 document.body

container 裡面,由上而下包含三個區域:header、body、footer

header 與 footer 分別固定在 container 上緣與下緣,body 撐滿剩下的空間,如果内容超過就會有捲軸滾動

layout 的示意圖如下:

layout

外層的 container 寬高分別是 100vw、100vh,在電腦上看起來沒什麼問題,然而在手機上就不是那麼回事:

100vh-issue-1

可以看到 container 高度是 100vh,卻仍然可以 scroll 的現象,照理來說應該要剛好撐滿才是

備註:可以在手機上點開連結,看一下真實情況


因此,若將 container 裡面的東西加進來,就會造成內外都有 scroll 的情況:

100vh-issue-2


看來 CSS 所抓到的 100vh,跟實際上的 window 高度不一樣,window 比較小,所以造成了 scroll

於是,試著把 container 元素的高度與 window 的高度印出來

var containerElement = document.getElementById("container");
var containerHeight = containerElement.clientHeight;
var heightElement = document.getElementById("container-height");
heightElement.innerText = "container clientHeight: " + containerHeight + "px";
var windowHeightEl = document.getElementById("window-height");
windowHeightEl.innerHTML = "window innerHeight: " + window.innerHeight + "px";

在電腦瀏覽器上,這兩個數字會是一樣的,在手機上卻不同:

different-height

查了一下網路上的資料,大概有兩種方式可以解

一個是用 JS,一個是用 CSS

於是我加上一個 resize 事件,監聽 window 的高度變化,並顯示在畫面上:

var containerElement = document.getElementById("container");
var containerHeight = containerElement.clientHeight;
var heightElement = document.getElementById("container-height");
heightElement.innerText = "container clientHeight: " + containerHeight + "px";
function updateWindowHeight() {
var windowHeightEl = document.getElementById("window-height");
windowHeightEl.innerHTML = "window innerHeight: " + window.innerHeight + "px";
}
updateWindowHeight();
window.addEventListener("resize", updateWindowHeight);

在 iOS Chrome 上,可以看出兩者的高度並不一致:

100vh-issue-3-ios-chrome

iOS 的 Safari,除了 container 與 window 高度不同之外,還發現了另一個現象

100vh-issue-3

在最頂的時候往下滑動,window 的高度是動態的

mind-explosion

在 Android Chrome 上,也出現 window 變化的狀況,但還是有點差異

100vh-issue-3-android-chrome

原來是彩蛋啊!!🥚

既然 100vh 與 window 的高度會不一致,而且在 iOS Safari 與 Android Chrome 上還會不斷改變, 那麼就在監聽 windowresize 事件時, 將 container 元素的高度複寫成當下的 window 高度就行了:

(function () {
function updateWindowHeight() {
var currentWindowHeight = window.innerHeight;
var windowHeightEl = document.getElementById("window-height");
windowHeightEl.innerHTML = "window innerHeight: " + currentWindowHeight + "px";
var containerElement = document.getElementById("container");
containerElement.style.height = currentWindowHeight + "px";
var containerHeight = containerElement.clientHeight;
var heightElement = document.getElementById("container-height");
heightElement.innerText = "container clientHeight: " + containerHeight + "px";
}
updateWindowHeight();
window.addEventListener("resize", updateWindowHeight);
})();

100vh-issue-4

我們可以看到,container 高度與 window 高度,始終保持同步,也就避免出現外層 scroll 的情況發生了

100vh-issue-rotate

旋轉螢幕也不是問題~

既然 container 的高度解決了,再將 container 裡面的 header、body、footer 加進來:

100vh-issue-final

一切看來都非常美好 😎

至於 resize 事件是否要加入 debounce 增進效能,那就看個人選擇了

CSS 的方法有分幾種:

.container {
display: flex;
flex-direction: column;
width: 100vw;
height: 100vh;
max-height: -webkit-fill-available; // 加上 fill-available
background: lavender;
}

但如同前綴 -webkit- 的字面意義,在 Android Chrome 上仍然有問題

但 Windows 及 macOS 上的 Chrome 卻沒事

ChromeSafari
macOSOO
WindowsONA
iOSOO
AndroidXNA

將 html 與 body 的高度設為 100%,底下的 container 就放心地用 100%

完全將在 mobile 上有爭議的 vh 單位移除了,是一個簡潔的方式

html,
body {
margin: 0;
padding: 0;
height: 100%; // 用 100%
width: 100%;
}
.container {
display: flex;
flex-direction: column;
width: 100%;
height: 100%; // 這裡也用 100%
background: lavender;
}

所有平台及裝置的瀏覽器都通過了考驗 💯

ChromeSafari
macOSOO
WindowsONA
iOSOO
AndroidONA

有一個人分享了利用新 CSS 單位的解法,也是一個不錯的選擇:

.container {
display: flex;
flex-direction: column;
width: 100%;
height: 100dvh; // 用單位 dvh
background: lavender;
}

關於 dvh 的示意圖如下:

new unit

PS. 本圖來自 Google IO

但這個似乎還很新,看來支援度沒有太好

簡單測試了一下,在 Safari 可以正常運作,但是 Chrome 卻不認識這個單位

ChromeSafari
macOSXO
WindowsXNA
iOSOO
AndroidXNA

支援度可以參考:“dvh” | Can I use… Support tables for HTML5, CSS3, etc

之後想要來研究一下關於 dvh 這一類給 mobile 專用的單位

ChromeSafari
MacOSOO
WindowsONA
iOSXX
AndroidXNA
  • O:代表 100vh 與 window 高度一致
  • X:代表 100vh 與 window 高度不同
  • NA:此平台無此瀏覽器
ChromeSafari
iOSXO
AndroidONA
  • O:代表 window 高度會隨 scroll 變化
  • X:代表 window 高度固定
  • NA:此平台無此瀏覽器
OSChromeSafari
iOS15.15103.0.5060.6315.15
Android11103.0.5060.71NA

這次因為使用了滿版 layout 搭配 100vh,而發現了這個水很深的問題

iOS Safari 在往下滑動時,window 會不斷改變的現象,確實是大開眼界

總結下來,JS 的解法需要去監聽 resize 事件, 此外大概不可避免要搭配 debounce,會需要寫很多的程式碼, 還需要在適當的時機取消訂閱 resize 事件,避免記憶體洩漏問題, 簡單來說為了效能及資源,都要額外再做其他事

CSS 的三種解法都相對簡單,沒有太多的程式碼

但唯有第二個解法(html、body 設為 100%)通過了所有瀏覽器的考驗

第三個解法(新單位 dvh)雖然現在有相容性問題,但未來的可用性令人期待

[開箱] Nomad 無線充電盤與皮革錶帶

觀望 Nomad 這個品牌有一陣子了,很喜歡他們低調沉穩的色調及產品設計。尤其是對無線充電盤 Base Station 系列與 Apple Watch 皮革錶帶特別有興趣

考慮買無線充電盤卻遲遲沒有入手,是因爲手邊支援無線充電的裝置,只有 iPhone 與 AirPods Pro,因此覺得沒有改成無線充電的絕對必要性。直到前陣子入手了 Apple Watch 之後,就有購買花錢的理由,三個裝置都支援了無線充電,實現了所謂的三位一體方便且可同時為所有裝置充電的方案

Nomad 的無線充電盤大概分爲以下幾種:Base Station Pro、Base Station、Base Station Stand、Base Station Mini

Base Station Pro 要價 US$199.95(約 NT$5,516),是尊爵不凡的最高級款。最大特色就是「全表面充電」(Full-Surface Charging),有別於一般的無線充電必須對準線圈的中心才能充電的小缺點,它的黑科技(官方稱為 FreePower 演算法)可以自動判斷手機的位置並進行充電,最多可支援三個裝置隨手丟在充電盤上充電,某方面是實現了蘋果公司當時沒有上市的 AirPower 無線充電盤

Base Station 就是一般的無線充電盤,擁有三個充電線圈,最多同時為兩個裝置充電。有三種版本:Hub 版、Watch 版、Watch Mount 版。後面兩者的差異在於後者只有手錶架,必須加裝 Apple Watch 的磁吸充電器才行;前者則有内附 MFI 原廠認證的磁吸充電器

此款是直立式無線充電盤,價格 US$99.95(約 NT$2,756),只能為一支 iPhone 充電。此外,直立式的充電盤不在我的考慮範圍内,所以這裡就不做太多介紹了

同樣也是單一裝置的無線充電盤,可以算是 Nomad 入門無線充電盤了,價格 US$69.95(約 NT$1,929)。體積小巧,如果沒有太多充電設備的話,這款也滿不錯的選擇。也因爲功率較低,所以只需要 20W 的變壓器就可以驅動。擁有磁吸功能,只要是 iPhone 12 以後的機種,放上去就可以輕鬆地對應到可以充電的位置

美國官網的整新品:價格很香,但看得到買不到

Section titled “美國官網的整新品:價格很香,但看得到買不到”

去年(2021 年)11 月錯過了黑色星期五(Black Friday)官網的大折扣活動。想説聖誕節應該還有優惠活動吧?但什麽都沒有……

某天看到了官網的 Outlet Sales 上架了 Base Station 的整新機,價格最多有到原價的 5 折!當下看了很心動,很開心地加入購物車,要填寫國際寄送的時候就卡住了……上面顯示無法運送至該地址。我試了一下一般的產品是可以運送到台灣的。於是推測應該是整新機不給送海外。想花錢卻買不到啊~

直到聖誕節的隔日,12/26 看到全站 8 折的活動:Treat Your Self ⋯⋯ 😭

嗯?你說這表情?這叫做內牛 ⋯⋯ 啊不是,是淚流滿面

原本有考慮 Base Station Apple Watch Edition,但因為已經有原廠附的磁吸充電器了,若買了 Apple Watch Edition,充電器就會晾在一旁,覺得這樣有點浪費。所以最後買的是磁吸版本的 Base Station Hub Station | Magnetic 搭配 Apple Watch Charging Mount

base station box

因為最新的磁吸版本目前只有 Hub 版,所以要加手錶架,再裝上去:

watch mount box

其實是用 3M 膠黏上去的,有安裝說明書、適應各種尺寸手錶的橡膠墊片:

watch mount content

充電盤後面還有兩個充電孔,依序是 USB-A、USB-C、電源孔、光源感應器:

base station back

另外還有附兩個國際插頭可以替換,一個歐規(右)、一個英規(左):

base station plug

Nomad 目前的錶帶材質,分成皮革、橡膠與金屬(鈦金屬與不鏽鋼)

但說到 Nomad,第一個聯想到的材質當然是皮革啦

以往都習慣配戴橡膠的錶帶,基於防水及耐用性的考量,但一直有想要嘗試皮革錶帶的想法

Nomad 的 Modern BandTraditional Band 皮革錶帶看起來都不錯,後來選擇了 Modern Band,我選的是棕色、黑色錶扣:

leather band

發現皮革材質不太防水,洗手的時候難免會有噴濺到皮革的表面,皮革就會有很明顯的痕跡,乾燥之後,痕跡才會慢慢消失,對於用習慣橡膠錶帶,洗手時有時候會順便沖洗手錶的我來說,算是有點小不習慣。如果要防水又想要皮革錶帶的話,就要選擇 Active Band Pro 皮革橡膠複合材質的錶帶了,但是就少了皮革的味道

另外,皮革錶帶是用幾次後,就會有明顯的皺紋,尤其是內側:

leather band front

leather band back

防水的部分,目前簡單用皮革保養油做簡單防水,未來考慮用防水噴霧做防水塗層,彌補原本不防水的皮革表面

Nomad 錶帶用的皮革就是 Horween 的皮革,對皮革沒什麼太多研究的我,不禁好奇:Horween 皮革又是何方神聖?

查了一下別人的介紹文章,發現這家發跡於美國芝加哥的皮革公司,他們生產的皮革被用在很多知名品牌上,例如:Alden、Timberland、Cole Haan、Carmina、Allen Edmonds、Chippewa⋯⋯ 等,無論是平價或是昂貴的品牌,聽起來就滿厲害。(雖然我只聽過 Timberland 🙈)此外,美國三大運動聯盟:NFL(美式足球)、NBA(籃球)、MLB(棒球)都使用 Horween 皮革製作比賽用具,像是 Wilson 的美式足球、Spalding 的籃球、Rawlings 的棒球手套

用手機多年來都是使用有線的方式充電,不但穩定快速、發熱量低也對電池比較的壽命。但如果很多裝置每天都要頻繁地充電,充電的便利性就相對重要,因此才決定改用無線的方式。至於電池壽命嘛 ⋯⋯ 我用手機平均是 2 ~ 3 年,而且從未換過電池(全部有線充電的情況)就換新手機了,若真的感受到電池嚴重老化,就送原廠換一顆新電池吧!畢竟為了極度保護電池健康而無法隨心所欲地充電,對我而言只是本末倒置罷了,花錢買來的東西就是要開心地使用,為生活帶來價值,而不是用得提心吊膽,各位同學認為呢?