简体   繁体   English

从 svg 图像生成操作图标?

[英]Generate action icon from svg image?

I'm trying make my action icon dynamic by manipulating it with offscreenCanvas() :我正在尝试通过使用offscreenCanvas()操作来使我的动作图标动态化:

service_worker.js 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)});
});

This works fine, however when I attempt use a SVG image instead of PNG , the createImageBitmap() returns error:这工作正常,但是当我尝试使用SVG图像而不是PNG时, createImageBitmap()返回错误:

DOMException: The source image could not be decoded

manifest.json 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

<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>

Any suggestions how to use SVG image in offscreenCanvas in service workers?有什么建议如何在服务人员的offscreenCanvas中使用SVG图像?

If your the SVG images in your Blobs have absolute width and height , they should be supported by createImageBitmap natively.如果您的 Blob 中的 SVG 图像具有绝对widthheight ,则createImageBitmap应该原生支持它们。 However, currently no browser supports this feature, and while I made a polyfill for the main thread, it won't work in a Worker where implementers are very reluctant to add an SVG parser.然而,目前没有浏览器支持这个功能,虽然我为主线程做了一个polyfill ,但它在实现者非常不愿意添加 SVG 解析器的 Worker 中不起作用。

So one could have hoped that injecting some scripts into the current tab would have made it.因此,人们可能希望将一些脚本注入到当前选项卡中就可以了。

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

But Chrome extensions still use JSON serialization for their various messaging APIs ( BUG 248548 ) and there is thus no way (that I found) of transferring the ImageBitmap from one context to the other in MV3.但是 Chrome 扩展程序仍然使用 JSON 序列化来进行各种消息传递 API( BUG 248548 ),因此(我发现)无法在 MV3 中将 ImageBitmap 从一个上下文传输到另一个上下文。


So you will have to parse the SVG image yourself and convert it to Canvas2D commands.因此,您必须自己解析 SVG 图像并将其转换为 Canvas2D 命令。
For this task, a library like canvg may come handy.对于这个任务,像canvg这样的库可能会派上用场。 They're in the area for a long time, seem to do a pretty good job, and do work in Workers... after some tweaks.他们在该地区待了很长时间,似乎做得很好,并且在工人中工作……经过一些调整。
They only provide an UMD version of their library, so you'd have to build it to ESM yourself, and also bring a DOMParser implementation (canvg uses xmldom, you can make your build export it too).他们只提供其库的 UMD 版本,因此您必须自己将其构建到 ESM,并带来DOMParser实现(canvg 使用 xmldom,您也可以将构建导出它)。
Then, you have to declare your background script as a module and you can import it all in there:然后,您必须将您的后台脚本声明为一个模块,并且您可以将其全部导入其中:

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
}

But if you don't want to do all these builds, and add the 300KB of lib necessary for all this, AND if you have only icons as simple as the one in your example, which consist of only <path> elements, you can certainly come up with your own parser, or even avoid the SVG format entirely and instead store the data as JSON.但是,如果您不想进行所有这些构建,并添加所有这些所需的 300KB 库,并且如果您只有与示例中的图标一样简单的图标,其中仅包含<path>元素,您可以当然想出你自己的解析器,甚至完全避免 SVG 格式,而是将数据存储为 JSON。 The Path2D constructor can take an SVG path declaration string as input, which allows us to generate simple assets in JSON format: 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>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM