DEV Community

Cover image for HTML meta 標籤中 viewport 的設定
codemee
codemee

Posted on

HTML meta 標籤中 viewport 的設定

設計網頁時, 都會加上以下這行 meta 標籤

<meta name="viewport" content="width=device-width, initial-scale=1.0">
Enter fullscreen mode Exit fullscreen mode

以便在手機螢幕上也能顯示可以輕鬆閱讀文字的網頁, 不過我自己一直沒有仔細研究這行在說什麼, 今天剛好花了點時間測試, 記錄下來。

要素一:以 devicePixelRatio 調整 px 大小

實際在顯示網頁時是以 CSS 像素 (px) 為基準, 這個 CSS 像素並不是實際硬體的像素, 兩者之間對應的關係就由 window.devicePixelRatio 來決定, 若這個比例是 2, 就表示在當前的裝置中, 會用 2×2 個像素來代表一個 px, 透過這個比例, 就可以讓同樣 px 數的字在不同尺寸的裝置上都能顯示合適的大小, 而不會顯示過小無法閱讀。以下是我在不同裝置或是不同顯示比例下得到的 devicePixelRatio 比例:

裝置 解析度 像素密度 devicePixelRatio 值
OPPA A31 720×1600 270PPI 2
Google Pixel 8a 1080×2400 430PPI 2.625(Chrome)
Google Pixel 8a 1080×2400 430PPI 2.6087(Firefox)
Windows 11 筆電 1920×1080 N/A 1
Windows 11 13.3 吋筆電 顯示比例 125% 1920×1200 N/A 1.25

你可以看到即使是相同的裝置上, 不同的瀏覽器也可能會有不同的比例, 顯示時就是以這個比例為基準, 表示一個 px 的大小。

要素二:設定 viewport 的大小

所謂的 viewport, 指的就是瀏覽器視窗內可以用來顯示網頁的區域, 這個大小一樣是使用步驟一得到的 px 為單位。在手機這類裝置上因為沒有視窗, 所以 viewport 是一個假想的虛擬視窗。

viewport 的設定中, 最重要的是 width(寬度), 可以設定為 1~10000, 它會影響網頁元素的排列、文字折行等等。在手機裝置上, 如果想把 viewport 設定為和螢幕一樣寬, 可以從裝置的實際像素寬度值除以 devicePixelRatio 得到以 px 為單位的 viewport 寬度值;或者直接設定為 device-width 由系統幫你計算, 讓網頁的寬度與裝置的螢幕寬度一致。如果沒有設定 viewport, 預設值為 980。

在 JavaScript 中, 你可以透過以下方式取得螢幕與 viewport 以 px 為單位的寬度:

屬性 說明
window.innerWidth viewport 的寬度
window.screen.width 裝置的螢幕寬度

要素三:縮放比例

檢視頁面時, 使用者可以縮放, 在 viewport 設定中的 initial-scale 就是設定首次載入頁面後的縮放比例 (0.1~10.0)。如果沒有設定, 瀏覽器預設會自動縮放到能夠顯示頁面橫向完整內容的最大比例。

前面提過, 沒有設定 viewport 時預設寬度會是 980px, 以剛剛看到的 Google Pixel 8A 的 Firefox 為例, 螢幕寬度是 1080/2.6087 = 414px, 瀏覽器必須將網頁縮到 414/980=42.2% 才能完整顯示網頁橫向的內容, 導致字太小無法閱讀。

如果需要, 也可以在 viewport 中設定 minimum-scale 限制使用者可以縮放的最小倍數, 預設為 0.1。如果完整顯示網頁橫向內容的最大縮放倍數比 minimum-scale 設定的倍數大, 就會取代 minimum-scale 的設定, 也就是最小只能縮到可以顯示網頁橫向內容為止。你也可以設定 maximum-scale 限制最大倍數, 預設為 10。或者也可以進一步透過設定 user-scalable 設定 1/0 或是 yes/no 限制使用者可不可以縮放。

在 JavaScript 中可以如下方式取得目前頁面的縮放倍數:

屬性 說明
window.visualViewport.scale viewport 目前的縮放倍數

實測

以下我們就以底下的網頁實際測試:

<html>
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <style>
    /* 修改樣式設定 */
    .s {
      margin-bottom: 2px;
      border-radius: 3px;
      color: white;
      font-weight: bold;
      display: inline-block;
      width: 100px;
      height: 50px;
      text-align: center;
      line-height: 50px;
    }
    .s1 {
      background-color: #2ecc71;
    }
    .s2 {
      background-color: hsl(78, 63%, 49%);
    }

    /* 新增數值顯示區的樣式 */
    .info-container {
      margin: 20px;
      padding: 15px;
      background-color: #f8f9fa;
      border-radius: 8px;
      box-shadow: 0 2px 4px rgba(0,0,0,0.1);
    }

    .info-item {
      margin: 10px 0;
      padding: 8px;
      border-left: 4px solid #2ecc71;
      background-color: white;
    }

    .info-label {
      color: #666;
      font-weight: bold;
      margin-right: 10px;
    }

    .info-value {
      display: inline-block;
      background-color: #2ecc71;
      color: white;
      padding: 3px 8px;
      border-radius: 4px;
      min-width: 50px;
      text-align: center;
    }
  </style>
  <script>
    window.addEventListener('load', function() {
      document.getElementById('devicePixelRatio').textContent = window.devicePixelRatio;
      document.getElementById('screenWidth').textContent = window.screen.width;
      document.getElementById('innerWidth').textContent = window.innerWidth;

      // 取得並顯示目前的縮放倍數
      function updateScale() {
        const currentScale = window.visualViewport ? window.visualViewport.scale : '不支援';
        document.getElementById('currentScale').textContent = currentScale;
      }

      // 初始化顯示
      updateScale();

      // 監聽縮放變化
      if (window.visualViewport) {
        window.visualViewport.addEventListener('resize', updateScale);
      }
    });
  </script>
</head>
<body>
  <span class="s s1">1</span>
  <span class="s s2">2</span>
  <span class="s s1">3</span>
  <span class="s s2">4</span>
  <span class="s s1">5</span>
  <span class="s s2">6</span>
  <span class="s s1">7</span>
  <span class="s s2">8</span>

  <div class="info-container">
    <div class="info-item">
      <span class="info-label">window.devicePixelRatio = </span>
      <span class="info-value" id="devicePixelRatio"></span>
    </div>
    <div class="info-item">
      <span class="info-label">window.screen.width = </span>
      <span class="info-value" id="screenWidth"></span>
    </div>
    <div class="info-item">
      <span class="info-label">window.innerWidth = </span>
      <span class="info-value" id="innerWidth"></span>
    </div>
    <div class="info-item">
      <span class="info-label">目前縮放倍數 = </span>
      <span class="info-value" id="currentScale"></span>
    </div>
  </div>

  <!-- <img src="img/meebox_square.png" /> -->
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

電腦上的 Firefox

在電腦上的 Firefox 顯示如下:

Image description

你可以看到 viewport 的寬度就是目前瀏覽器視窗的寬度 646px, 即使把 viewport 設定拿掉, 顯示結果也不會變化。如果刻意把 viewport 的寬度設定的比視窗寬, 例如:

  <meta name="viewport" content="width=1200, initial-scale="1.0">
Enter fullscreen mode Exit fullscreen mode

也不會影響 viewport 的實際值, 也就是說, 對於一般電腦上的瀏覽器來說, 設不設定 viewport 並沒有差別。

手機上的 Firefox

如果沒有設定 viewport, 把剛剛 HTML 內容的 viewport 設定變成註解:

  <!--   <meta name="viewport" content="width=device-width, initial-scale=1.0"> -->
Enter fullscreen mode Exit fullscreen mode

在手機上的 Firefox 顯示如下:

Image description

把縮小顯示的部分放大, 會看到:

Image description

由於預設的 viewport 寬度是 980, 為了能夠完整顯示網頁橫向內容, 所以自動縮小到 0.4224 倍, 以便顯示網頁的橫向內容。此倍數比 minimum-scale 的預設值 0.1 大, 會取代 minimum-scale 的設定, 使用者即使自行縮小顯示最多也就只能縮小到 0.4224 倍。

設定 viewport 與螢幕等寬

如果把 viewport 的設定加回來:

  <meta name="viewport" content="width=device-width, initial-scale=1.0">
Enter fullscreen mode Exit fullscreen mode

看到的畫面就會是這樣:

Image description

你可以看到現在 viewport 的寬度 (window.innerWidth) 和裝置螢幕的寬度 (window.screen.width) 是一樣的, 都是 414px, 會以此寬度顯示網頁, 並且縮放倍數是 1, 可以清楚閱讀顯示的網頁內容。由於這是可以顯示網頁橫向內容的最大縮放倍數, 也會取代 minimum-scale 預設的 0.1, 使用者自行縮放頁面最小只能縮到 1 倍。

如果保留預設縮放倍數為 1, 但是不設定 viewport 寬度, 像是這樣:

  <meta name="viewport" content="initial-scale=1.0">
Enter fullscreen mode Exit fullscreen mode

實際結果就跟設定寬度為 device-width 一樣。

刻意設定 viewport 寬度

如果刻意設定 viewport 寬度為 980:

  <meta name="viewport" content="width=980, initial-scale=1.0">
Enter fullscreen mode Exit fullscreen mode

就會如下顯示:

Image description

由於現在 viewport 的寬度比螢幕寬, 所以在排列時就會延伸到螢幕外的範圍, 你也可以從實際顯示的結果看到螢幕寬度的確是 980。

如果刻意將 viewport 寬度設成比螢幕窄, 像是:

<meta name="viewport" content="width=200, initial-scale=1.0">
Enter fullscreen mode Exit fullscreen mode

瀏覽器會以螢幕寬度為最低的 viewport 寬度, 所以顯示結果會和設定寬度為 device-width 一樣:

Image description

如果只有設定 viewport 寬度, 沒有設定 initial-scale, 像是這樣:

  <meta name="viewport" content="width=device-width">
Enter fullscreen mode Exit fullscreen mode

仍會採用 1.0 為啟始的縮放倍數。

設定大於 1 的啟始倍數

如果更改縮放倍數, 就可以在網頁首次載入後就採用指定的縮放倍數, 例如:

  <meta name="viewport" content="width=device-width, initial-scale=3.0">
Enter fullscreen mode Exit fullscreen mode

就會看到放大 3 倍的結果:

Image description

請注意 initial-scale 只對首次載入網頁有效, 即使你修改設定重新載入網頁, 如果原本該網頁的縮放倍數合乎新設定的縮放範圍內, 就會維持原本的縮放倍數。因此建議開啟新的隱私頁面測試會比較準, 否則可能會發生修改 initial-scale 卻不會改變顯示比例的狀況。

如果你就是要強制使用者以放大倍數觀看網頁, 可以設定 minimum-scale, 不過這應該是從一開始設計時就把網頁內容放大才比較正確。

設定小於 1 的啟始倍數

initial-scale 也可以設為小於 1, 也就是縮小顯示, 但若是 viewport 寬度照比例縮小會比螢幕寬度小, 就會違反最小只能縮小到可以顯示網頁完整橫向內容的規則, 瀏覽器就會自動將目前設定的 viewport 寬度除以縮小倍數, 讓網頁可以保持縮到最小倍數時可以呈現完整橫向的內容。例如, 若是設定為 0.5:

  <meta name="viewport" content="width=device-width , initial-scale=0.5 ">
Enter fullscreen mode Exit fullscreen mode

就會把 viewport 的寬度變成 414/0.5=828px:

Image description

放大看詳細的數據:

Image description

如果一開始把 viewport 寬度設得夠寬, 就會保持 meta 標籤中的設定, 例如:

  <meta name="viewport" content="width=900 , initial-scale=0.5 ">
Enter fullscreen mode Exit fullscreen mode

結果如下:

Image description

你可以看到寬度不變:

Image description

圖片的顯示

如果你在網頁中放入圖片, 這時候圖片的解析度會以 px 為單位解譯, 所以一張 200×200 的圖片, 在 devicePixelRatio 為 2 的裝置上, 就會以 400×400 個實體像素來顯示。例如在剛剛的網頁最後面我們加上了一張圖片:

  <img src="img/meebox_square.png" />
Enter fullscreen mode Exit fullscreen mode

這是一張 584×604 大小的圖片:

Image description

網頁顯示結果如下:

Image description

你會看到由於圖片比較寬, 所以超過了螢幕邊界, 但是整體的頁面仍然是以 viewport 設定的寬度來編排, 因此 4 號方塊被擠到第二列。這種情況下, 使用者可以縮小的倍數可以比 initial-scale 設定的 1.0 小, 最小可到能夠完整顯示圖片的寬度為止, 像是這樣:

Image description

上圖中就縮小到了 0.749 倍。

如果刻意把 viewport 寬度設成與圖片同寬:

Image description

你可以看到跟剛剛的結果不同, 現在上方的 4,5 兩個方塊都排到第一列了, 這是因為 viewport 的設定變寬了。

SurveyJS custom survey software

JavaScript UI Libraries for Surveys and Forms

SurveyJS lets you build a JSON-based form management system that integrates with any backend, giving you full control over your data and no user limits. Includes support for custom question types, skip logic, integrated CCS editor, PDF export, real-time analytics & more.

Learn more

Top comments (0)

Retry later
Retry later