[CSS3] 深入 @viewport 與 @media queries

說在最前面,這是要詳細交代這兩件事情的關係,不過由於目前 CSS Device Adaptation 還在草稿(W3C Working Draft)階段,所以以後或許還是會有變化。

這是很悶的詳細解釋文,所以理論上可以跳過。

@viewport and @media

還是一個重點,

@media depends on @viewport size.

但是由於 @viewport 這件事情,雖然他有許多設定可以用,但是預設值是相當討厭的。以目前的規範來看,

<viewport-length> = auto | device-width | device-height | <length> | <percentage>

這是 viewport-length 所預設會取用的數值。所以,後面那個 lengthpercentage 根本是寫心酸的,因為你怎麼設定都會無效。這一點對於官方的說法是,只要是在桌機的環境底下,預設值永遠都會是 auto

換句話說就是,除非使用的是其他行動裝置,他就是會取用你的 auto, device-width 或是 device-height,而 @media 也會跟著走,所以這一點在想要利用 <meta> 標籤來作弊的我來說,相當不方便(喂

當然,這種使用法也就沒有什麼效果了,雖然 W3C 他允許你這樣寫,

@viewport {
    width: 500px;
}

@media screen and ( max-width: 480px ) {

    @viewport {
        width: 800px;
    }

}

不要問我這樣寫能幹嘛,我只能說這可以用來作弊而已。另外在對於 <meta> 標籤上面的設定,其實也有一些規範,

  • 寬、高度預設單位是 px,範圍從 1px ~ 10000px
  • 不能為負值
  • device-width, device-height 為可用關鍵字
  • 其他非合法數值都會轉成 1px

但是呢,神奇的事情來了,

固定 @viewport 這件事在手機上可以用!

不過,目前以 Chrome 23/iOS 6 Safari Mobile 使用 CSSOM 的規範來取得 CSSRule 的 MediaList Interface 是拿不到的,換句話說,@viewport 目前在桌機或是行動裝置,也無法寫在 <style> 或是外部樣式表中,所以要使用 JS 來操作 @viewport 這件事情,以現行的狀況看來,就得等瀏覽器先去實作才可以了。

@viewport in action

雖然說桌機沒有辦法做實驗,不過行動裝置上卻可以做一些簡易的測試,我以下面這個簡單的靜態頁面來做一次給大家看看,

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=320">
        <style id="styles">
            html {
                background: #ff6633;
                margin: 0;
                padding: 0;
            }
            body {
                background: #669933;
                margin: 0;
                padding: 0;
            }
            @media screen and (max-width: 480px) {
                body {
                    background: #336699;
                }
            }
            @media screen and (min-width: 1200px) {
                body {
                    background: #ff6644;
                }
            }
        </style>
    </head>
    <body>
        <h1>It's work!</h1>
        <p id="log"></p>
        <script src="./zepto.min.js"></script>
        <script>
            $(document).ready(function() {
                var log = $('#log'), content;

                content = "Currect @viewport: 320px<br>";
                content += "Window width: "+$(window).width()+"px<br>";
                content += "Document width: "+$(document).width()+"px";

                log.html(content);
            })
        </script>
    </body>
</html>

其中我使用了 zepto 這一套 JS Framework,你要換成 jQuery 也是可以的,接下來我們看圖說故事(以下全程使用 iPad/iOS6/Safari Mobile 當作測試環境

    <meta name="viewport" content="width=320">

viewport width=320

首先我們把 @viewport 固定為 320px,在螢幕不縮放的情況下,文件與視窗寬度相等。藍色的部份是 body 的區域,底下橘色的部份是 html 的區域(以下範例 html 區塊一律會是橘色

viewport width=320, zoom

在我們使用雙指縮放的情況下,就發生了這樣的事情,

  • 文件尺寸依舊為 320px
  • 視窗尺寸因為縮放的關係,變為 225px

這個時候 @viewport 產生了什麼變化?

  • initial viewport 因為縮放的關係所以重新設定了裝置的寬度
  • actual viewport 表示:干我屁事

實際上我們是無法得知 @viewport 產生了什麼改變,所以我們實際的使用 javascript 去偵測的時候,就會發現有些東西偷偷被改掉了,這裡跟桌機上面的瀏覽器有些微的不同。由於沒有特別定義文件尺寸,所以在桌機的瀏覽器上面,

document width == window width

但是,由行動裝置上的反應,我們可以猜想一件事情,

縮放的功能,對應於桌機瀏覽器的反應,也或許是改變視窗的大小。但是,唯一不同的地方是,雖然視窗的大小被改變了,但是文件的尺寸還是會依照 @viewport 的設定去走,並不會受到影響。

接著我們來看看 initial-scale 的設定,

    <meta name="viewport" content="width=320, initial-scale=1.0">

viewport width=320, initial-scale

由於 initial-scale 設定的關係,所以你會發現我們寬度的設定失效了。是的,initial-scale 這項設定會複寫尺寸這件事情,完全依照你手上的裝置來初始化你的畫面尺寸,

initial-scale == device- prefix with width and height

那如果使用縮放功能呢?

viewport width=320, initial-scale, zoom

很抱歉的,你的視窗尺寸依舊會變動,文件尺寸則不會受到影響。所以,這也是為什麼許多的 <meta> 設定會是這樣的原因之一,

    <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, user-scalable=no">

好處是什麼?

  • 取消縮放,讓視窗與文件尺寸,與桌機瀏覽器相同!
  • 避開 javascript 抓取尺寸的問題(萬一你有用 window 運算定位,尺寸跑掉就哭哭了
  • 減少畫面元件使用上的不良經驗(像是按鈕按不到這種問題

當你在製作行動裝置畫面時,你知道他是這麼的,所以自然而然就必須去面對使用者操作上的問題。畢竟,行動裝置設計不是把元素放大縮小,或是拆開重新排列就能解決的事情。

那,這樣說來 <meta> 這樣設定似乎也沒什麼問題。

BUT!

以下為各位帶來一連串的視頻魔術靈異事件,

Magic

Magic

Magic

Magic

Magic

Magic

有以上症頭,請卡免付費電話,控八控控,請先檢查你的 css 是不是有使用以下的東西,

  • position 定位,尤其是絕對定位(absolute
  • margin, paddingwidth: 100%;
  • overflow 該要 hidden 的地方忘記 hidden
  • 文本斷行(breakflow)定位問題

javascript 請檢查,

  • javascript 有使用 document 或是 window 尺寸進行演算
  • 是否有更動畫面元素的樣式

How to test @viewport and @media queries

首先,

請不要相信自己寫好的 CSS!

當裝置告訴你、瀏覽器告訴你,他的文件尺寸寬度是 480px 的時候,而你所寫的 Media Queries 卻無法正確生效的時候,

留下寫好的 Media Queries,剔除其他的,再測試一次!

我遇過許多靈異事件,不過大多數都能藉由觀落陰原始碼中找到一些端倪,而有些則是需要測試才知道為什麼,例如,

    <meta name="viewport" content="target-densityDpi=120dpi, initial-scale=1.0, user-scalable=no">

看起來是相當良好的設定,但是,iOS 與 Android 卻兩樣情,為什麼?這個我在上一篇已經有解釋過了,所以我就不在這邊囉唆了(笑

如果你真的需要很方便的測試 @viewport 的話,或許 Chrome 的 Dev tool 是個不錯的選擇,

Chrome Dev Tool

如果你真的要實際測試關於 @viewport@media 這件事情,那麼或許買台 iPad 也是不錯(喂,在瀏覽器上雖然方便,但是你可能測不出實際裝置上面的狀況。

目前支援的程度看來,大抵上只有 Opera 跟 IE10 可以使用 @viewport 這樣的樣式設定,不過還是需要加上前輟(@-o-viewport, @-ms-viewport)來達到對瀏覽器的支援。更多詳細的 @viewport 支援細則,可以看這裡,

Browser compatibility - viewports

結語

相信自己的眼睛,不要太相信自己的 Code

樣式除錯挺困難的其實。

Hina Chen
偏執與強迫症的患者,算不上是無可救藥,只是我已經遇上我的良醫了。
Taipei