0

I've seen the option to simulate an outer stroke by simply doubling the stroke (and changing the order) when writing text to canvas (see here)

However, the app I'm working on allows for rgba colours to be used. As a result, it's possible that the "hidden" inner stroke becomes visible. Here's a sample:

In this example, the stroke is white and the text fill is rgba(0,0,0,0.5).

As a result, the hidden white inner stroke can be seen. Is it possible to apply an outer stroke without the inner? If not, is it possible to apply something like a clipping mask to the stroke to prevent the inner from being visible if the text fill has opacity?

I feel like I know the answer here, but hoping someone might have a clever solution.

Thanks -

Community
  • 1
  • 1
onassar
  • 3,313
  • 7
  • 36
  • 58
  • Yes it is possible. See this answer http://stackoverflow.com/a/34234589/3877726 it explains how to do this with a offscreen canvas and using masking, Could also be done with clipping regions. – Blindman67 Jun 30 '16 at 00:39
  • @Blindman67, my bad didn't saw you present gCo masking.. but still clipping won't do :) – Kaiido Jun 30 '16 at 00:47
  • @Kaiido I don't see why it would not work, there is no limit on the clipping complexity and there are many ways to get a path data for fonts, SVG fonts for example. – Blindman67 Jun 30 '16 at 01:08
  • 1
    @Blindman67 yeah... it's just that I guess OP is not wanting to convert all his fonts to svg ones and then extract the path data just for that, since canvas texts are not Path objects and can't be used as-is for clipping. – Kaiido Jun 30 '16 at 01:12
  • This needs to be done on the fly, and the canvas is being updated constantly, so I think creating an offscreen canvas won't work. I'll look into it a bit more though. Thanks @Blindman67 – onassar Jun 30 '16 at 03:43

1 Answers1

1

By using a simple sequence of composition operation you can obtain the result by these steps:

  • Stroke the text using target color for stroke
  • Change composition mode to destination-out
  • Fill using any solid color. This will knock out the interior.
  • Change composition mode to source-over
  • Fill again using target color and alpha

If you need to see the background through you can do these steps on a hidden (off-screen) canvas, then draw back the result on top of the background.

result

var ctx = c.getContext("2d");
ctx.font = "128px sans-serif";
ctx.textBaseline = "top";
ctx.lineJoin = "round";
ctx.strokeStyle = "#fff";
ctx.lineWidth = 11;

ctx.strokeText("MY TEXT", 10, 10);                 // stroke
ctx.globalCompositeOperation = "destination-out";  // will delete what's under
ctx.fillText("MY TEXT", 10, 10);                   //   on next draw
ctx.globalCompositeOperation = "source-over";      // normal comp. mode
ctx.fillStyle = "rgba(0,0,0,0.5)";                 // draw in target fill/color
ctx.fillText("MY TEXT", 10, 10);
#c {background:rgb(55, 68, 236)}
<canvas id=c width=580></canvas>
  • Thanks this makes a lot of sense. I'm using FabricJS, so having a bit of trouble trying to retrofit this into their internal rendering methods. I'll give it a go a bit more and see if I can at the very least create a JS fiddle of the basics. – onassar Jun 30 '16 at 03:41