19

If I draw a rectangle of say linewidth=2 and then scale it to double the size of the rectangle, I get a rectangle that has its border double the size of the initial linewidth.

Is there a way to keep the linewidth to the perceived size of 2 or the original size.

In short, I want to just scale the size of the rectangle but keep the linewidth visually of size 2.

I tried setting the linewidth before and after the scale(2,2) command but the border width also increases.

One option is to divide the linewidth by the scale factor and this will work if the x and y scale factors are the same.

I don't have the option to scale the rectangle width and height and I need to use the scale command as I have other objects within the rectangle that need the scaling.

Nilesh
  • 191
  • 1
  • 4

3 Answers3

23

You can define path with transformation and stroke it without one. That way line width won't be transformed.

Example:

var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.save(); //save context without transformation
ctx.scale(2, 0.5); //make transformation
ctx.beginPath(); //define path
ctx.arc(100, 75, 50, 0, 2 * Math.PI);
ctx.restore(); //restore context without transformation
ctx.stroke(); //stroke path
<canvas id="myCanvas" width="300" height="150" style="border:1px solid #d3d3d3;">
gman
  • 100,619
  • 31
  • 269
  • 393
  • Which browsers did you test it with ? on Chrome 38 it doesn't seem to work. – b0fh Nov 16 '14 at 21:40
  • Awesome! Works for me in Chrome 44, FF 39, IE 9+ – Alexey Lebedev Jul 31 '15 at 12:13
  • This works great. But if there is a clip() region prior to the restore it is lost, and if the clip region is added before the stroke, the clip path is stroked. Is there another solution? – PeterT Mar 22 '17 at 01:00
  • You can reset the transform so this would work. `ctx.setTransform(1, 0, 0, 1, 0, 0); ctx.stroke();`. You could also save and restore as in `ctx.save(); ctx.setTransform(1, 0, 0, 1, 0, 0); ctx.stroke(); ctx.restore();` so as to just effect the stroking but keep everything else. No idea how that will effect clipping – gman Mar 08 '19 at 07:59
  • Is there any way you can do this with a Path2D object that has already been set? – Polygon Nov 26 '19 at 19:45
5

The lineWidth should be scaled down beforehand.

ctx.lineWidth = 2 / Math.max(scaleX, scaleY);
ctx.scale(scaleX, scaleY);
ctx.fillRect(50, 50, 50, 50);
Perception
  • 79,279
  • 19
  • 185
  • 195
naka
  • 59
  • 1
  • 2
1

Ok, you have a couple of options:

You can do your own scaling of coordinates and dimensions, e.g.

ctx.strokeRect( scaleX * x, scaleY * y, scaleX * width, scaleY * height) ;

And you'll need to apply the scaling to all the other objects too.

Alternatively you could apply the scaling but not rely on lineWidth for drawing the border of the rectangle. A simple method would be to draw the rectangle by filling the correct region and then removing the interior, minus the border, like so:

var scaleX = 1.5, scaleY = 2;
var lineWidth = 2;

ctx.scale(scaleX, scaleY);

ctx.fillStyle = "#000";
ctx.fillRect(100, 50, 100, 
ctx.clearRect(100+lineWidth/scaleX, 50+lineWidth/scaleY, 100-(2*lineWidth)/scaleX, 60-(2*lineWidth)/scaleY);
andrewmu
  • 14,276
  • 4
  • 39
  • 37
  • The OP appears to be experiencing the exact opposite... the `lineWidth` _is_ being affected by scaling? – MrWhite Sep 26 '10 at 14:16
  • Good grief! That was some pretty bad reading comprehension by me! And testing for myself, yes it is affected. – andrewmu Sep 27 '10 at 09:03
  • w3d is right, my linewidth is affected by scaling. My solution is to divide the line width by the scale factor if scale =2 then linewidth=original_linewidht/scale. This works good w.r.t perceived thickness – Nilesh Oct 25 '10 at 19:24