ウェブ機能 Codelab

ウェブ機能

Google は、ウェブとネイティブの機能のギャップを埋め、デベロッパーがオープンなウェブで優れたエクスペリエンスを簡単に構築できるようにしたいと考えています。Google は、すべてのデベロッパーが優れたウェブ エクスペリエンスを実現するために必要な機能にアクセスできるべきであるという考えに基づき、ウェブの性能向上に取り組んでいます。

ファイル システム アクセスやアイドル検出など、ネイティブ テクノロジーでは利用できるものの、ウェブでは利用できない機能もあります。これらの機能がないため、一部の種類のアプリはウェブで配信できないか、有用性が低くなります。

Google は、既存のオープンなウェブ プラットフォーム標準プロセスを使用して、オープンかつ透明性のある方法でこれらの新機能を設計および開発すると同時に、デベロッパーや他のブラウザ ベンダーから初期段階のフィードバックを収集して設計の相互運用性を確保します。

作成するアプリの概要

この Codelab では、まったく新しいウェブ API やフラグでのみ利用可能なウェブ API を操作します。そのため、この Codelab では、特定の最終プロダクトをビルドすることではなく、API 自体と、これらの API がロック解除するユースケースに焦点を当てます。

ラボの内容

この Codelab では、いくつかの最先端の API の基本的な仕組みについて説明します。これらの仕組みはまだ決まっていないため、デベロッパー フローに関するフィードバックをお寄せいただきありがとうございます。

必要なもの

この Codelab で取り上げる API は最先端のものであるため、それぞれの API の要件は異なります。各セクションの冒頭に記載されている互換性情報をよくお読みください。

Codelab へのアプローチ方法

Codelab は、必ずしも順番に処理されるとは限りません。各セクションは独立した API を表すため、関心のあるトピックを自由に選択してください。

Badging API の目標は、ユーザーがバックグラウンドで起こっている出来事に注意を向けさせることです。この Codelab のデモを簡単にするために、この API を使って、フォアグラウンドで起きていることにユーザーの注意を向けましょう。その後で、バックグラウンドで起こることを頭に感じることができます。

Airhorner をインストールする

この API を使用するには、ホーム画面に PWA をインストールしておく必要があります。まず、世界的に有名な airhorner.com などの PWA をインストールします。右上隅の [インストール] ボタンをクリックするか、その他メニューを使用して手動でインストールします。

確認を求めるメッセージが表示されたら、[インストール] をクリックします。

オペレーティング システムの Dock に新しいアイコンが表示されます。これをクリックして PWA を起動します。専用のアプリ ウィンドウがあり、スタンドアロン モードで実行されます。

バッジの設定

これで PWA のインストールが完了しました。次に、バッジに表示する数値データ(バッジに含まれるのは数値のみ)とします。エアホーナーで計算すると、ホーンの回数です。Airhorner アプリをインストールしたら、角を曲げてバッジを確認してください。ホルンの音が鳴るたびに音が鳴ります。

では、その仕組みについて説明します。基本的に、コードは次のとおりです。

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

ホーンを数回鳴らして、PWA のアイコンを確認します。アイコンは毎回更新され、ホーンが鳴ります。このように簡単に設定できます。

バッジを削除する

カウンタは 99 まで上昇し、その後、最初からやり直します。手動でリセットすることもできます。DevTools の Console タブを開き、下の行を貼り付けて Enter キーを押します。

navigator.setExperimentalAppBadge(0);

または、次のスニペットに示すように明示的にバッジを消去して削除することもできます。これで、PWA のアイコンが初めのように、鮮明な状態で、バッジなしで再表示されます。

navigator.clearExperimentalAppBadge();

Feedback

この API に対する評価をお聞かせください。お手数ですが、以下のアンケートにご協力いただけますと幸いです。

この API は直感的に使えるものでしたか?

はい いいえ

実行する例を入手しましたか。

はい いいえ

他にもご意見がございましたら、不足している機能はありましたか?簡単なアンケートでフィードバックをお寄せください。ご静聴ありがとうございました。

Native File System API を使用すると、ローカル デバイス上のファイルを操作する強力なウェブアプリを構築できます。ユーザーがウェブアプリへのアクセス権を付与すると、この API によってウェブアプリはユーザーのデバイス上のファイルとフォルダに対する変更を直接読み取り、保存できるようになります。

ファイルの読み取り

Native File System API の「Hello, world」は、ローカル ファイルを読み取り、ファイルの内容を取得するためのものです。プレーンな .txt ファイルを作成し、テキストを入力します。次に、example.com などのセキュアなサイト(つまり HTTPS で配信されているサイト)に移動し、DevTools コンソールを開きます。以下のコード スニペットをコンソールに貼り付けます。Native File System API にはユーザー ジェスチャーが必要なため、ドキュメントにはダブルクリック ハンドラが追加されます。後でファイル ハンドルが必要になるため、単にグローバル変数にします。

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

その後、example.com ページの任意の場所をダブルクリックすると、ファイル選択ツールが表示されます。

以前に作成した .txt ファイルを選択します。このファイルの内容が example.com の実際の body の内容と置き換わります。

ファイルの保存

次に、いくつかの変更を行います。そのため、次のコード スニペットを貼り付けて body を編集できるようにしましょう。ブラウザがテキスト エディタであるかのようにテキストを編集できるようになりました。

document.body.contentEditable = true;

次に、これらの変更を元のファイルに書き戻します。そのため、ファイル ハンドルにライターを設定する必要があります。これは、以下のスニペットをコンソールに貼り付けることで取得できます。ここでもユーザー ジェスチャーが必要なため、今回はメイン ドキュメントをクリックするのを待ちます。

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

ダブルクリックすると、ダブルクリックしたときに権限を求めるメッセージが表示されます。権限を付与すると、ファイルの内容は、以前 body で編集したものになります。別のエディタでファイルを開いて、変更内容を確認します(または、ドキュメントをダブルクリックして開き直します)。

これで、[citation needed] で最小のテキスト エディタが作成されました。

Feedback

この API に対する評価をお聞かせください。お手数ですが、以下のアンケートにご協力いただけますと幸いです。

この API は直感的に使えるものでしたか?

はい いいえ

実行する例を入手しましたか。

はい いいえ

他にもご意見がございましたら、不足している機能はありましたか?簡単なアンケートでフィードバックをお寄せください。ご静聴ありがとうございました。

Shape Detection API は、加速度シェイプ検出機能(人間の顔など)へのアクセスを提供し、静止画像やライブ画像フィードで動作します。オペレーティング システムには、Android FaceDetector などの高性能で高度に最適化された機能検出項目があります。Shape Detection API は、これらのネイティブ実装を開き、JavaScript インターフェースのセットを通じて公開します。

現在サポートされている機能は、FaceDetector インターフェースによる顔検出、BarcodeDetector インターフェースによるバーコード検出、TextDetector インターフェースを介したテキスト検出(光学式文字認識)です。

顔検出

Shape Detection API の特長は、顔検出です。顔をテストするには、ページに顔が写っている必要があります。まずは著者の顔が掲載されたこちらのページをご覧ください。次に示すスクリーンショットのようになります。サポートされているブラウザでは、顔の境界ボックスと顔のランドマークが認識されます。

これを実現するために必要なコードは、Glitch プロジェクト(特に script.js ファイル)のリミックスまたは編集によるものです。

作成者の顔を操作するだけでなく、完全に動的にする場合は、こちらの 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 の 2 つ目の機能はバーコード検出です。前述のように、バーコードがあるページ(こちら)が必要です。これをブラウザで開くと、解読された各種 QR コードが表示されます。Glitch プロジェクト(特に script.js ファイル)をリミックスまたは編集し、実際の結果を確認します。

もっと動的な内容が必要な場合は、Google 画像検索をご利用ください。今回は、ブラウザでシークレット タブまたはゲストモードで、この Google 検索結果ページに移動します。下のスニペットを Chrome DevTools コンソールのタブに貼り付けます。しばらくすると、認識されたバーコードに未加工の値とバーコードのアノテーションが表示されます。

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 ファイル)をリミックスまたは編集し、実際の結果を確認します。

動的にテストするには、検索結果ページ(シークレット タブまたはゲストモード)に移動します。下のスニペットを Chrome DevTools コンソールのタブに貼り付けます。少し待つと、一部のテキストが認識されます。

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);
  }
});

Feedback

この API に対する評価をお聞かせください。お手数ですが、以下のアンケートにご協力いただけますと幸いです。

この API は直感的に使えるものでしたか?

はい いいえ

実行する例を入手しましたか。

はい いいえ

他にもご意見がございましたら、不足している機能はありましたか?簡単なアンケートでフィードバックをお寄せください。ご静聴ありがとうございました。

Web Share Target API を使用すると、インストール済みのウェブアプリは基盤となるオペレーティング システムに共有ターゲットとして登録し、Web Share API またはシステム イベント(オペレーティング システムレベルの共有ボタンなど)から共有コンテンツを受け取ることができます。

共有する PWA をインストールする

最初に、共有可能な PWA が必要です。今回は Airhorner(がうれしい)に勝ちませんが、Web Share Target デモアプリがサポートします。デバイスのホーム画面にアプリをインストールします。

PWA に何かを共有する

次に、Google フォトからの写真など、共有したい情報が必要です。[共有] ボタンを使用して、スクラップブック PWA を共有ターゲットとして選択します。

アプリアイコンをタップすると、スクラップブック PWA(写真を表示)が直接表示されます。

では、その仕組みについて説明します。詳細は、Scrapbook PWA のウェブアプリ マニフェストをご確認ください。Web Share Target API を動作させる設定は、マニフェストの "share_target" プロパティにあり、"action" フィールドに、"params" にリストされているパラメータで装飾される URL が指定されます。

共有側はそれに応じて URL テンプレートに入力します(共有アクションによって促進されるか、Web Share API を使用してプログラムで制御します)。これにより、受信側はパラメータを抽出し、表示などの処理を行うことができます。

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

Feedback

この API に対する評価をお聞かせください。お手数ですが、以下のアンケートにご協力いただけますと幸いです。

この API は直感的に使えるものでしたか?

はい いいえ

実行する例を入手しましたか。

はい いいえ

他にもご意見がございましたら、不足している機能はありましたか?簡単なアンケートでフィードバックをお寄せください。ご静聴ありがとうございました。

バッテリーの消耗を避けるため、ほとんどのデバイスは、アイドル状態のままにするとすぐにスリープ状態になります。ほとんどの場合は問題ありませんが、アプリによっては、処理を完了するために画面やデバイスを復帰させておく必要があります。Wake Lock API を使用すると、デバイスの画面が暗くなりロックされるのを防ぐことができます。また、デバイスがスリープ状態になるのを防ぐこともできます。この機能により、これまでネイティブ アプリが必要だった新しいエクスペリエンスを実現できます。

スクリーンセーバーを設定する

Wake Lock API をテストするには、まず、デバイスがスリープ状態になることを確認します。そのため、オペレーティング システムの設定ペインで、任意のスクリーンセーバーを有効にし、1 分後に起動することを確認してください。デバイスを長時間、放置して動作し続けることを確認します(そうです、苦痛です)。以下のスクリーンショットは macOS を示していますが、もちろん、Android モバイル デバイスまたは対応するデスクトップ プラットフォームで試すこともできます。

画面の wake lock を設定する

スクリーンセーバーが機能していることを確認したので、タイプ "screen" の wake lock を使用して、スクリーンセーバーがそのジョブを実行しないようにします。Wake Lock デモアプリに移動して [Activate] screen [Wake Lock] チェックボックスをオンにします。

その時点から、wake lock がアクティブになります。デバイスをしばらくそのままにしておくと、スクリーンセーバーは実際には開始されません。

仕組み詳しくは、Wake Lock デモアプリの Glitch プロジェクトにアクセスして script.js をご覧ください。コードの gist は以下のスニペットにあります。新しいタブを開き(または開いたことがあるタブを使用して)、下記のコードを 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);
}

Feedback

この API に対する評価をお聞かせください。お手数ですが、以下のアンケートにご協力いただけますと幸いです。

この API は直感的に使えるものでしたか?

はい いいえ

実行する例を入手しましたか。

はい いいえ

他にもご意見がございましたら、不足している機能はありましたか?簡単なアンケートでフィードバックをお寄せください。ご静聴ありがとうございました。

とても面白い API は、Contact Picker API です。ウェブアプリがデバイスのネイティブ連絡先管理ツールの連絡先にアクセスできるようにするため、ウェブアプリは連絡先の名前、メールアドレス、電話番号にアクセスできます。1 つの連絡先だけを使用するか、すべての項目を使用するか、名前、メールアドレス、電話番号のサブセットのみを使用するかを指定できます。

プライバシーへの配慮

選択ツールが開いたら、共有する連絡先を選択できます。[すべて選択] オプションはありません。これは慎重な選択であり、共有を意識的に行うことが目的です。同様に、アクセスは継続的ではなく、1 回限りの決定です。

連絡先へのアクセス

連絡先へのアクセスは簡単です。選択ツールが開く前に、フィールド(nameemailtelephone など)を選択したり、複数の連絡先にアクセスするか、1 つの連絡先にアクセスするかを指定したりできます。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);
});

テキストのコピーして貼り付け

これまで、画像をプログラムでクリップボードにコピーして貼り付ける方法はありませんでした。最近、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 の動作はデモアプリで見ることができます。ソースコードからの関連するスニペットは上に埋め込まれています。画像をクリップボードにコピーすることは権限がなくても行えますが、クリップボードから貼り付ける権限を付与する必要があります。

アクセスを許可すると、クリップボードから画像を読み込んでアプリケーションに貼り付けることができます。

お疲れさまでした。Codelab は以上です。繰り返しになりますが、ほとんどの API はまだ流動的なものであり、積極的に開発が行われています。したがって、APIを適切にご使用いただくためには皆様の皆様の協力が不可欠です。

また、機能に関するランディング ページを頻繁にご覧いただくことをおすすめします。この API は最新の状態に保たれ、Google が取り組んでいる API に関する詳細な記事がすべて参照されています。ロックインを続けましょう'!

Tom と Capabilities チーム全体 🐡?