我正试图通过使用offscreenCanvas()
:来操作我的动作图标,使其具有动态性
service_worker.js
const iconFile = `icon.png`;
const canvas = new OffscreenCanvas(24, 24),
ctx = canvas.getContext("2d");
fetch(iconFile)
.then(r => r.blob())
.then(createImageBitmap)
.then(img => ctx.drawImage(img, 0, 0))
.then(() =>
{
ctx.fillStyle = 'lightgreen';
ctx.fillRect(0, canvas.height-9, canvas.width, 9);
chrome.action.setIcon({imageData: ctx.getImageData(0, 0, 24, 24)});
});
这很好,但是当我尝试使用SVG
图像而不是PNG
时,createImageBitmap()
返回错误:
DOMException: The source image could not be decoded
manifest.json
{
"manifest_version": 3,
"name": "Test extension",
"author": "test",
"description": "Test",
"version": "0.0.1",
"permissions":[],
"action": {},
"background": {
"service_worker": "service_worker.js"
}
}
icon.svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="#00864b" d="M8.1 18.8.7 11.4l2.1-2.2 5.3 5.3L21.4 1.2l2.2 2.1z"/></svg>
如何在服务工作者中使用offscreenCanvas
中的SVG
图像有什么建议吗?
如果Blob中的SVG图像具有绝对width
和height
,则它们应该由createImageBitmap
本地支持。然而,目前没有浏览器支持此功能,虽然我为主线程制作了polyfill,但它在Worker中不起作用,因为Worker中的实现者非常不愿意添加SVG解析器。
因此,人们本可以希望在当前选项卡中注入一些脚本会成功
async function execScript() {
const [tab] = await chrome.tabs.query({active: true, currentWindow: true});
const bmp = await chrome.scripting.executeScript({
target: {tabId: tab.id},
func: grabImageBitmap,
args: [the_url]
});
const canvas = new OffscreenCanvas(bmp.width, bmp.height);
// ... do whatever with the bitmap
}
function grabImageBitmap(url) {
const img = new Image();
img.src = url;
// we can't use img.decode() here, doesn't work with SVG...
return new Promise((res, rej) => {
img.onload = async (evt) => {
const bmp = await createImageBitmap(img);
res(bmp);
};
img.onerror = rej;
});
}
但是Chrome扩展仍然对其各种消息API(BUG 248548(使用JSON序列化,因此(我发现(无法在MV3中将ImageBitmap从一个上下文传输到另一个上下文。
因此,您必须自己解析SVG图像并将其转换为Canvas2D命令
对于这个任务,像canvg这样的库可能会派上用场。他们在这个地区待了很长时间,似乎做得很好,在工人。。。经过一些调整
他们只提供其库的UMD版本,因此您必须自己将其构建到ESM,并带来DOMParser
实现(canvg使用xmldom,您也可以使您的构建导出它(
然后,您必须将后台脚本声明为一个模块,然后您可以将其全部导入其中:
import { Canvg, presets, DOMParser } from "your_custom_build.js";
const preset = presets.offscreen({ DOMParser });
async function execScript() {
const req = await fetch(the_url);
const markup = req.ok && await req.text();
const canvas = new OffscreenCanvas(width, height);
const ctx = canvas.getContext("2d");
const renderer = await Canvg.from(ctx, markup, preset);
renderer.render();
// The SVG image has been drawn on your context
}
但是,如果您不想完成所有这些构建,并添加所有这些所需的300KB的lib,并且如果您只有像示例中那样简单的图标,只由<path>
元素组成,那么您当然可以使用自己的解析器,甚至完全避免SVG格式,而是将数据存储为JSON。Path2D
构造函数可以采用SVG路径声明字符串作为输入,这允许我们生成JSON格式的简单资产:
(async () => {
// You'd use the OffscreenCanvas() constructor in background.js
const ctx = document.querySelector("canvas").getContext("2d");
// You'd use an actual URL to a JSON file
const JSONURL = `data:application/json,${
encodeURIComponent(JSON.stringify(
[
{
"d": "M8.1 18.8.7 11.4l2.1-2.2 5.3 5.3L21.4 1.2l2.2 2.1z",
"fill": "#00864b",
"transform": "matrix(2, 0, 0, 2, 20, 20)",
"stroke": "red",
"strokeWidth": 3
},
// Add more if needed
]
))
}`;
const resp = await fetch(JSONURL);
const pathes = resp.ok && await resp.json();
for (const path of pathes) {
const pathObject = new Path2D(path.d);
if (path.transform) { // as a CSSMatrixFunction
ctx.setTransform(new DOMMatrix(path.transform));
}
if (path.stroke) {
ctx.lineWidth = path.strokeWidth ?? 1;
ctx.strokeStyle = path.stroke;
ctx.stroke(pathObject);
}
if (path.fill) {
ctx.fillStyle = path.fill;
ctx.fill(pathObject);
}
// Add more features if wanted
}
})().catch(console.error);
<canvas></canvas>