[英]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 圖像具有絕對width
和height
,則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.