如何在Service Worker的OffscreenCanvas中使用SVG生成动态Action图标?
在Chrome扩展Service Worker的OffscreenCanvas中使用SVG图片的解决方案
问题背景
使用PNG图片时动态生成Action图标的代码运行正常,但替换为SVG后,createImageBitmap()抛出错误:
DOMException: The source image could not be decoded
相关代码及配置如下:
原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)}); });
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>
解决方案
Service Worker环境中createImageBitmap无法直接解码SVG Blob,可通过以下两种方法解决:
方法一:将SVG转为Data URL后用Image对象加载
先把SVG文本转为Data URL,通过Image对象完成渲染后再绘制到OffscreenCanvas:
const iconFile = `icon.svg`; const canvas = new OffscreenCanvas(24, 24), ctx = canvas.getContext("2d"); fetch(iconFile) .then(r => r.text()) .then(svgText => { const svgDataUrl = `data:image/svg+xml;base64,${btoa(svgText)}`; const img = new Image(); return new Promise((resolve, reject) => { img.onload = () => resolve(img); img.onerror = reject; img.src = svgDataUrl; }); }) .then(img => { ctx.drawImage(img, 0, 0, canvas.width, canvas.height); ctx.fillStyle = 'lightgreen'; ctx.fillRect(0, canvas.height-9, canvas.width, 9); return chrome.action.setIcon({imageData: ctx.getImageData(0, 0, 24, 24)}); }) .catch(err => console.error(err));
方法二:通过Blob URL中转后生成ImageBitmap
创建Blob URL让Image对象加载SVG,再基于加载完成的Image生成ImageBitmap:
const iconFile = `icon.svg`; const canvas = new OffscreenCanvas(24, 24), ctx = canvas.getContext("2d"); fetch(iconFile) .then(r => r.blob()) .then(blob => { const url = URL.createObjectURL(blob); const img = new Image(); return new Promise((resolve, reject) => { img.onload = () => { URL.revokeObjectURL(url); createImageBitmap(img).then(resolve).catch(reject); }; img.onerror = reject; img.src = url; }); }) .then(imgBitmap => { ctx.drawImage(imgBitmap, 0, 0); ctx.fillStyle = 'lightgreen'; ctx.fillRect(0, canvas.height-9, canvas.width, 9); return chrome.action.setIcon({imageData: ctx.getImageData(0, 0, 24, 24)}); }) .catch(err => console.error(err));
原理说明
Service Worker中的createImageBitmap不支持直接解析SVG Blob,因为SVG需要先经过浏览器的渲染流程转为位图。通过Image对象加载SVG(Data URL或Blob URL形式),浏览器会完成SVG的解析渲染,生成可用于Canvas绘制的图像源,从而解决解码失败的问题。
内容的提问来源于stack exchange,提问作者vanowm
相关产品推荐
相关产品推荐

