延遲載入影片

圖片元素相同,您也可以延遲載入影片。影片通常會透過 <video> 元素載入 (雖然使用 <img> 的替代方法已經受到有限的實作影響)。不過,延遲載入 <video> 的「方式」會因用途而異。讓我們討論幾個情境,這些情境各自需要不同的解決方案。

非自動播放的影片

如果是使用者啟動播放 (也就是「不會」自動播放的影片),建議您在 <video> 元素上指定 preload 屬性

<video controls preload="none" poster="one-does-not-simply-placeholder.jpg">
  <source src="one-does-not-simply.webm" type="video/webm">
  <source src="one-does-not-simply.mp4" type="video/mp4">
</video>

上述範例使用值為 nonepreload 屬性,防止瀏覽器預先載入「任何」影片資料。poster 屬性會為 <video> 元素提供預留位置,在影片載入時佔用空間。這是因為載入影片的預設行為會因瀏覽器而異:

  • 在 Chrome 中,preload 的預設值為 auto,但自 Chrome 64 版起,現在預設為 metadata。即使在電腦版 Chrome 中,系統可能會使用 Content-Range 標頭預先載入影片的部分內容。其他以 Chromium 為基礎的瀏覽器和 Firefox 的運作方式,
  • 和電腦版 Chrome 一樣,Safari 11.0 電腦版會預先載入一段影片。而自 11.2 版起,系統只會預先載入影片中繼資料。在 iOS 上的 Safari 中,系統一律不會預先載入影片
  • 數據節省模式啟用時,preload 會預設為 none

由於與 preload 相關的瀏覽器預設行為並非無法變更,因此明確設定可能是最好的做法。在這種情況下,當使用者啟動播放,要延遲在所有平台上載入影片,使用 preload="none" 是最簡單的方法。preload 屬性不是延遲影片內容載入的唯一方法。透過影片預先載入功能快速播放相關文章,或許能帶給您一些想法和見解,協助您以 JavaScript 播放影片。

不過,使用影片取代動畫 GIF 時,這個方法並無法發揮效用,我們稍後會介紹。

將影片當做 GIF 替換動畫

儘管動畫 GIF 廣受大眾歡迎,但在許多方面,動畫 GIF 檔案在許多方面卻差不多,特別是在檔案大小方面。GIF 動畫可延伸至數 MB 的資料範圍畫質差不多的影片往往會比較小

使用 <video> 元素取代動畫 GIF 效果並不能直接當做 <img> 元素。動畫 GIF 具有三大特性:

  1. 影片載入後會自動播放。
  2. 它們會持續循環 (但不一定情況)。
  3. 沒有音軌。

透過 <video> 元素達成此目的,看起來會像這樣:

<video autoplay muted loop playsinline>
  <source src="one-does-not-simply.webm" type="video/webm">
  <source src="one-does-not-simply.mp4" type="video/mp4">
</video>

autoplaymutedloop 屬性應該很容易理解。playsinline在 iOS 裝置上自動播放影片。現在,您已擁有適用於各種平台且服務型 GIF 替代影片。但對於如何延遲載入嗎?首先,請根據以下方式修改 <video> 標記:

<video class="lazy" autoplay muted loop playsinline width="610" height="254" poster="one-does-not-simply.jpg">
  <source data-src="one-does-not-simply.webm" type="video/webm">
  <source data-src="one-does-not-simply.mp4" type="video/mp4">
</video>

您會發現新增了 poster 屬性,讓您指定在影片延遲載入之前佔用 <video> 元素空間的預留位置。和 <img> 延遲載入範例一樣,請將影片網址存放在每個 <source> 元素的 data-src 屬性中。接著,使用類似 Intersection Observer 圖片延遲載入範例的 JavaScript 程式碼:

document.addEventListener("DOMContentLoaded", function() {
  var lazyVideos = [].slice.call(document.querySelectorAll("video.lazy"));

  if ("IntersectionObserver" in window) {
    var lazyVideoObserver = new IntersectionObserver(function(entries, observer) {
      entries.forEach(function(video) {
        if (video.isIntersecting) {
          for (var source in video.target.children) {
            var videoSource = video.target.children[source];
            if (typeof videoSource.tagName === "string" && videoSource.tagName === "SOURCE") {
              videoSource.src = videoSource.dataset.src;
            }
          }

          video.target.load();
          video.target.classList.remove("lazy");
          lazyVideoObserver.unobserve(video.target);
        }
      });
    });

    lazyVideos.forEach(function(lazyVideo) {
      lazyVideoObserver.observe(lazyVideo);
    });
  }
});

延遲載入 <video> 元素時,您必須疊代所有子項 <source> 元素,並將其 data-src 屬性轉換為 src 屬性。完成後,您必須呼叫元素的 load 方法,觸發載入影片,之後系統根據 autoplay 屬性會自動開始播放媒體。

使用這個方法時,您要有一個可模擬 GIF 動畫行為的影片解決方案,但產生的數據用量不會與動畫 GIF 相同,而且可以延遲載入該內容。

延遲載入程式庫

下列程式庫可協助您延遲載入影片:

  • vanilla-lazyloadlozad.js 是只使用 Intersection Observer 的超輕量選項。因此,這類函式的效能都很出色,但需要以折線的形式填滿,才能在舊版瀏覽器中使用。
  • yall.js 是一個使用 Intersection Observer,且會改回使用事件處理常式的程式庫。也可以使用 data-poster 屬性延遲載入影片 poster 圖片。
  • 如果您需要 React 專屬的延遲載入程式庫,可以考慮使用React-lazyload。雖然不使用 Intersection Observer,但「確實」提供了一種熟悉的延遲載入映像檔方法,適合習慣使用 React 開發應用程式的人。

每個延遲載入程式庫都有妥善記錄,針對各種延遲載入等提供眾多標記模式。