ホームページ >ウェブフロントエンド >jsチュートリアル >Service Worker による動的なイメージ作成
Service Worker は素晴らしいテクノロジーです。プログレッシブ Web アプリケーション (PWA) という用語に関連してこれらのことを知っているかもしれません。ブラウザ上で通常表示されるものを OS に「インストール」し、ネイティブ アプリケーションのように開いたり、ネイティブ アプリケーションのようにアンインストールしたりできます。見た目はネイティブ アプリケーションのように見えます。しかし、サービスワーカーはそれ以上のことができます。
アクセシビリティと説明については、ここをご覧ください。
Service Worker は基本的に共有 Web ワーカー (ちなみに、別のテクノロジーとして存在します) であり、同じ スコープ内の URL からブラウザによって行われたすべての http リクエスト をインターセプトする特別な機能を備えています。 (元のパス) ワーカーが登録されています。次に、構築された応答またはキャッシュされた応答で応答する (実際にはブラウザがリクエストでネットワークにアクセスするのを防ぐ) か、通常どおり、または (フェッチを使用して) リクエストを変更することによってリクエストをネットワークに渡すように指示される可能性があります。
そうは言っても、Service Worker が オフライン のときに Web ページにアクセスする機能と関連付けられることが多いのは明らかです。つまり、初めてすべての静的リソースをダウンロードしてキャッシュすることができます (これは基本的に「インストール」されます)。ページ) にアクセスすると、Service Worker はキャッシュされたバージョンを使用して同じリクエストに応答でき、基本的にはネイティブ アプリであるかのように「アプリケーション リソース」を提供します。 dev.to はその好例です。
これはすでに単純化されており、キャッシュ無効化、更新、その他についてはこの記事の範囲外なので、ここでは触れません。私がお話しするのは、構築された応答を提供するサービス ワーカーの能力です。
私のチームは最近、「ショーケース」アプリケーション、つまり基本的には何もしないが、デザイン システムに従って Web コンポーネント UI キットの使用方法を示す目的を持つ Web アプリケーションを構築する任務を負っていました。およびコーディング ガイドライン。
アプリケーションは純粋にフロントエンド アプリケーションとして意図されていました (つまり、バックエンドも開発する予定ではなかったということです) が、お客様がバックエンドなどを含めて保守している多くの B2B アプリケーションの 1 つと 似ている はずです。 。そこで役立つのがサービスワーカーの役割です。
テキスト形式の応答で応答するのは非常に簡単です。 JSON であっても基本的にはテキストなので、最終的に Service Worker は次のようになります:
self.addEventListener('fetch', event => { if (event.request.url.includes('/api/hello')) { event.respondWith(new Response( JSON.stringify({ message: 'Hello!' }), { headers: { 'Content-Type': 'application/json' }} ); } else { event.respondWith(fetch(event.request)); } });
このスニペットをどのように改善できるかについては、退屈するつもりはありません。 URL マッチングには URLPattern を使用できます。静的データをフェッチでロードし、IndexedDB に保存できます。これには夢中になっても構いません。
しかし、他の種類の動的応答についてはどうでしょうか?画像が好きですか?
動的な画像を生成する最も簡単な方法は、基本的に XML ドキュメントである SVG を作成することです。つまり、テキストです。これは完全に実現可能なタスクであり、D3.js などのライブラリを使用して SVG 要素とパスを生成できます。 line() などのファクトリーは、
self.addEventListener('fetch', event => { if (event.request.url.includes('/api/hello')) { event.respondWith(new Response( JSON.stringify({ message: 'Hello!' }), { headers: { 'Content-Type': 'application/json' }} ); } else { event.respondWith(fetch(event.request)); } });
SVG を動的に生成すると、タスクをメインスレッドから切り離すのに最適であり、結果をキャッシュすることもできます。これはチャートやインフォグラフィックに最適で、「簡単」に実現できます。
さらに注意が必要なのは、PNG や JPG などの ラスター 画像を生成することです。 「生成」とは、編集ツールを使用して画像を変更したり、最初から作成したりすることを意味します。このような場合に通常行うのは、
問題は、Service Worker が DOM 要素にアクセスできないことです。では、運が悪いのでしょうか?
心配しないでください、友達の皆さん!すべてのワーカー (サービス ワーカーを含む) が OffscreenCanvas オブジェクトを作成できるためです。コンストラクターに幅と高さをピクセル単位で指定すると、Service Worker に完全に細かい (ただし目に見えない) キャンバスが完成します。
import { pie, arc } from 'd3-shape'; const pieData = pie().sort(null)(data); const sectorArc = arc().outerRadius(35).innerRadius(20); const svg = '<svg viewBox="-40 -40 80 80" xmlns="http://www.w3.org/2000/svg">' + pieData.map((pie, index) => `<path d="${sectorArc(pie)}" fill="${colors[index]}"/>` ).join('') + '</svg>'; event.respondWith(new Response( svg, { headers: { 'Content-Type': 'image/svg+xml' }} ));
疑問に思っている人のために: はい、さまざまなタイプのコンテキストを取得できますが、すべてのコンテキストがすべてのブラウザで利用できるわけではありません。 three.js のようなライブラリを使用して、Service Worker で 3D シーンを生成してみることができます (後で試してみようと思います)。
これで、基本的に何でもできるようになりました。線、円弧、パスなどを描画し、キャンバスのジオメトリを変更することもできます。これは DOM キャンバス コンテキストに描画するのと同じくらい簡単なので、この部分には詳しく説明しません。
確かにテキストも書くことができます。他の環境、つまりペイント ワークレットではそれができないため、これは重要です。
注: PaintRenderingContext2D は CanvasRenderingContext2D API のサブセットを実装します。具体的には、CanvasImageData、CanvasUserInterface、CanvasText、または CanvasTextDrawingStyles API は実装されていません。
しかし、Service Worker ではこれで大丈夫です。これは、背景画像を生成するための、より強力な (ただしパフォーマンスは低い) 環境があることを意味します。
テキストの描画は次のように簡単です:
const canvas = new OffscreenCanvas(800, 600); const context = canvas.getContext('2d');
ここでは好きなフォントを使用できますが、サンセリフ、等幅、システム UI などの通常の標準値は、すべて次の値に戻ってしまうため、機能しないことがわかりました。デフォルトのセリフフォント。ただし、フォント スタックは通常どおり使用できます。
context.fillStyle = '#222'; context.font = '24px serif'; // (x, y) = (50, 90) will be the *bottom left* corner of the text context.fillText('Hello, world!', 50, 90);
さらに、フォント読み込み API を使用して、外部リソースからフォントを読み込むことができます。
self.addEventListener('fetch', event => { if (event.request.url.includes('/api/hello')) { event.respondWith(new Response( JSON.stringify({ message: 'Hello!' }), { headers: { 'Content-Type': 'application/json' }} ); } else { event.respondWith(fetch(event.request)); } });
応答を送り返すのは、ご想像のとおり、Blob の Promise を返す ConvertToBlob メソッドを呼び出すだけで簡単です。また、BLOB は送信者に簡単に返送できます。
import { pie, arc } from 'd3-shape'; const pieData = pie().sort(null)(data); const sectorArc = arc().outerRadius(35).innerRadius(20); const svg = '<svg viewBox="-40 -40 80 80" xmlns="http://www.w3.org/2000/svg">' + pieData.map((pie, index) => `<path d="${sectorArc(pie)}" fill="${colors[index]}"/>` ).join('') + '</svg>'; event.respondWith(new Response( svg, { headers: { 'Content-Type': 'image/svg+xml' }} ));
このメソッドはデフォルトで PNG 画像を作成しますが、上記のように、代わりに JPG ファイルを作成するように指示される場合もあります。 「image/webp」も一般的な形式ですが、Safari はこれをサポートしていません。正直に言うと、新しく利用可能になり、より高性能な画像フォーマット デコーダー は、対応する エンコーダー に反映されないため、ここでの選択は少し説得力に欠けます。しかし、ほとんどの目的にはこれで十分です。
面白い事実: ConvertToBlob メソッドは OffscreenCanvas クラスに固有です。 HTMLCanvasElements には代わりに toBlob があり、Promise 以前の一般的な非同期タスク処理スタイルでコールバックを最初の引数として受け取ります。
これで、最初から画像を作成する場合はすべて機能します。しかし、空のテンプレートから始めたい場合はどうすればよいでしょうか?
メインスレッドで作業する場合は、2D コンテキストのdrawImage メソッドを使用してコンテキスト内で画像をスラップし、画像を取得できます。すぐに利用できる から要素。
問題は、やはり DOM にアクセスできないため、 を参照できないことです。要素。私たちができることは、代わりに、背景として必要な画像をフェッチし、その Blob を取得して、それをdrawImage が消化できる別のものに変換することです。 Service Worker でも使用できるグローバル メソッドである createImageBitmap を入力します。これは、フロントエンド Web 開発のあまり知られていないクラスの 1 つである ImageBitmap インスタンスの Promise を返します。これは明らかに WebGL コンテキストでより広く使用されていますが、drawImage はそれを受け入れているようですので...
const canvas = new OffscreenCanvas(800, 600); const context = canvas.getContext('2d');
この時点から、落書きやテキストを描画して、満足のいく動的な画像を作成してユーザーに送り返すことができます。
注: これは、
を使用するだけなので、SVG を使用するとより簡単に解決できます。背景画像を設定する要素。しかし、それはブラウザが生成された画像が送信された後に画像を読み込む必要があることを意味しますが、この手法ではこれが前に行われます。フォントを選択するときにも同様のことが当てはまります。
これらすべての例で、module サービス ワーカーを使用しました (つまり、他の ES モジュールからのインポートを使用しました)。残念ながら、モジュール サービス ワーカーは Firefox ではまだサポートされていませんが、すぐにサポートされるようになるでしょう。それまでの間、代わりに古い importScript を使用するようにコードを調整する必要がある場合があります。
import または importScripts を介して他のスクリプトを Service Worker にインポートするときは、インポートされたファイルが変更されたときにブラウザが updatefound イベントを発生しないことを覚えておいてください。このイベントは、次の場合にのみ発生します。 Service Worker エントリ スクリプトが変更されます。
私たちのようなケースでは、Service Worker がバックエンドの存在を模擬するためだけに必要な場合、install イベントが発生した直後に self.skipWaiting() を呼び出し、その後 self を呼び出すことでライフサイクルを短縮できます。リクエストにすぐに応答できるようにするために、activate イベントで client.claim() を実行します (そうしないと、次のページ更新時にのみ開始されます)。
self.addEventListener('fetch', event => { if (event.request.url.includes('/api/hello')) { event.respondWith(new Response( JSON.stringify({ message: 'Hello!' }), { headers: { 'Content-Type': 'application/json' }} ); } else { event.respondWith(fetch(event.request)); } });
これが基本的にすべてですので...サービス ワーカーの皆さん、楽しんでください!
以上がService Worker による動的なイメージ作成の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。