網頁功能程式碼研究室

1. 介紹與設定

網頁功能

我們想消除網站與原生網站之間的功能差距,讓開發人員能在開放網路環境中輕鬆打造優質體驗。Google 堅信每一位開發人員都應該具備必要能力,能打造優質的網路體驗,因此我們致力打造更優質的網路環境。

不過,有些功能可供原生使用,例如檔案系統存取和閒置偵測,但這類功能無法在網路上使用。這些缺少的功能包括無法在網路上提供某些類型的應用程式,或根本不實用。

我們會參考現有的開放網路平台標準程序,以公開透明的方式設計及開發這些新功能,同時盡量從開發人員和其他瀏覽器供應商取得初步意見回饋,確保設計可互通。

建構項目

在本程式碼研究室中,您將使用幾種全新的網路 API,或只有標記才能使用。因此,本程式碼研究室著重於介紹 API 本身和這些 API 可用的用途,而不是建構特定的最終產品。

課程內容

本程式碼研究室將說明多種邊緣 API 的基本機制。請注意,這些機制尚未以石頭設計,我們非常感謝您對於開發人員流程的寶貴意見。

軟硬體需求

由於本程式碼研究室提供的 API 真正屬於最新技術,因此各 API 的要求不盡相同。請務必仔細閱讀各部分開頭的相容性資訊。

如何使用程式碼研究室

程式碼研究室不一定需要依序執行。每個部分都代表獨立的 API,歡迎您自行選擇最感興趣的項目。

2. 徽章 API

Badging API 的目標是為使用者提供並集中註意力在背景執行為了讓本程式碼研究室中的示範簡單明瞭,我們要利用這個 API注意前景發生的事。你可以將心理轉移到背景中出現的事物。

安裝 Airhorner

這個 API 必須安裝在主畫面上的 PWA,因此第一步是安裝 PWA,例如全球知名的 airhorner.com。輕觸右上角的「安裝」按鈕,或使用三點圖示選單手動安裝。

8b7fa8b3c94c6bdd.png

系統會顯示確認提示,請點按「安裝」

98e90422167ac786.png

現在作業系統的 Dock 會顯示新的圖示。按一下該按鈕即可啟動 PWA。該應用程式有專屬的應用程式視窗,並以獨立模式執行。

設定徽章

安裝 PWA 後,您需要在徽章上顯示一些數值資料 (徽章只能包含數字)。空氣 Horner 的計算重點之一,就是「嘆氣」,指著該項目的次數。事實上,已安裝的 Airhorner 應用程式助你調整角,檢查徽章。早上起床就計算一次

b5e39de7a1775054.png

那麼,這項功能如何運作呢?基本上,程式碼如下:

let hornCounter = 0;
const horn = document.querySelector('.horn');
horn.addEventListener('click', () => {
  navigator.setExperimentalAppBadge(++hornCounter);
});

對空氣發出多次音效,然後檢查 PWA 的圖示:每次更新都會更新。單曲。讓應用程式從可以最快做出回應的位置 回應使用者要求空氣聲的聲音就是這麼簡單!

eed10c3ffe59999.png

清除徽章

計數器會達 99 度,然後再重新開始計算。你也可以手動重設。開啟「開發人員工具」分頁,貼上以下這一行,然後按下 Enter 鍵。

navigator.setExperimentalAppBadge(0);

或者,您也可以直接清除徽章,如以下程式碼片段所示。您的 PWA 圖示現在應美觀呈現在開頭、清晰可見,且不會顯示標記。

navigator.clearExperimentalAppBadge();

33eafb314a3a9f30.png

意見回饋

您對這個 API 有何想法?請填寫這份問卷,協助我們提升服務品質:

這個 API 是否直覺好用?

你有執行這個範例嗎?

還有其他想法嗎?缺少功能嗎?請在這份問卷調查中快速提供您的意見。感謝您!

3. 原生檔案系統 API

開發人員可使用 Native File System API,建構強大的網頁應用程式,與使用者本機裝置上的檔案互動。使用者授予網頁應用程式存取權後,這個 API 可讓網頁應用程式直接讀取或儲存使用者裝置上的檔案和資料夾變更。

讀取檔案

「Hello, world」就是讀取本機檔案,並取得檔案內容。建立一般的 .txt 檔案並輸入一些文字。接著,前往任何安全網站 (也就是透過 HTTPS 提供的網站,例如 example.com),然後開啟開發人員工具控制台。將下方的程式碼片段貼到控制台。因為 Native File System API 需要使用者做出手勢,所以我們在文件中附加了按兩下處理常式。稍後需要檔案控制代碼,所以只要將其設為全域變數即可。

document.ondblclick = async () => {
  window.handle = await window.chooseFileSystemEntries();
  const file = await handle.getFile();
  document.body.textContent = await file.text();
};

c02679081eb4d538.png

接著在「example.com」example.com頁面的任一處按兩下,即可開啟檔案選擇器。

d98962600d62d149.png

選取您先前建立的 .txt 檔案。然後檔案內容會取代 example.com 的實際 body 內容。

eace3d15bd4f8192.png

儲存檔案

接著要做些變更因此,我們要body貼上以下程式碼片段,讓您可以編輯。現在,您可以像編輯文字編輯器一樣編輯文字。

document.body.contentEditable = true;

ca32797417449343.png

現在,我們要將這些變更寫回原始檔案。因此,我們需要檔案控制代碼的寫入者,只要在控制台中貼上下方程式碼片段,即可取得檔案控制代碼。我們同樣需要使用者做出手勢,因此目前需要等待點選主要文件。

document.onclick = async () => {
  const writer = await handle.createWriter();
  await writer.truncate(0);
  await writer.write(0, document.body.textContent);
  await writer.close();
};

d2729a8f76f43073.png

現在當你點選 (未按兩下) 文件時,系統會顯示權限提示。如果授予權限,檔案內容將會是您先前在 body 中編輯的內容。以其他編輯器開啟檔案以確認變更 (或是再次按兩下文件並重新開啟檔案,即可重新執行這項程序)。

2eccf61fe4a46109.png

202263abdedae737.png

恭喜!您剛剛建立了世界 [citation needed] 最小的文字編輯器。

意見回饋

您對這個 API 有何想法?請填寫這份問卷,協助我們提升服務品質:

這個 API 是否直覺好用?

你有執行這個範例嗎?

還有其他想法嗎?缺少功能嗎?請在這份問卷調查中迅速提供意見。感謝您!

4. Shape Detection API

Shape Detection API 提供加速形狀偵測工具 (例如適用於人臉),適用於靜態圖片和/或即時影像動態饋給。作業系統具備高效能和高度最佳化的功能偵測工具,例如 Android FaceDetector。Shape Detection API 可開啟這些原生實作項目,並透過一組 JavaScript 介面顯示這些實作項目。

目前支援的功能是透過 FaceDetector 介面進行臉部偵測、透過 BarcodeDetector 介面偵測條碼,以及透過 TextDetector 介面進行文字偵測 (光學字元辨識)。

臉部偵測

Shape Detection API 的一項迷人功能是臉部偵測。如要測試,我們需要包含臉孔的頁面。標有作者這個頁面是很好的開始。如下方螢幕截圖所示。在支援的瀏覽器中,系統可辨識臉部邊界方塊和臉孔地標。

您可以查看重混或編輯 Glitch 專案 (特別是 script.js 檔案),藉此瞭解應用程式只需使用少許程式碼即可。

f4aa7b77a0a1d1f5.png

如要完全動態顯示作者臉,而不侷限於使用作者的臉,請使用私密分頁或訪客模式前往這個 Google 搜尋結果網頁,查看所有臉孔。接著,在該頁面中任一處按一下滑鼠右鍵,然後點選「檢查」,即可開啟 Chrome 開發人員工具。接著,在「Console」(控制台) 分頁中,貼上下方的程式碼片段。程式碼會以半透明的紅色方塊醒目顯示偵測到的臉孔。

document.querySelectorAll('img[alt]:not([alt=""])').forEach(async (img) => {
  try {
    const faces = await new FaceDetector().detect(img);
    faces.forEach(face => {
      const div = document.createElement('div');
      const box = face.boundingBox;
      const computedStyle = getComputedStyle(img);
      const [top, right, bottom, left] = [
        computedStyle.marginTop,
        computedStyle.marginRight,
        computedStyle.marginBottom,
        computedStyle.marginLeft
      ].map(m => parseInt(m, 10));
      const scaleX = img.width / img.naturalWidth;
      const scaleY = img.height / img.naturalHeight;
      div.style.backgroundColor = 'rgba(255, 0, 0, 0.5)';
      div.style.position = 'absolute';
      div.style.top = `${scaleY * box.top + top}px`;
      div.style.left = `${scaleX * box.left + left}px`;
      div.style.width = `${scaleX * box.width}px`;
      div.style.height = `${scaleY * box.height}px`;
      img.before(div);
    });
  } catch(e) {
    console.error(e);
  }
});

請注意,系統發生一些 DOMException 訊息,因此系統無法處理所有圖片。這是因為「不需捲動位置」的圖片會以資料 URI 內嵌且可供存取,而「需捲動位置」的圖片則來自未設定來支援 CORS 的其他網域。為了方便示範,我們不需要擔心這個問題。

臉部地標偵測

除了人臉外,macOS 還支援偵測臉部地標。如要測試臉部地標的偵測結果,請將下列程式碼片段貼到控制台。提醒:由於 crbug.com/914348 的緣故,地標陣容並非完全完善,但您可以瞭解這股趨勢以及這項功能的強大之處。

document.querySelectorAll('img[alt]:not([alt=""])').forEach(async (img) => {
  try {
    const faces = await new FaceDetector().detect(img);
    faces.forEach(face => {
      const div = document.createElement('div');
      const box = face.boundingBox;
      const computedStyle = getComputedStyle(img);
      const [top, right, bottom, left] = [
        computedStyle.marginTop,
        computedStyle.marginRight,
        computedStyle.marginBottom,
        computedStyle.marginLeft
      ].map(m => parseInt(m, 10));
      const scaleX = img.width / img.naturalWidth;
      const scaleY = img.height / img.naturalHeight;
      div.style.backgroundColor = 'rgba(255, 0, 0, 0.5)';
      div.style.position = 'absolute';
      div.style.top = `${scaleY * box.top + top}px`;
      div.style.left = `${scaleX * box.left + left}px`;
      div.style.width = `${scaleX * box.width}px`;
      div.style.height = `${scaleY * box.height}px`;
      img.before(div);

      const landmarkSVG = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
      landmarkSVG.style.position = 'absolute';
      landmarkSVG.classList.add('landmarks');
      landmarkSVG.setAttribute('viewBox', `0 0 ${img.width} ${img.height}`);
      landmarkSVG.style.width = `${img.width}px`;
      landmarkSVG.style.height = `${img.height}px`;
      face.landmarks.map((landmark) => {                    
        landmarkSVG.innerHTML += `<polygon class="landmark-${landmark.type}" points="${
        landmark.locations.map((point) => {          
          return `${scaleX * point.x},${scaleY * point.y} `;
        }).join(' ')
      }" /></svg>`;          
      });
      div.before(landmarkSVG);
    });
  } catch(e) {
    console.error(e);
  }
});

條碼偵測

Shape Detection API 的第二個功能是條碼偵測。和先前一樣,我們需要含有條碼的網頁,例如這個網頁。在瀏覽器中開啟時,畫面上會顯示各種 QR code。你可以重混或編輯 Glitch 專案 (特別是 script.js 檔案),以便查看完成進度。

7778003ff472389b.png

如果要讓內容更生動,可以再次使用 Google 圖片搜尋這次使用瀏覽器透過私密分頁或訪客模式前往這個 Google 搜尋結果網頁。現在,將下方的程式碼片段貼到 Chrome 開發人員工具控制台分頁。不久之後,系統會識別出的條碼會以原始值和條碼類型加上註解。

document.querySelectorAll('img[alt]:not([alt=""])').forEach(async (img) => {
  try {
    const barcodes = await new BarcodeDetector().detect(img);
    barcodes.forEach(barcode => {
      const div = document.createElement('div');
      const box = barcode.boundingBox;
      const computedStyle = getComputedStyle(img);
      const [top, right, bottom, left] = [
        computedStyle.marginTop,
        computedStyle.marginRight,
        computedStyle.marginBottom,
        computedStyle.marginLeft
      ].map(m => parseInt(m, 10));
      const scaleX = img.width / img.naturalWidth;
      const scaleY = img.height / img.naturalHeight;
      div.style.backgroundColor = 'rgba(255, 255, 255, 0.75)';
      div.style.position = 'absolute';
      div.style.top = `${scaleY * box.top + top}px`;
      div.style.left = `${scaleX * box.left - left}px`;
      div.style.width = `${scaleX * box.width}px`;
      div.style.height = `${scaleY * box.height}px`;
      div.style.color = 'black';
      div.style.fontSize = '14px';      
      div.textContent = `${barcode.rawValue}`;
      img.before(div);
    });
  } catch(e) {
    console.error(e);
  }
});

文字偵測

Shape Detection API 的最後一個功能是文字偵測。現在你已知道該演習:我們需要含有文字的圖片頁面,比如 Google 圖書掃描結果的這個網頁。在支援的瀏覽器中,您會看到已辨識的文字,以及文字段落周圍繪製的定界框。你可以重混或編輯 Glitch 專案 (特別是 script.js 檔案),以便查看完成進度。

ec2be17d1e4d01ba.png

如要動態測試,請透過私密分頁或訪客模式前往這個搜尋結果網頁。現在,將下方的程式碼片段貼到 Chrome 開發人員工具控制台分頁。只要稍待片刻,系統就會辨識部分文字。

document.querySelectorAll('img[alt]:not([alt=""])').forEach(async (img) => {
  try {
    const texts = await new TextDetector().detect(img);
    texts.forEach(text => {
      const div = document.createElement('div');
      const box = text.boundingBox;
      const computedStyle = getComputedStyle(img);
      const [top, right, bottom, left] = [
        computedStyle.marginTop,
        computedStyle.marginRight,
        computedStyle.marginBottom,
        computedStyle.marginLeft
      ].map(m => parseInt(m, 10));
      const scaleX = img.width / img.naturalWidth;
      const scaleY = img.height / img.naturalHeight;
      div.style.backgroundColor = 'rgba(255, 255, 255, 0.75)';
      div.style.position = 'absolute';
      div.style.top = `${scaleY * box.top + top}px`;
      div.style.left = `${scaleX * box.left - left}px`;
      div.style.width = `${scaleX * box.width}px`;
      div.style.height = `${scaleY * box.height}px`;
      div.style.color = 'black';
      div.style.fontSize = '14px';      
      div.innerHTML = text.rawValue;
      img.before(div);
    });
  } catch(e) {
    console.error(e);
  }
});

意見回饋

您對這個 API 有何想法?請填寫這份問卷,協助我們提升服務品質:

這個 API 是否直覺好用?

你有執行這個範例嗎?

還有其他想法嗎?缺少功能嗎?請在這份問卷調查中迅速提供意見。感謝您!

5. 網路分享目標 API

Web Share Target API 可讓已安裝的網頁應用程式向基礎作業系統註冊做為共用目標,以接收來自 Web Share API 或系統事件 (例如作業系統層級分享按鈕) 的共用內容。

安裝 PWA 即可分享內容

首先,您需要有可以與他人共用的 PWA。這次,Airhorner (幸運) 無法執行這項工作,但 Web Share Target 試用版應用程式可以助您一臂之力。在裝置的主畫面安裝應用程式。

925df16a12638bb2.png

將內容分享到 PWA

接下來,您需要分享一些相片,例如 Google 相簿中的相片。使用「分享」按鈕並選取「留言簿 PWA」做為分享目標。

7216e8bb1be6d6db.png

只要輕觸應用程式圖示,就會直接進入簽名簿 PWA,相片就在上面。

9016985cb4bb48fe.png

那麼,這項功能如何運作呢?如需瞭解詳情,請參閱 Scrapbook PWA 的網頁應用程式資訊清單。使 Web Share Target API 正常運作的設定位於資訊清單的 "share_target" 屬性中,且在資訊清單的 "action" 欄位指向以 "params" 中所列參數裝飾的網址。

接著,共用端將據此填入網址範本 (透過共用動作執行,或由開發人員使用 Web Share API 透過程式輔助方式控制),讓接收端可擷取參數並執行其他操作,例如顯示參數。

{
  "action": "/_share-target",
  "enctype": "multipart/form-data",
  "method": "POST",
  "params": {
    "files": [{
      "name": "media",
      "accept": ["audio/*", "image/*", "video/*"]
    }]
  }
}

意見回饋

您對這個 API 有何想法?請填寫這份問卷,協助我們提升服務品質:

這個 API 是否直覺好用?

你有執行這個範例嗎?

還有其他想法嗎?缺少功能嗎?請在這份問卷調查中迅速提供意見。感謝您!

6. Wake Lock API

為避免電量消耗,多數裝置在閒置時都會快速進入休眠狀態。雖然這是最常見的情況,但某些應用程式必須讓螢幕或裝置保持喚醒狀態,才能完成作業。Wake Lock API 可讓您避免裝置變暗、鎖定螢幕,或避免裝置進入休眠狀態。這項功能可提供新體驗,但目前必須仰賴原生應用程式才能運作。

設定螢幕保護程式

如要測試 Wake Lock API,您必須先確保裝置能夠進入休眠模式。因此,請在作業系統的偏好設定窗格中啟用所選螢幕保護程式,並確保裝置會在 1 分鐘後啟動。為了確保裝置正常運作,請在這段時間內不要離開您的裝置 (是的,我會很痛苦)。下方螢幕截圖顯示的是 macOS。當然,您可以在行動裝置或任何支援的電腦平台上試用這項功能。

6f345e8c2b6c22c.png

設定螢幕 Wake Lock

您現在知道螢幕保護程式已正常運作,並使用 "screen" 類型的 Wake Lock,避免螢幕保護程式執行工作。前往 Wake Lock 試用版應用程式,然後按一下「啟用」

screen「Wake Lock」核取方塊。

12ed15dd94f51d4d.png

從該時刻開始,Wake Lock 就會啟用。如果您一定有耐心,導致裝置沒動過一分鐘,現在您會發現螢幕保護程式並未啟動。

運作方式想進一步瞭解相關資訊,請前往 Wake Lock 試用版應用程式的 Glitch 專案,並參考 script.js。下方程式碼片段是程式碼的重點。開啟新分頁 (或使用任何開啟的分頁),然後將下列程式碼貼入 Chrome 開發人員工具控制台。當您點選視窗時,應該會看到持續運作 10 秒的 Wake Lock (請參閱主控台記錄),而螢幕保護程式應該不會啟動。

if ('wakeLock' in navigator && 'request' in navigator.wakeLock) {  
  let wakeLock = null;
  
  const requestWakeLock = async () => {
    try {
      wakeLock = await navigator.wakeLock.request('screen');
      wakeLock.addEventListener('release', () => {        
        console.log('Wake Lock was released');                    
      });
      console.log('Wake Lock is active');      
    } catch (e) {      
      console.error(`${e.name}, ${e.message}`);
    } 
  };

  requestWakeLock();
  window.setTimeout(() => {
    wakeLock.release();
  }, 10 * 1000);
}

621c2654d06a7cce.png

意見回饋

您對這個 API 有何想法?請填寫這份問卷,協助我們提升服務品質:

這個 API 是否直覺好用?

你有執行這個範例嗎?

還有其他想法嗎?缺少功能嗎?請在這份問卷調查中快速提供您的意見。感謝您!

7. 聯絡人挑選器 API

我們最期待的 API 是 Contact Picker API。允許網頁應用程式透過裝置的原生聯絡人管理員存取聯絡人資料,以便網頁應用程式存取你的聯絡人姓名、電子郵件地址和電話號碼。您可以指定要只列出一位或多位聯絡人,也可以指定所有欄位,或只指定部分名稱、電子郵件地址和電話號碼。

隱私權注意事項

挑選器開啟後,您就可以選取要分享的聯絡人。請注意,系統不會提供「全選」選項,是有意識地做出分享決定。同樣地,存取權並非連續性,而是一次性決定。

存取聯絡人

存取聯絡人非常簡單。在挑選器開啟前,您可以指定所需的欄位 (nameemailtelephone 選項),以及是否要存取多個聯絡人或單一聯絡人。開啟試用版應用程式後,即可在 Android 裝置上測試這個 API。原始碼的相關章節主要內容如下:

getContactsButton.addEventListener('click', async () => {
  const contacts = await navigator.contacts.select(
      ['name', 'email'],
      {multiple: true});
  if (!contacts.length) {
    // No contacts were selected, or picker couldn't be opened.
    return;
  }
  console.log(contacts);
});

de94db2dfb7c67af.png

8. 非同步剪貼簿 API

複製及貼上文字

到目前為止,仍無法透過程式複製圖片並貼到系統的剪貼簿。我們最近為 Async Clipboard API 新增了圖片支援,

方便你隨處複製及貼上圖片新功能是您也可以將圖片複製到剪貼簿。非同步剪貼簿 API 目前有一段時間內支援複製及貼上文字的功能。您可以呼叫 navigator.clipboard.writeText() 將文字複製到剪貼簿,之後再呼叫 navigator.clipboard.readText() 貼上該文字。

複製及貼上圖片

現在您也可以將圖片複製到剪貼簿。為此,您需要圖片資料做為 blob,接著再傳遞至剪貼簿項目建構函式。最後,您可以呼叫 navigator.clipboard.write() 複製這個剪貼簿項目。

// Copy: Writing image to the clipboard
try {
  const imgURL = 'https://developers.google.com/web/updates/images/generic/file.png';
  const data = await fetch(imgURL);
  const blob = await data.blob();
  await navigator.clipboard.write([
    new ClipboardItem(Object.defineProperty({}, blob.type, {
      value: blob,
      enumerable: true
    }))
  ]);
  console.log('Image copied.');
} catch(e) {
  console.error(e, e.message);
}

將圖片貼回剪貼簿看起來很複雜,但實際上只是將 blob 從剪貼簿項目中復原。可能有許多種,因此你必須逐一確認,直到想查看其中一個項目為止。基於安全考量,目前這項工具僅支援 PNG 圖片,但日後可能會支援更多圖片格式。

async function getClipboardContents() {
  try {
    const clipboardItems = await navigator.clipboard.read();
    for (const clipboardItem of clipboardItems) {
      try {
        for (const type of clipboardItem.types) {
          const blob = await clipboardItem.getType(type);
          console.log(URL.createObjectURL(blob));
        }
      } catch (e) {
        console.error(e, e.message);
      }
    }
  } catch (e) {
    console.error(e, e.message);
  }
}

您可以在試用版應用程式中查看這個 API 的實際運作情形,上方已嵌入相關原始碼的相關程式碼片段。沒有權限可將圖片複製到剪貼簿,但您必須授予存取權,才能從剪貼簿貼上圖片。

99f6dbf35ff4f393.png

授予存取權後,您就可以讀取剪貼簿中的圖片,並貼入應用程式:

ace5945f4aca70ff.png

9. 成功抵達!

恭喜,您已經完成本程式碼研究室的結尾。再次提醒您,大多數 API 仍為不斷變動,且正在積極開發。因此,我們的團隊十分重視你的意見回饋,因為只有與這樣的人互動,才能確保這些 API 符合需求。

此外,建議您定期查看功能到達網頁。我們會隨時更新這些內容,並指向所有關於目前所用 API 的深度文章。繼續保持!

Tom 和整個能力團隊 🐡?