无法修改SVG源码时,如何调整Canvas中SVG的颜色?
问题:无法修改SVG源码时,如何在Canvas中将黑色SVG转为白色显示?
我有一段无法修改源码的SVG:
<?xml version="1.0" encoding="UTF-8"?> <svg id="shape" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"> <path d="M29.57,19.75l-3.5-13.59c-.12-.52-.45-.97-.89-1.23-2.03-1.25-4.19-1.28-6.22-.08-.59,.35-.96,1-.96,1.71v3.45h-4v-3.45c0-.71-.37-1.36-.96-1.71-2.04-1.19-4.19-1.16-6.22,.08-.44,.27-.76,.72-.88,1.22l-3.51,13.61h0c-.28,.7-.44,1.45-.44,2.25,0,3.31,2.69,6,6,6s6-2.69,6-6v-2h4v2c0,3.31,2.69,6,6,6s6-2.69,6-6c0-.79-.16-1.55-.44-2.25h0Zm-5.44-13.11l2.58,10.01c-.81-.41-1.73-.65-2.7-.65-1.54,0-2.94,.59-4.01,1.55l-.02-10.98c1.42-.83,2.74-.81,4.16,.08Zm-16.26-.01c1.42-.87,2.75-.89,4.14-.08v10.99c-1.06-.95-2.46-1.54-4-1.54-.98,0-1.89,.24-2.71,.65L7.86,6.63Zm.14,19.37c-2.21,0-4-1.79-4-4s1.79-4,4-4,4,1.79,4,4-1.79,4-4,4Zm6-8v-6h4v6h-4Zm10,8c-2.21,0-4-1.79-4-4s1.79-4,4-4,4,1.79,4,4-1.79,4-4,4Z"/> </svg>
我需要将该SVG居中放置在带方形背景色的Canvas中,且要把默认黑色的SVG改为白色。当前React实现代码如下:
export const useRenderIcon = ( previewCanvas: React.RefObject<HTMLCanvasElement>, selectedColor: Color, selectedSVG: Svg ): void => { useEffect(() => { if (previewCanvas.current) { const ctx = previewCanvas.current.getContext("2d"); if (ctx) { previewCanvas.current.style.width = `${PREVIEW_CANVAS_SIZE}px`; previewCanvas.current.style.height = `${PREVIEW_CANVAS_SIZE}px`; // draw icon background color ctx.fillStyle = selectedColor.color; ctx.fillRect(0, 0, previewCanvas.current.width, previewCanvas.current.height); // draw icon svg const offset = previewCanvas.current.width - PREVIEW_SVG_SIZE; // calculate offset to center icon to middle of canvas const svgIconImage = new Image(); svgIconImage.setAttribute("src", selectedSVG.uri); svgIconImage.setAttribute("width", `${PREVIEW_SVG_SIZE}`); // trying to make it white svgIconImage.style.fill = "white"; svgIconImage.style.stroke = "white"; svgIconImage.onload = () => { // trying to make it white svgIconImage.style.fill = "white"; svgIconImage.style.stroke = "white"; ctx.imageSmoothingEnabled = false; ctx.drawImage(svgIconImage, offset / 2, offset / 2, PREVIEW_SVG_SIZE, PREVIEW_SVG_SIZE); }; } } }, [previewCanvas, selectedColor, selectedSVG]); };
但设置svgIconImage.style.fill = "white"和svgIconImage.style.stroke = "white"没有效果,SVG在Canvas中仍显示为黑色,请问该如何解决?
解决方案
方法一:使用Canvas全局合成模式实现颜色替换
原理是先绘制白色矩形覆盖SVG区域,再通过合成模式只保留SVG形状与白色矩形的重叠部分,最终得到白色的SVG图案。
修改后的代码:
export const useRenderIcon = ( previewCanvas: React.RefObject<HTMLCanvasElement>, selectedColor: Color, selectedSVG: Svg ): void => { useEffect(() => { if (previewCanvas.current) { const ctx = previewCanvas.current.getContext("2d"); const canvas = previewCanvas.current; if (ctx) { // 同步Canvas实际尺寸与样式尺寸,避免拉伸变形 canvas.width = PREVIEW_CANVAS_SIZE; canvas.height = PREVIEW_CANVAS_SIZE; canvas.style.width = `${PREVIEW_CANVAS_SIZE}px`; canvas.style.height = `${PREVIEW_CANVAS_SIZE}px`; // 绘制背景色 ctx.fillStyle = selectedColor.color; ctx.fillRect(0, 0, canvas.width, canvas.height); const offset = canvas.width - PREVIEW_SVG_SIZE; const svgX = offset / 2; const svgY = offset / 2; const svgIconImage = new Image(); // 跨域SVG需设置此属性,避免后续操作报错 svgIconImage.crossOrigin = "anonymous"; svgIconImage.src = selectedSVG.uri; svgIconImage.onload = () => { ctx.imageSmoothingEnabled = false; // 1. 绘制白色矩形,覆盖SVG的位置和大小 ctx.fillStyle = "white"; ctx.fillRect(svgX, svgY, PREVIEW_SVG_SIZE, PREVIEW_SVG_SIZE); // 2. 设置合成模式为destination-in,只保留白色矩形与SVG重叠的区域 ctx.globalCompositeOperation = "destination-in"; ctx.drawImage(svgIconImage, svgX, svgY, PREVIEW_SVG_SIZE, PREVIEW_SVG_SIZE); // 3. 恢复默认合成模式,不影响后续绘制 ctx.globalCompositeOperation = "source-over"; }; } } }, [previewCanvas, selectedColor, selectedSVG]); };
方法二:修改SVG的Data URI(仅适用于Data URI格式的SVG)
如果selectedSVG.uri是data:image/svg+xml开头的格式,可以直接解码并插入强制颜色的样式:
// 工具函数:修改SVG的填充和描边颜色 const modifySvgColor = (svgUri: string, color: string) => { const svgContent = decodeURIComponent(svgUri.split(",")[1]); // 在svg标签内添加全局样式,强制所有元素使用指定颜色 const modifiedSvg = svgContent.replace( '<svg', `<svg style="fill: ${color}; stroke: ${color};"` ); return `data:image/svg+xml,${encodeURIComponent(modifiedSvg)}`; }; // 在useEffect中使用: export const useRenderIcon = ( previewCanvas: React.RefObject<HTMLCanvasElement>, selectedColor: Color, selectedSVG: Svg ): void => { useEffect(() => { if (previewCanvas.current) { const ctx = previewCanvas.current.getContext("2d"); const canvas = previewCanvas.current; if (ctx) { canvas.width = PREVIEW_CANVAS_SIZE; canvas.height = PREVIEW_CANVAS_SIZE; canvas.style.width = `${PREVIEW_CANVAS_SIZE}px`; canvas.style.height = `${PREVIEW_CANVAS_SIZE}px`; ctx.fillStyle = selectedColor.color; ctx.fillRect(0, 0, canvas.width, canvas.height); const offset = canvas.width - PREVIEW_SVG_SIZE; const svgIconImage = new Image(); svgIconImage.src = modifySvgColor(selectedSVG.uri, "white"); svgIconImage.onload = () => { ctx.imageSmoothingEnabled = false; ctx.drawImage(svgIconImage, offset/2, offset/2, PREVIEW_SVG_SIZE, PREVIEW_SVG_SIZE); }; } } }, [previewCanvas, selectedColor, selectedSVG]); };
方法三:使用Canvas滤镜反转颜色(仅适用于纯黑色转白色)
如果SVG只有黑色内容,直接用反转滤镜即可快速转换颜色:
svgIconImage.onload = () => { ctx.imageSmoothingEnabled = false; // 应用反转滤镜,黑色转为白色 ctx.filter = "invert(100%)"; ctx.drawImage(svgIconImage, offset / 2, offset / 2, PREVIEW_SVG_SIZE, PREVIEW_SVG_SIZE); // 清除滤镜,避免影响后续绘制 ctx.filter = "none"; };
各方法优缺点
- 方法一:兼容性强,支持任意颜色转换,不受SVG内容限制
- 方法二:修改直接,绘制时无需额外操作,但仅适用于Data URI格式的SVG
- 方法三:代码最简洁,但仅适合黑白互转的场景
内容的提问来源于stack exchange,提问作者erol_smsr
相关产品推荐
相关产品推荐

