Dmitriy Girskiy
Dmitriy Girskiy

Reputation: 153

Canvas shadow / glow effect on a semi-transparent shape?

I have a semi-transparent shape:

this.cx.beginPath();
this.cx.globalAlpha = .1;
this.cx.fillStyle = gradientFill;
this.cx.strokeStyle = gradientStroke;
this.cx.arc(150, 150, 139, 0, Math.PI * 2, true);
this.cx.lineWidth = 1;
this.cx.stroke();
this.cx.fill();

I want to add a bit of shadow, but I want it to only appear outside of the shape, I guess more of a glow than a shadow. Is there a way to do this in canvas as my attempts with:

this.cx.shadowColor = 'rgba(0, 0, 0, .75)';
this.cx.shadowBlur = 5;
this.cx.shadowOffsetX = 5;
this.cx.shadowOffsetY = -5;

Look fairy ordinary as the dark shadow is visible through the semi-transparent shape.

Thanks!

Upvotes: 0

Views: 564

Answers (1)

Kaiido
Kaiido

Reputation: 137044

One way is to use globalCompositeOperations in order to keep only your outer shadow first, and then redraw your semi-transparent parts over it.

But note that you'll have a lot of artifact noise...

(async function() {
  var ctx = c.getContext('2d');
  // commons
  var gradientFill = ctx.createLinearGradient(0, 0, 200, 0);
  gradientFill.addColorStop(0, 'rgba(0,0,0,0)')
  gradientFill.addColorStop(1, 'rgba(255,0,0,1)')
  var gradientStroke = ctx.createLinearGradient(0, 0, 200, 0);
  gradientStroke.addColorStop(0, 'rgba(0,0,0,0)')
  gradientStroke.addColorStop(1, 'rgba(0,255,0,1)')
  ctx.lineWidth = 5;
  // needed only once
  ctx.beginPath();
  ctx.arc(150, 150, 139, 0, Math.PI * 2, true);

  await wait(1000); // simply to show each step

  // firt we draw only the shadow with black fill and stroke
  ctx.shadowColor = 'rgba(0, 0, 0, .75)';
  ctx.shadowBlur = 5;
  ctx.shadowOffsetX = 5;
  ctx.shadowOffsetY = -5;
  ctx.stroke();
  ctx.fill();

  await wait(1000);

  // then keep only the shadow
  ctx.globalCompositeOperation = 'destination-out'; // will erase existing content at drawn position
  ctx.shadowColor = 'transparent'; // remove the shadow
  ctx.stroke();
  ctx.fill();

  await wait(1000);
  
  // finally draw the semi-transparent version
  ctx.globalCompositeOperation = 'source-over';
  ctx.globalAlpha = .1;
  ctx.fillStyle = gradientFill;
  ctx.strokeStyle = gradientStroke;
  ctx.stroke();
  ctx.fill();

})();

function wait(t) {
  return new Promise(r => setTimeout(r, t))
}
<canvas id="c" height="300"></canvas>

Upvotes: 1

Related Questions