简体   繁体   中英

Drawing text with an outer stroke with HTML5's canvas

I'm currently using HTML5's canvas to render a number of strings using the fillText method. This works fine, but I'd also like to give each string a 1px black outer stroke. Unfortunately the strokeText function seems to apply an inner stroke. To counter this, I've written a drawStrokedText function that achieves the effect I'm after. Unfortunately it's horrible slow (for obvious reasons).

Is there a fast, cross-browser way of achieving a 1px outer stroke using native canvas functionality?

drawStrokedText = function(context, text, x, y)
{
    context.fillStyle = "rgb(0,0,0)";
    context.fillText(text, x-1, y-1);
    context.fillText(text, x+1, y-1);
    context.fillText(text, x-1, y);
    context.fillText(text, x+1, y);
    context.fillText(text, x-1, y+1);
    context.fillText(text, x+1, y+1);

    context.fillStyle = "rgb(255,255,255)";
    context.fillText(text, x, y);
};

Here's an example of the effect at work:

在此处输入图像描述

What's wrong with stroke? Since half the stroke will be outside of the shape, you can always draw the stroke first with a line width of double what you want. So if you wanted a 4px outer stroke you could do:

function drawStroked(text, x, y) {
    ctx.font = '80px Sans-serif';
    ctx.strokeStyle = 'black';
    ctx.lineWidth = 8;
    ctx.strokeText(text, x, y);
    ctx.fillStyle = 'white';
    ctx.fillText(text, x, y);
}


drawStroked("37°", 50, 150);

Which makes:

在此处输入图像描述

live fiddle here: http://jsfiddle.net/vNWn6/


IF that happens to not look as accurate at smaller text rendering scales, you can always draw it large but scale it down (in the above case you'd do ctx.scale(0.25, 0.25) )

Simon's answer is a good solution, yet it may have mitering glitches in some cases, especially with capital 'M', 'V', & 'W':

drawStroked("MVW", 50, 150);

http://jsfiddle.net/hwG42/1/

In this case, it's best to utilize:

ctx.miterLimit=2;

http://jsfiddle.net/hwG42/3/

Best of luck!

The above answers are great, using some of these solutions* and some of my own ideas, I made a quick reference and some creative alternatives in the below fiddle.

*All credits given where due in the fiddle code

drawStrokedText   ( text, x, y );
drawShadowedText  ( text, x, y, shadowBlur);
drawGlowingText   ( text, x, y, glowColorHex, glowDistance);
drawBlurredText   ( text, x, y, blurAmount);
drawReflectedText ( text, x, y, reflectionScale, reflectionOpacity);

https://jsfiddle.net/vtmnyea8/

 // Author: Aaron Edmistone // Text effects using HTML5 Canvas with 2D Context. // https://stackoverflow.com/questions/7814398/a-glow-effect-on-html5-canvas var canvas = document.getElementById('myCanvas'); var ctx = canvas.getContext('2d'); // prepare the presentation of the canvas ctx.fillStyle = 'black'; ctx.fillRect(0,0,250,450); ctx.fillStyle = 'gray'; ctx.fillRect(250,0,250,450); ctx.fillStyle = 'white'; ctx.fillRect(500,0,250,450); ctx.fillStyle = '#0066CC'; ctx.fillRect(750,0,250,450); // prepare the font and fill ctx.font = "80px Sans-serif"; ctx.fillStyle = "white"; function drawStrokedText(text, x, y) { // using the solutions from @Simon Sarris and @Jackalope from // https://stackoverflow.com/questions/7814398/a-glow-effect-on-html5-canvas ctx.save(); ctx.strokeStyle = 'black'; ctx.lineWidth = 8; ctx.lineJoin="round"; ctx.miterLimit=2; ctx.strokeText(text, x, y); ctx.fillText(text, x, y); ctx.restore(); } function drawShadowedText(text, x, y, shadowBlur = 3) { ctx.save(); ctx.shadowBlur = shadowBlur; ctx.shadowColor = "#000000"; ctx.shadowOffsetX = 4; ctx.shadowOffsetY = 4; ctx.fillText(text, x, y); ctx.restore(); } function drawGlowingText(text, x, y, glowColorHexString, glowDistance = 10) { ctx.save(); ctx.shadowBlur = glowDistance; ctx.shadowColor = glowColorHexString; ctx.strokeText(text, x, y); for(let i = 0; i < 3; i++) ctx.fillText(text, x, y); //seems to be washed out without 3 fills ctx.restore(); } function drawBlurredText(text, x, y, blur = 5) { //using technique from https://www.html5rocks.com/en/tutorials/canvas/texteffects/ ctx.save(); let width = ctx.measureText(text).width + blur * 2; ctx.shadowColor = ctx.fillStyle; ctx.shadowOffsetX = width + x + ctx.canvas.width; ctx.shadowOffsetY = 0; ctx.shadowBlur = blur; ctx.fillText(text, -width + -ctx.canvas.width, y); ctx.restore(); } function drawReflectedText(text, x, y, reflectionScale = 0.2, reflectionAlpha = 0.10) { ctx.save(); ctx.fillText(text, x, y); ctx.scale(1, -reflectionScale); ctx.globalAlpha = reflectionAlpha; ctx.shadowColor = ctx.fillStyle; ctx.shadowBlur = 15; ctx.fillText(text, x, -(y * (1 / reflectionScale))); ctx.restore(); } for(let i = 0; i < 4; i++) { drawStrokedText ("MVW", 20 + i * 250, 80 * 1); drawShadowedText ("MVW", 20 + i * 250, 80 * 2, 3); drawGlowingText ("MVW", 20 + i * 250, 80 * 3, "#FF0000", 10); drawBlurredText ("MVW", 20 + i * 250, 80 * 4, 5); drawReflectedText ("MVW", 20 + i * 250, 80 * 5, 0.5, 0.5); }
 <canvas id="myCanvas" width="1000" height="500"></canvas>

Output of the fiddle:

下面小提琴的输出

What it supports:

  • Outline text
  • Shadow text
  • Glowing text
  • Blurred text
  • Reflected text

Some performance metrics:

Considering using this in a game or at high frame rates? Check out this jsperf using the above methods.

https://jsperf.com/various-text-effects-html5-2d-context

For a smooth shadow you can try this

ctx.beginPath();
ctx.fillStyle = 'white';
ctx.font = "bold 9pt Tahoma";
ctx.shadowBlur = 3;
ctx.textAlign = "center";
ctx.shadowColor = "#000000";
ctx.shadowOffs = 0;                 
ctx.fillText('www.ifnotpics.com', 100, 50);        
ctx.closePath();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM