首页 >web前端 >js教程 >使用 Service Worker 创建动态图像

使用 Service Worker 创建动态图像

Patricia Arquette
Patricia Arquette原创
2024-11-19 18:00:04248浏览

Service Workers 是一项非常棒的技术。您可能知道它们与术语渐进式 Web 应用程序 (PWA) 相关,因此通常在浏览器上可见的内容可以“安装”在操作系统中,并且可以像本机应用程序一样打开,并像本机应用程序一样卸载,看起来就像一个本机应用程序。但服务人员能做的远不止这些。

Dynamic image creation with service workers

有关可访问性和解释,请查看此处。

Service Worker 基本上是共享的 Web Worker(顺便说一句,它作为一种单独的技术存在),具有拦截浏览器从同一 范围内的 URL 发出的所有 http 请求 的特殊能力(原始路径)worker 已注册。然后,可以指示它使用构造的或缓存的响应进行响应 - 实际上阻止浏览器通过请求访问网络 - 或者正常地将请求传递到网络或通过修改请求(使用 fetch)。

这就是说,很明显为什么 Service Worker 通常与离线时访问网页的能力相关联:第一次可以下载并缓存所有静态资源(基本上是“安装”)页面),然后 Service Worker 可以使用缓存版本响应相同的请求,基本上像本机应用程序一样提供“应用程序资源”。 dev.to 就是一个很好的例子。

这已经是一个简化,谈论缓存清除、更新和其余内容超出了本文的范围,所以我不会沉迷于此。我要讨论的是 Service Worker 提供构建响应的能力。

嘲笑回应

我的团队最近的任务是构建一个“展示”应用程序,即一个基本上不执行任何操作的 Web 应用程序,但目的是展示如何按照设计系统使用我们的 Web 组件 UI 套件以及编码指南。

该应用程序旨在作为纯粹的前端应用程序(意味着我们不应该也开发后端),但应该

看起来像我们的客户维护的众多 B2B 应用程序之一,具有后端和所有功能。这时候 Service Worker 的角色就派上用场了。

现在,通过文字回复非常简单。即使 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。您可以使用 fetch 加载静态数据并将其存储在 IndexedDB 上。你可以为此发疯。

但是其他类型的动态响应呢?喜欢图片吗?

生成图像:“简单”的方式。

生成动态图像的最简单方法是创建 SVG,它基本上是一个 XML 文档。意思是,它是文本。这是一项完全可行的任务,您可以使用 D3.js 等库为您生成 SVG 元素和路径:像 line() 和其他工厂这样的工厂返回函数,这些函数返回您需要放入 的 d 属性中的内容。元素:

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。 “生成”是指使用编辑工具来改变图片或从头开始创建它。在这些情况下我们通常做的是使用 。元素,获取其 2d 上下文并开始使用其许多绘图指令在其上绘画。

问题是,Service Worker 无法访问 DOM 元素。那么,我们运气不好吗?

不用担心,我的朋友们!因为所有worker(包括serviceworker)都可以创建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 画布上下文上绘图一样简单,所以我不会沉迷于这部分。

绘图文字

我们确实也可以写文字。这很重要,因为在其他环境中 - 即 Paint 工作集,我们不能这样做:

注意:PaintRenderingContext2D 实现了 CanvasRenderingContext2D API 的子集。具体来说,它没有实现 CanvasImageData、CanvasUserInterface、CanvasText 或 CanvasTextDrawingStyles API。

但对于 Service Worker 来说,这一切都很好。这意味着我们有一个更强大(尽管性能较差)的环境来生成背景图像。

绘制文本就这么简单:

const canvas = new OffscreenCanvas(800, 600);
const context = canvas.getContext('2d');

可以使用你喜欢的字体,但我发现通常的标准值,如 sans-serif、等宽字体或 system-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));
  }
});

发送回应用程序

发回响应就像调用 ConvertToBlob 方法一样简单,该方法返回一个 Blob(你猜对了)的承诺。并且 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 方法在上下文中绘制图片,例如获取它来自现成的 使用 Service Worker 创建动态图像元素。

问题再次出现,我们无法访问 DOM,因此无法引用 使用 Service Worker 创建动态图像元素。相反,我们可以做的是,它获取我们需要作为背景的图片,获取其Blob,然后将其转换为drawImage可以消化的其他内容。输入 createImageBitmap,这是一个在 Service Worker 中也可用的全局方法。它返回一个 ImageBitmap 实例的承诺,这是前端 Web 开发中许多鲜为人知的类之一。它显然在 WebGL 上下文中使用更广泛,但 drawImage 似乎接受它,所以...

const canvas = new OffscreenCanvas(800, 600);
const context = canvas.getContext('2d');

从现在开始,我们可以继续在其上绘制涂鸦和文本,创建令人满意的同步图像并发回给用户。

注意:使用 SVG 可以更轻松地解决这个问题,因为您可以只使用 元素设置背景图片。但这意味着浏览器必须在发送生成的图像之后加载图片,而使用这种技术,这是在之前完成的。选择字体时也有类似的情况。

将所有内容放在一起

在所有这些示例中,我使用了模块服务工作者(即我使用了从其他ES模块导入)。遗憾的是,Firefox 尚不支持模块服务工作者,但希望很快就会支持。与此同时,您可能需要调整代码以使用旧的 importScripts。

通过 import 或 importScripts 将其他脚本导入服务工作人员时,请记住,当导入的文件发生更改时,浏览器将不会触发 updatefound 事件:它在以下情况下触发Service Worker 入口脚本发生变化。

在像我们这样的情况下,服务工作者只需要模拟后端的存在,它的生命周期可以通过在安装事件触发时立即调用 self.skipWaiting() 来缩短,然后调用 self.在激活事件上调用clients.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中文网其他相关文章!

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn