ポータルのハンズオン: ウェブ上でのシームレスなナビゲーション

提案された Portals API によってナビゲーション UX がどのように改善されるかを説明します。

優れたユーザー エクスペリエンスを提供するには、ページの読み込みを速くすることが重要です。 しかし、私たちがよく見落とす領域の一つがページの遷移です。これは、ユーザーがページ間を移動したときに表示されるものです。

Portals と呼ばれる新しいウェブ プラットフォーム API の提案は、ユーザーがサイト内を移動する際のエクスペリエンスを合理化することで、これを可能にすることを目的としています。

ポータルの活用例:

ポータルによるシームレスな埋め込みとナビゲーション。作成者: Adam Argyle

ポータルでできること

シングルページ アプリケーション(SPA)でも移行は可能ですが、その代償として構築が複雑になります。マルチページ アプリ(MPA)は簡単に作成できますが、ページ間に空白の画面が生じます。

ポータルでは、MPA の複雑さの軽減と SPA のシームレスな移行という、両方の長所を兼ね備えています。埋め込みが可能なという点では <iframe> に似ていますが、<iframe> とは異なり、コンテンツに移動するための機能もあります。

Chrome Dev Summit 2018 で発表された内容

従来のナビゲーションでは、ブラウザがデスティネーションのレンダリングを完了するまで、ユーザーは空白の画面のまま待たなければなりません。ポータルでは、ユーザーはアニメーションを体験し、<portal> はコンテンツをプリレンダリングしてシームレスなナビゲーション エクスペリエンスを実現します。

ポータルが登場する前は、<iframe> を使って別のページをレンダリングすることができました。ページ内でフレームを移動するアニメーションを追加することもできます。ただし、<iframe> ではコンテンツに移動できません。ポータルはこのギャップを埋め、興味深いユースケースを可能にします。

ポータルを試す

about://flags を介して有効にする

Chrome 85 以降のバージョンでは、試験運用版のフラグを切り替えてポータルをお試しください。

  • 同一オリジン ナビゲーションで about://flags/#enable-portals フラグを有効にします。
  • クロスオリジン ナビゲーションをテストする場合は、about://flags/#enable-portals-cross-origin フラグも有効にします。

ポータル テストの初期段階では、--user-data-dir コマンドライン フラグを設定して、まったく別のユーザーデータ ディレクトリをテストに使用することをおすすめします。ポータルが有効になったら、DevTools で、新しい HTMLPortalElement があることを確認します。

HTMLPortalElement を表示している DevTools コンソールのスクリーンショット

ポータルを実装する

基本的な実装例を見ていきましょう。

// Create a portal with the wikipedia page, and embed it
// (like an iframe). You can also use the <portal> tag instead.
portal = document.createElement('portal');
portal.src = 'https://en.wikipedia.org/wiki/World_Wide_Web';
portal.style = '...';
document.body.appendChild(portal);

// When the user touches the preview (embedded portal):
// do fancy animation, e.g. expand …
// and finish by doing the actual transition.
// For the sake of simplicity, this snippet will navigate
// on the `onload` event of the Portals element.
portal.addEventListener('load', (evt) => {
   portal.activate();
});

とてもシンプルです。DevTools コンソールでこのコードを試すと、Wikipedia のページが開きます。

プレビュー ポータル スタイルのデモの GIF

Chrome Dev Summit で紹介したように、上記のデモと同様の機能を構築したい場合は、次のスニペットをご覧ください。

// Adding some styles with transitions
const style = document.createElement('style');
style.innerHTML = `
  portal {
    position:fixed;
    width: 100%;
    height: 100%;
    opacity: 0;
    box-shadow: 0 0 20px 10px #999;
    transform: scale(0.4);
    transform-origin: bottom left;
    bottom: 20px;
    left: 20px;
    animation-name: fade-in;
    animation-duration: 1s;
    animation-delay: 2s;
    animation-fill-mode: forwards;
  }
  .portal-transition {
    transition: transform 0.4s;
  }
  @media (prefers-reduced-motion: reduce) {
    .portal-transition {
      transition: transform 0.001s;
    }
  }
  .portal-reveal {
    transform: scale(1.0) translateX(-20px) translateY(20px);
  }
  @keyframes fade-in {
    0%   { opacity: 0; }
    100% { opacity: 1; }
  }
`;
const portal = document.createElement('portal');
// Let's navigate into the WICG Portals spec page
portal.src = 'https://wicg.github.io/portals/';
// Add a class that defines the transition. Consider using
// `prefers-reduced-motion` media query to control the animation.
// https://developers.google.com/web/updates/2019/03/prefers-reduced-motion
portal.classList.add('portal-transition');
portal.addEventListener('click', (evt) => {
  // Animate the portal once user interacts
  portal.classList.add('portal-reveal');
});
portal.addEventListener('transitionend', (evt) => {
  if (evt.propertyName == 'transform') {
    // Activate the portal once the transition has completed
    portal.activate();
  }
});
document.body.append(style, portal);

また、ポータルを使用してウェブサイトを段階的に強化するための機能検出も簡単です。

if ('HTMLPortalElement' in window) {
  // If this is a platform that have Portals...
  const portal = document.createElement('portal');
  ...
}

ポータルの雰囲気をすぐに確認したい場合は、uskay-portals-demo.glitch.me をお試しください。Chrome 85 以降のバージョンでアクセスし、試験運用版フラグをオンにしてください。

  1. プレビューする URL を入力します。
  2. そのページは <portal> 要素として埋め込まれます。
  3. プレビューをクリックします。
  4. アニメーションの後にプレビューがアクティブになります。

ポータルを使用するグリッチデモの GIF

仕様を確認する

Google は、Web Incubation Community Group(WICG)でポータルの仕様について積極的に議論しています。状況をすばやく把握するには、主なシナリオをご覧ください。これら 3 つの重要な機能をよく理解しておく必要があります。

  • <portal> 要素: HTML 要素自体。API は非常にシンプルです。これは、src 属性、activate 関数、メッセージング用のインターフェース(postMessage)で構成されます。activate は、アクティベート時にデータを <portal> に渡すオプションの引数を取ります。
  • portalHost インターフェース: portalHost オブジェクトを window オブジェクトに追加します。これにより、ページが <portal> 要素として埋め込まれているかどうかを確認できます。また、ホストにメッセージング(postMessage)を返すインターフェースも用意されています。
  • PortalActivateEvent インターフェース: <portal> がアクティブになったときに発生するイベント。adoptPredecessor という便利な関数があります。これを使用すると、前のページを <portal> 要素として取得できます。これにより、2 つのページ間でシームレスなナビゲーションと構成されたエクスペリエンスを作成できます。

基本的な使用パターンの先を見ていきましょう。ここでは、ポータルで実現できることとサンプルコードの一部をご紹介します。

<portal> 要素として埋め込まれた場合のスタイルをカスタマイズする

// Detect whether this page is hosted in a portal
if (window.portalHost) {
  // Customize the UI when being embedded as a portal
}

<portal> 要素と portalHost の間のメッセージング

// Send message to the portal element
const portal = document.querySelector('portal');
portal.postMessage({someKey: someValue}, ORIGIN);

// Receive message via window.portalHost
window.portalHost.addEventListener('message', (evt) => {
  const data = evt.data.someKey;
  // handle the event
});

<portal> 要素を有効にして portalactivate イベントを受信する

// You can optionally add data to the argument of the activate function
portal.activate({data: {somekey: 'somevalue'}});

// The portal content will receive the portalactivate event
// when the activate happens
window.addEventListener('portalactivate', (evt) => {
  // Data available as evt.data
  const data = evt.data;
});

先行する要素を取得する

// Listen to the portalactivate event
window.addEventListener('portalactivate', (evt) => {
  // ... and creatively use the predecessor
  const portal = evt.adoptPredecessor();
  document.querySelector('someElm').appendChild(portal);
});

ページが先行として採用されたことを確認する

// The activate function returns a Promise.
// When the promise resolves, it means that the portal has been activated.
// If this document was adopted by it, then window.portalHost will exist.
portal.activate().then(() => {
  // Check if this document was adopted into a portal element.
  if (window.portalHost) {
    // You can start communicating with the portal element
    // i.e. listen to messages
    window.portalHost.addEventListener('message', (evt) => {
      // handle the event
    });
  }
});

ポータルでサポートされているすべての機能を組み合わせることで、洗練されたユーザー エクスペリエンスを構築できます。たとえば、以下のデモは、ポータルによって、ウェブサイトとサードパーティの埋め込みコンテンツとの間でシームレスなユーザー エクスペリエンスを実現する方法を示しています。

ユースケースと計画

このポータルの簡単なツアーはいかがでしたでしょうか。皆様からのご応募を心よりお待ちしております。たとえば、商品カテゴリ一覧ページからベストセラー商品のページをプリレンダリングするなど、複雑なナビゲーションにはポータルの使用を開始できます。

知っておくべきもう一つの重要な点は、<iframe> と同様に、ポータルはクロスオリジン ナビゲーションで使用できるということです。したがって、相互を参照する複数のウェブサイトがある場合は、ポータルを使用して、2 つの異なるウェブサイト間をシームレスに移動することもできます。クロスオリジンのユースケースはポータルに特有で、SPA のユーザー エクスペリエンスも向上させることができます。

フィードバックをお待ちしています

ポータルは Chrome 85 以降のバージョンでテストできます。新しい API を設計するには、コミュニティからのフィードバックが不可欠です。ぜひお試しいただき、ご意見をお寄せください。機能に関するリクエストやフィードバックは、WICG GitHub リポジトリまでお寄せください。