[SOLVED] Safari incorrectly drawing shadows on SVGs via HTML5 Canvas

Issue

This Content is from Stack Overflow. Question asked by Ryan Peschel

According to caniuse.com, using an SVGImageElement as the parameter to a context.drawImage call is supported in every browser except Safari.

However, it seems like many people are using a canvas to draw SVGs in Safari?

For example, this post seems to be drawing images (granted the shadows are slightly messed up).

So, can you draw SVGs on a canvas in Safari?

Is it simply that SVGImageElement is not supported, but a regular Image is, and you can simply set the src on an image to an svg, and then it works fine?

If that is indeed the case, then does it really matter that SVGImageElement is not supported in Safari? You can still draw SVGs onto the canvas regardless by just using a regular Image and setting the src to be an svg asset file?

Do I have that right?



Solution

That’s a bug, you should report it to webkit’s bug-tracker.

Though you can workaround it by first drawing the image on a second canvas just to rasterize that svg image and use that canvas as source for the shadowing:

var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');

var image = new Image();
image.src = 'https://storage.googleapis.com/card-conjurer/img/manaSymbols/0.svg';
image.onload = function() {
  const off = canvas.cloneNode();
  off.getContext('2d').drawImage(image, 10, 10, 100, 100);
  context.shadowOffsetX = 10;
  context.shadowOffsetY = 10;
  context.shadowColor = 'red';
  context.drawImage(off, 0, 0);
}
<canvas id='canvas'></canvas>

In order to use a single canvas, we need to use an offset trick, but it’s not always easy to do since it requires knowing clearly the position of our drawing:

var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');

var image = new Image();
image.src = 'https://storage.googleapis.com/card-conjurer/img/manaSymbols/0.svg';
image.onload = function() {
  // first pass without shadow
  context.drawImage(image, 10, 10, 100, 100);
  // set shadow offsets to the position in page of bottom-right corner
  context.shadowOffsetX = 10 + 110;
  context.shadowOffsetY = 10 + 110;
  context.shadowColor = 'red';
  // draw behind
  context.globalCompositeOperation = "destination-over";
  // draw with inverse offset, so that the image is not visible
  // but the shadow is in-screen
  context.drawImage(canvas, -110, -110);
}
<canvas id='canvas'></canvas>


This Question was asked in StackOverflow by ImKyle4815 and Answered by Kaiido It is licensed under the terms of CC BY-SA 2.5. - CC BY-SA 3.0. - CC BY-SA 4.0.

people found this article helpful. What about you?