skip to Main Content

I need to render font awesome image using canvas drawImage(). The drawImage accept image argument:

https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage#image
An element to draw into the context. The specification permits any canvas image source, specifically, an HTMLImageElement, an SVGImageElement, an HTMLVideoElement, an HTMLCanvasElement, an ImageBitmap, an OffscreenCanvas, or a VideoFrame.

I found the svg file from Font Awesome repository. For example:
https://raw.githubusercontent.com/FortAwesome/Font-Awesome/master/svgs/solid/calendar.svg

So here is my working code:

const context = document.getElementById("myCanvas").getContext('2d');

const image = new Image();
image.src = `https://raw.githubusercontent.com/FortAwesome/Font-Awesome/master/svgs/solid/calendar.svg`;

image.onload = function(){
    context.drawImage(image, 0, 0, 100, 100);
};
<canvas id="myCanvas" width="200" height="100"
style="border:1px solid #000000;">

But the svg file has a black color with white background. I want to change the color and the background font awesome icon before rendering. How can I do that? Here is my current attempt. Currently it has this error: DOMException: The source image could not be decoded. and I don’t think my current approach will work anyway…

const context = document.getElementById("myCanvas").getContext('2d');

modifyAndDraw(
  `https://raw.githubusercontent.com/FortAwesome/Font-Awesome/master/svgs/solid/calendar.svg`,
  context
);
        
async function modifyAndDraw(iconUrl, ctx) {
  try {
    const svgText = await fetchData(iconUrl);
    const modifiedSvgText = modifySvgContent(svgText, '#ff0000', '#00ff00');
    const imageBitmap = await createImageBitmap(
      new Blob([modifiedSvgText], { type: 'image/svg+xml;charset=utf-8' })
    );
    ctx.drawImage(imageBitmap, 0, 0, 100, 100);
  } catch (error) {
    console.error('Error:', error);
  }
};

async function fetchData(url) {
  const response = await fetch(url);
  if (!response.ok) {
    throw new Error(`Failed to fetch data from ${url}`);
  }
  return response.text();
};

async function modifySvgContent(svgText, fillColor, backgroundColor) {
  const parser = new DOMParser();
  const xmlDoc = parser.parseFromString(svgText, 'image/svg+xml');
  const svgElement = xmlDoc.documentElement;

  // Change fill color
  svgElement.querySelectorAll('[fill]').forEach((element) => {
    element.setAttribute('fill', fillColor);
  });

  // Add background color
  svgElement.setAttribute('style', `background-color: ${backgroundColor}`);

  return new XMLSerializer().serializeToString(svgElement);
};
<canvas id="myCanvas" width="200" height="100"
    style="border:1px solid #000000;">

2

Answers


  1. Using createImageBitmap directly with a Blob containing the modified SVG data should work, but looks like there is some issue with decoding the SVG data when using this method.

    You can try using the Image object with a data URL to decode and render the SVG data correctly.

    const context = document.getElementById("myCanvas").getContext('2d');
    
    modifyAndDraw(
      `https://raw.githubusercontent.com/FortAwesome/Font-Awesome/master/svgs/solid/calendar.svg`,
      context
    );
    
    async function modifyAndDraw(iconUrl, ctx) {
      try {
        const svgText = await fetchData(iconUrl);
        const modifiedSvgText = modifySvgContent(svgText, '#ff0000', '#00ff00');
        const image = new Image(); // <--- Notice here
        image.src = `data:image/svg+xml;charset=utf-8,  ${encodeURIComponent(modifiedSvgText)}`;
        image.onload = function() {
          ctx.drawImage(image, 0, 0, 100, 100);
        };
      } catch (error) {
        console.error('Error:', error);
      }
    };
    
    async function fetchData(url) {
      const response = await fetch(url);
      if (!response.ok) {
        throw new Error(`Failed to fetch data from ${url}`);
      }
      return response.text();
    };
    
    function modifySvgContent(svgText, fillColor, backgroundColor) {
      const parser = new DOMParser();
      const xmlDoc = parser.parseFromString(svgText, 'image/svg+xml');
      const svgElement = xmlDoc.documentElement;
    
      const styleElement = xmlDoc.createElementNS('http://www.w3.org/2000/svg', 'style');
      styleElement.textContent = `path { fill: ${fillColor}; }`;
      svgElement.insertBefore(styleElement, svgElement.firstChild);
    
      // Add background color
      svgElement.setAttribute('style', `background-color: ${backgroundColor}`);
    
      return new XMLSerializer().serializeToString(svgElement);
    };
    <canvas id="myCanvas" width="200" height="100" style="border:1px solid #000000;">
      </canvas>
    Login or Signup to reply.
  2. The Font Awesome SVGs don’t have any fills specified, so trying to change them won’t work.

    For a SVG that has a single <path>, you can just inject the desired fill color into the text.

    const context = document.getElementById("myCanvas").getContext('2d');
    
    modifyAndDraw(
      `https://raw.githubusercontent.com/FortAwesome/Font-Awesome/master/svgs/solid/calendar.svg`,
      context
    );
    
    async function modifyAndDraw(iconUrl, ctx) {
      const svgText = await fetchData(iconUrl);
      const modifiedSvgText = svgText.replace('<path ', '<path fill="purple" ');
      const blob = new Blob([modifiedSvgText], {
        type: 'image/svg+xml;charset=utf-8'
      });
      const image = await loadImage(URL.createObjectURL(blob));
      ctx.fillStyle = "orange";
      ctx.fillRect(0, 0, 100, 100);
      ctx.drawImage(image, 0, 0, 100, 100);
    };
    
    async function fetchData(url) {
      const response = await fetch(url);
      if (!response.ok) {
        throw new Error(`Failed to fetch data from ${url}`);
      }
      return response.text();
    };
    
    async function loadImage(url) {
      return new Promise((resolve) => {
        const img = new Image();
        img.src = url;
        img.onload = () => resolve(img);
      });
    }
    <canvas id="myCanvas" width="200" height="200" style="border:1px solid #000000;">
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search