HTML5 Canvas and Line Width

HTML5 canvas line width less that 1 pixel

Although it doesn't make much sense, you can acheive that with using a regular 1-pixel line with a 50% scaled canvas (but again it's a 1-pixel rendition, read below). See this snippet:

var canvas = document.querySelector('canvas');var context = canvas.getContext('2d');
function scale() { context.scale(0.5, 0.5); draw();}
function draw() { context.beginPath(); context.moveTo(100, 150); context.lineTo(450, 50); context.stroke();}
draw()
<canvas width="400" height="150"></canvas><button onclick="scale()">Scale down</button>

Canvas Line Width too thick

Opacity is how a canvas creates the appearance of lines narrower than a pixel, because a pixel is (by definition) the smallest unit a canvas works with.

That doesn't mean all hope is lost, however, because this is only a problem depending on what you mean by "pixel".

There's the CSS unit px, which corresponds to what used to be the smallest actual pixels available on video display devices, typically in the range of 72-96 per inch (28-38 per cm).

And then there are actual device pixels, which are now often half as small or smaller.

The trick to getting sharper lines with a canvas when you've got high-res device pixels is scaling. Use this to figure how much you can effectively scale:

const scaling = window.devicePixelRatio || 1;

Suppose the value here is 2 (as it is on my current laptop). If you want to create a canvas that occupies, say, 400px by 300px, create an 800x600 canvas. Then use CSS styling width: 400px; height: 300px on your canvas.

Now you'll be able to create sharp half-width lines so long as the device you're drawing to supports the higher resolution.

I use this trick many places in the Angular app at https://skyviewcafe.com/. There's a link there to the source code if you want to dig through it to find examples of high-res canvas drawing.

Please note!

You're either have to specify a lineWidth of 1 when you mean half-width, or use canvas scaling like this:

const context = this.canvas.getContext('2d');
context.scale(scaling, scaling);

Be careful with context.scale() — its effects are cumulative. If you execute context.scale(2, 2) twice in a row with the same context, your scaling factor will be 4, not 2.

context.setTransform(1, 0, 0, 1, 0, 0);

...resets the scaling factor if you want to call context.scale() more than once, without the effect being cumulative.

HTML5 Canvas - create striped canvas with lineWidth and createPattern()

Code logic problems

The size of the pattern needs to match the slope of the line. That size must be expanded to allow for a set spacing between the lines.

Your code has a fixed size that does not match the slope of either of the lines you draw.

The lines you draw are both in different directions. You will never get them to create a repeatable pattern.

The code you have given is too ambiguous for me to understand what you wish to achieve thus the example adds some constraints that considers my best guess at your requirements.

Tileable striped pattern

The function in the example below creates a striped repeatable (tilded) pattern.

The function createStripedPattern(lineWidth, spacing, slope, color) requires 4 arguments.

  • lineWidth width of the line to draw

  • spacing distance between lines. Eg if lineWidth is 5 and spacing is 10 then the space between the lines is the same width as the line.

  • slope The slope of the line eg 45 degree slope is 1. I have only tested value >= 1 and am not sure if it will work below 1.

    Nor have I tested very large slopes. The point of the example is to show how to draw the line on the pattern to repeat without holes.

  • color Color of line to draw.

The function works by creating a canvas that will fit the constraints given by the arguments. It then draws a line from the top left to bottom right corners. This leaves a gap in the repeating pattern at the top right and bottom left corners.

To fill the missing pixels two more lines are drawn. One through the top right corner and the other through the bottom left.

Note you could also just copy the canvas onto itself (offset to the corners) to fill the missing corner pixels. For pixel art type patterns this may be preferable.

Note that canvas sizes are integer values and lines are rendered at sub pixel accuracy. For very small input values there will be artifact as the relative error between the canvas (integer) pixel size and required (floating point) size grows larger

Example

The example contains the function to create the pattern as outlined above and then renders some examples.

The first canvas has inset patterns with each pattern increasing the line width will keeping the spacing and slope constant.

The second canvas just fills with a fixed lineWidth as 4, spacing as 8 and a slope of 3

function createAARotatedPattern(lineWidth, spacing, ang, color) {
const can = document.createElement('canvas');
const w = can.width = 2;
const h = can.height = spacing;
const ctx = can.getContext('2d');
ctx.fillStyle = color;
ctx.fillRect(0, 0, 2, lineWidth);

const pat = ctx.createPattern(can, 'repeat');
const xAx = Math.cos(ang);
const xAy = Math.sin(ang);
pat.setTransform(new DOMMatrix([xAx, xAy, -xAy, xAx, 0, 0]));
return pat;
}
function createStripedPattern(lineWidth, spacing, slope, color) {
const can = document.createElement('canvas');
const len = Math.hypot(1, slope);

const w = can.width = 1 / len + spacing + 0.5 | 0; // round to nearest pixel
const h = can.height = slope / len + spacing * slope + 0.5 | 0;

const ctx = can.getContext('2d');
ctx.strokeStyle = color;
ctx.lineWidth = lineWidth;
ctx.beginPath();

// Line through top left and bottom right corners
ctx.moveTo(0, 0);
ctx.lineTo(w, h);
// Line through top right corner to add missing pixels
ctx.moveTo(0, -h);
ctx.lineTo(w * 2, h);
// Line through bottom left corner to add missing pixels
ctx.moveTo(-w, 0);
ctx.lineTo(w, h * 2);

ctx.stroke();
return ctx.createPattern(can, 'repeat');
};

function fillWithPattern(canvas, pattern, inset = 0) {
const ctx = canvas.getContext('2d');
ctx.clearRect(inset, inset, canvas.width - inset * 2, canvas.height - inset * 2);
ctx.fillStyle = pattern;
ctx.fillRect(inset, inset, canvas.width - inset * 2, canvas.height - inset * 2);
return canvas;
}

fillWithPattern(targetCanvas, createStripedPattern(2, 6, 2, "#000"));
fillWithPattern(targetCanvas, createStripedPattern(3, 6, 2, "#000"), 50);
fillWithPattern(targetCanvas, createStripedPattern(4, 6, 2, "#000"), 100);
fillWithPattern(targetCanvas1, createStripedPattern(4, 8, 3, "#000"));
var y = 0;
var ang = 0;
const ctx = targetCanvas2.getContext('2d');
while (y < targetCanvas2.height) {
ctx.fillStyle = createAARotatedPattern(2, 5, ang, "#000");
ctx.fillRect(0, y, targetCanvas2.width, 34);
y += 40;
ang += 2 * Math.PI / (targetCanvas2.height / 40);
}



<canvas id="targetCanvas" width="300" height="300"></canvas>
<canvas id="targetCanvas1" width="300" height="300"></canvas>
<canvas id="targetCanvas2" width="300" height="600"></canvas>

Incorrect line position with HTML Canvas when lineWidth is not set to 1

The fill is the root cause of your problems

const geometryParsed = [  {    geom: [{ x:78, y:132 }, { x:59, y:132 }, { x:59, y:183 }, { x:78, y:183 }, { x:78, y:132 }]  },  {    geom: [{ x:97, y:132 }, { x:78, y:132 }, { x:78, y:166 }, { x:97, y:166 }, { x:97, y:132 }]  },];
const canv = document.getElementById("canvas");const context = canv.getContext("2d");
context.translate(-50, -100)context.lineWidth = 2;for (let k = 0; k < geometryParsed.length; k++) { const geometry = geometryParsed[k].geom; context.beginPath(); context.moveTo(geometry[0].x, geometry[0].y); for (let i = 1; i < geometry.length; i++) { context.lineTo(geometry[i].x, geometry[i].y); } context.stroke();}
context.translate(80, 0)context.lineWidth = 4;for (let k = 0; k < geometryParsed.length; k++) { const geometry = geometryParsed[k].geom; context.beginPath(); context.moveTo(geometry[0].x, geometry[0].y); for (let i = 1; i < geometry.length; i++) { context.lineTo(geometry[i].x, geometry[i].y); } context.stroke(); context.fillStyle = "white"; context.fill();}
<canvas id="canvas" width="476px" height="170px"></canvas>

Line Width in Canvas

Happily (and Sadly) you have correctly implemented "pixel-snapping" when you add/subtract .5 pixels to get your lines to align with pixel boundaries. Unlike Photoshop, there is no option to automatically pixel snap in canvas. ...I feel your pain!

How to make line width for custom line in html5 canvas

A context.beginPath() is missing between

context.lineWidth = 1;
context.stroke(); // Draw it

and

context.lineTo(lrectx, lrecty);
context.lineWidth = 8;

context.strokeStyle = "#ddd";
context.stroke();

Without the beginPath call you are simply re-stroking all or the paths and subpaths already defined with the new stroke style and width.

The general answer to the second part of your question is that you don't get to do this.

Painting a canvas is equivalent to drawing an image. You can work out where the mouse is over the image, but you would then need to work out (in your program) if the mouse is over some pixels you want to change and redraw the canvas if it is.

If you want to use CSS :hover pseudo classes to change presentation you will need to construct the source code of an SVG element for the graphic, create the element from the source code generated, and supply appropriate CSS for the SVG element's child nodes that will be affected by mouse position.

HTML5 Canvas — Line too thick

You don't need to use 0.5 , when the lineWidth is 4 you must use:

  • 2 instead of 0
  • (w-2) instead of w
  • (h-2) instead of h

var canvas = document.getElementById("c"),     c = canvas.getContext("2d"),     w = canvas.width,     h = canvas.height,     hallwayWidth = w * 0.10, /*18px*/        l = 2;
c.beginPath(); c.lineWidth = l*2; c.moveTo(l,l); c.lineTo(w / 3, l); c.moveTo(l, l); c.lineTo(l, h-l); c.moveTo(w-l, l); c.lineTo(w-l, h-l); c.moveTo(w / 3 + hallwayWidth, l); c.lineTo(w-l, l); c.moveTo(l, h-l); c.lineTo(w / 2, h-l); c.moveTo(w / 2 + hallwayWidth, h-l); c.lineTo(w-l, h-l);
/*code for thick line*/ c.moveTo(hallwayWidth, l); c.lineTo(hallwayWidth, w / 3);
c.stroke();
canvas {    display: block;    margin: 0 auto;    margin-top: 50px;    /* border: 1px solid red; */    background:red;}
<canvas id="c" width="200" height="200"></canvas>


Related Topics



Leave a reply



Submit