Canvas inner shadow effect

wedge

In a previous rail transit visualization project, many drawing techniques were used. You can refer to the previous article “Drawing large Screen Project Effect of Rail Transit using Canvas Shadow Function and Two-line Technique”.

The track in the effect drawing has the effect of both external and internal luminous effects.

External luminous effect

We know that the outer glow effect is easy to achieve by setting the shadow effect directly. For example, if we draw a random line segment and add a shadow effect, it looks like the outer glow effect:

CTX. ClearRect (0, 0, canvas width, canvas, height); ctx.shadowBlur= 20; ctx.shadowOffsetX = 0; ctx.shadowOffsetY = 0; ctx.shadowColor="red"; ctx.lineCap = "round"; ctx.lineJoin = "round"; ctx.lineWidth = 10; ctx.strokeStyle = "blue"; ctx.beginPath(); CTX. MoveTo (300300); CTX. LineTo (750300); CTX. QuadraticCurveTo (800300800350); CTX. LineTo (800450); CTX. QuadraticCurveTo (800500750500); CTX. LineTo (300500); ctx.stroke();Copy the code

The renderings are as follows:

If I draw a circle, it looks like this:

The above code is easy to understand, which is to create a gradient shadow effect through shadowBlur. The default shadow, which we call outer shadow, means the shadow effect that the image wants to unfold.

Inner shadow

The next question might get a little more difficult. What if we wanted an inner shadow effect like the one below?

Some say it’s easy, just a gradient. What about this picture down here?

You can still do it with a gradient, but the stop gradient is a bit more difficult. How about a more complex graph, like the following line segment effect:

For the inner shadow effect of the line segment above, it is difficult to use a simple gradient to achieve.

How to draw inner shadow effect

To achieve the inner shadow effect, first use the shadowBlur parameter and then set CTX’s globalCompositeOperation parameter to “source-out”. Try the following code:

 ctx.globalCompositeOperation = 'source-out';
     ctx.beginPath();
     ctx.beginPath();
    ctx.moveTo(300.300);
    ctx.lineTo(750.300);
    ctx.quadraticCurveTo(800.300.800.350);
    ctx.lineTo(800.450);
    ctx.quadraticCurveTo(800.500.750.500);
    ctx.lineTo(300.500);
    ctx.lineCap = "round";
     ctx.shadowBlur =15;
     ctx.lineWidth = 20;
     ctx.shadowColor="blue";
     ctx.fillStyle = 'red';
     ctx.strokeStyle = 'red';
     ctx.stroke();
Copy the code

The final drawing looks like the line segment above:

Draw both inner and outer shadow effects

If we change the globalCompositeOperation to “xor”, we can also get the effect of both inner and outer shadows. The code is as follows:

ctx.globalCompositeOperation = 'xor'; ctx.beginPath(); ctx.beginPath(); CTX. MoveTo (300300); CTX. LineTo (750300); CTX. QuadraticCurveTo (800300800350); CTX. LineTo (800450); CTX. QuadraticCurveTo (800500750500); CTX. LineTo (300500); ctx.lineCap = "round"; ctx.shadowBlur =15; ctx.lineWidth = 20; ctx.shadowColor="red"; ctx.fillStyle = 'red'; ctx.strokeStyle = 'red'; ctx.stroke();Copy the code

The drawing looks like this:

Inner shadow defects

The inner shadow color implemented by the above method can only be the same color as the drawing body, but can not be defined as the outer shadow color. For example, if the shadowColor in the code above is changed to blue, only the outer shadowColor is changed:

ctx.globalCompositeOperation = 'xor'; ctx.beginPath(); ctx.beginPath(); CTX. MoveTo (300300); CTX. LineTo (750300); CTX. QuadraticCurveTo (800300800350); CTX. LineTo (800450); CTX. QuadraticCurveTo (800500750500); CTX. LineTo (300500); ctx.lineCap = "round"; ctx.shadowBlur =15; ctx.lineWidth = 20; ctx.shadowColor="red"; ctx.fillStyle = 'red'; ctx.strokeStyle = 'red'; ctx.stroke();Copy the code

The final result is shown below:

It can be seen from the figure that only the outer shadow color has changed, while the inner shadow uses the color of the body.

Achieve the effect of flickering

SetInterval (()=>{xor(); setInterval()=>{xor(); }, 10)

let shadowBlur = 5; Let offset = 0.5; Function xor () {CTX. ClearRect (0, 0, canvas width, canvas, height); ctx.globalCompositeOperation = 'xor'; ctx.shadowBlur= shadowBlur; ctx.shadowOffsetX = 0; ctx.shadowOffsetY = 0; ctx.shadowColor="red"; ctx.lineCap = "round"; ctx.lineJoin = "round"; ctx.lineWidth = 10; ctx.strokeStyle = "blue"; ctx.beginPath(); CTX. MoveTo (300300); CTX. LineTo (750300); CTX. QuadraticCurveTo (800300800350); CTX. LineTo (800450); CTX. QuadraticCurveTo (800500750500); CTX. LineTo (300500); ctx.stroke(); // ctx.stroke(); ctx.globalCompositeOperation = 'xor'; CTX. ShadowBlur = shadowBlur / 10.0; ctx.shadowOffsetX=0; ctx.shadowOffsetY=0; ctx.shadowColor="blue"; ctx.lineWidth =1; // ctx.stroke(); shadowBlur += offset; if(shadowBlur > 15 || shadowBlur < 1){ offset *= -1; }}Copy the code

If you do some overlay painting, you can also achieve the following effect:

Function xor () {CTX. ClearRect (0, 0, canvas width, canvas, height); ctx.globalCompositeOperation = 'xor'; ctx.shadowBlur= shadowBlur; ctx.shadowOffsetX = 0; ctx.shadowOffsetY = 0; ctx.shadowColor="red"; ctx.lineCap = "round"; ctx.lineJoin = "round"; ctx.lineWidth = 20; ctx.strokeStyle = "red"; ctx.beginPath(); CTX. MoveTo (300300); CTX. LineTo (750300); CTX. QuadraticCurveTo (800300800350); CTX. LineTo (800450); CTX. QuadraticCurveTo (800500750500); CTX. LineTo (300500); ctx.stroke(); // ctx.stroke(); ctx.globalCompositeOperation = 'destination-out'; CTX. ShadowBlur = shadowBlur / 10.0; ctx.shadowOffsetX=0; ctx.shadowOffsetY=0; ctx.shadowColor="red"; ctx.lineWidth =5; ctx.stroke(); shadowBlur += offset; if(shadowBlur > 15 || shadowBlur < 1){ offset *= -1; }}Copy the code

conclusion

Now that we have reached the end of this article, we can summarize the technical points used to draw inner shadows

  1. CanvasRenderingContext2D.globalCompositeOperation
  2. CanvasRenderingContext2D.shadowBlur

Among themglobalCompositeOperationIs an interesting property that can achieve many different effects by setting different parameters. For example, the following effect uses this property:

Interested readers can keep an eye out for more articles in the past.

If you are interested in visualization, you can communicate with me on wechat 541002349. In addition, you can receive more valuable articles in time through the public account “ITMan Biao Shu”.