簡體   English   中英

從 svg 圖像生成操作圖標?

[英]Generate action icon from svg image?

我正在嘗試通過使用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"
  }
}

圖標.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 圖像具有絕對widthheight ,則createImageBitmap應該原生支持它們。 然而,目前沒有瀏覽器支持這個功能,雖然我為主線程做了一個polyfill ,但它在實現者非常不願意添加 SVG 解析器的 Worker 中不起作用。

因此,人們可能希望將一些腳本注入到當前選項卡中就可以了。

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 擴展程序仍然使用 JSON 序列化來進行各種消息傳遞 API( BUG 248548 ),因此(我發現)無法在 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 庫,並且如果您只有與示例中的圖標一樣簡單的圖標,其中僅包含<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>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM