无法将SVG标记以Data URI形式在图片源中渲染,且无法在Canvas中为Google Material Symbols应用FILL字体变体设置
解决Google Material Symbols在Canvas中应用FILL变体并下载的问题
你遇到的两个核心问题:一是SVG转Image时触发onerror(看似有效的Data URI实际因为SVG结构问题无法被解析),二是font-variation-settings的fill属性无法生效。下面是针对性的分步解决方案:
问题1:Image加载报错的根源
你直接将HTML元素(icon的克隆)放入foreignObject,但SVG的foreignObject要求内部HTML内容必须放在带有HTML命名空间的容器中,否则浏览器无法正确解析SVG中的HTML片段,导致SVG无效,最终触发Image加载失败。
问题2:FILL变体不生效的解决
需要在Material Symbols的样式中显式添加font-variation-settings: 'FILL' 1;(值为1是填充状态,0是轮廓状态),同时要确保字体加载完成后再生成SVG,避免因字体未加载导致样式失效。
修正后的完整代码
// 先确保Material Symbols字体加载完成,避免样式不生效 async function loadMaterialSymbolsAndExport() { // 用FontFace API预加载字体 const fontFace = new FontFace( 'Material Symbols Outlined', 'url(https://fonts.gstatic.com/s/materialsymbolsoutlined/v315/kJEhBvYX7BgnkSrUwT8OhrdQw4oELdPIeeII9v6oFsI.woff2)' ); await fontFace.load(); document.fonts.add(fontFace); // 获取页面上的Material图标元素(请确保你的图标带有这个类) const icon = document.querySelector('.material-symbols-outlined'); const { width, height } = icon.getBoundingClientRect(); // 创建Canvas和SVG相关元素 const canvas = document.createElement("canvas"); const svgNS = 'https://www.w3.org/2000/svg'; const svg = document.createElementNS(svgNS, "svg"); const style = document.createElementNS(svgNS, "style"); // 样式中添加FILL变体控制 style.textContent = `@font-face { font-family: 'Material Symbols Outlined'; font-style: normal; font-weight: 100 700; src: url('https://fonts.gstatic.com/s/materialsymbolsoutlined/v315/kJEhBvYX7BgnkSrUwT8OhrdQw4oELdPIeeII9v6oFsI.woff2') format('woff2'); } .material-symbols-outlined { font-family: 'Material Symbols Outlined'; font-weight: normal; font-style: normal; font-size: 24px; display: inline-block; line-height: 1; text-transform: none; letter-spacing: normal; word-wrap: normal; white-space: nowrap; direction: ltr; font-variation-settings: 'FILL' 1; /* 关键:启用填充变体,改为0可切换回轮廓 */ }`; svg.append(style); // 创建foreignObject并添加HTML命名空间容器 const foreignObject = document.createElementNS(svgNS, "foreignObject"); foreignObject.setAttribute("x", '0'); foreignObject.setAttribute("y", '0'); foreignObject.setAttribute("width", `${width}`); foreignObject.setAttribute("height", `${height}`); // 必须创建带有HTML命名空间的容器,否则foreignObject无法解析内部HTML const htmlContainer = document.createElementNS('http://www.w3.org/1999/xhtml', 'div'); const iconClone = icon.cloneNode(true); htmlContainer.append(iconClone); foreignObject.append(htmlContainer); // 设置SVG尺寸并组装 svg.setAttribute("width", `${width}`); svg.setAttribute("height", `${height}`); svg.setAttribute("xmlns", svgNS); // 显式声明SVG命名空间 svg.append(foreignObject); // 生成Blob URL替代Data URI,避免编码问题 const svgMarkup = new XMLSerializer().serializeToString(svg); const svgBlob = new Blob([svgMarkup], { type: "image/svg+xml" }); const svgURL = URL.createObjectURL(svgBlob); // 加载SVG到Image并绘制到Canvas const img = new Image(); img.onload = () => { console.log('图标加载完成,开始绘制到Canvas'); // 设置Canvas尺寸并绘制 canvas.width = width; canvas.height = height; const ctx = canvas.getContext("2d"); ctx.drawImage(img, 0, 0); // 触发下载 const downloadLink = document.createElement('a'); downloadLink.download = 'material-icon.png'; downloadLink.href = canvas.toDataURL('image/png'); downloadLink.click(); // 释放Blob URL资源 URL.revokeObjectURL(svgURL); }; img.onerror = (error) => { console.error('图片加载失败:', error); URL.revokeObjectURL(svgURL); }; img.src = svgURL; } // 调用执行 loadMaterialSymbolsAndExport();
关键修改点说明
- 字体预加载:使用
FontFace API确保字体完全加载后再生成SVG,避免样式失效。 - HTML命名空间容器:在
foreignObject中添加了带有http://www.w3.org/1999/xhtml命名空间的div,解决HTML内容解析失败的问题。 - 显式控制FILL变体:在样式中添加
font-variation-settings: 'FILL' 1;,直接启用填充效果,修改数值即可切换状态。 - Blob URL替代Data URI:避免Data URI的编码限制和解析问题,同时记得在操作完成后释放URL资源。
额外提示
如果需要动态切换填充/轮廓状态,只需修改font-variation-settings中的FILL值,重新执行生成和下载逻辑即可。另外,请确保你的icon元素确实带有.material-symbols-outlined类,且包含正确的Material Symbols字符(比如<span class="material-symbols-outlined">home</span>)。
内容的提问来源于stack exchange,提问作者Tyler Dahle
相关产品推荐
相关产品推荐

