21

In the code below, the second fillStyle overrides the color specified in first one if I use rect() and then fill() in both places (ie, both rects are green) but works as expected (ie, the first rect being blue and second being green) if I change the first rect() to fillRect(). Why is it so? I thought fillRect() was just rect() and then fill(), right?

ctx.translate(canvas.width/2, canvas.height/2);

ctx.fillStyle = "#5A9BDC";
ctx.fillRect(0, 0, rectWidth, rectHeight);
// ctx.rect(0, 0, rectWidth, rectHeight);
// ctx.fill();    

ctx.translate(-canvas.width/2, -canvas.height/2);

ctx.fillStyle = "#31B131";
ctx.rect(0, 0, rectWidth, rectHeight);
ctx.fill();

Tested in Chrome | Fiddle

user229044
  • 232,980
  • 40
  • 330
  • 338
Sourabh
  • 8,243
  • 10
  • 52
  • 98

3 Answers3

30

fillRect

.fillRect is a "stand-alone" command that draws and fills a rectangle.

So if you issue multiple .fillRect commands with multiple .fillStyle commands, each new rect will be filled with the preceeding fillstyle.

ctx.fillStyle="red";
ctx.fillRect(10,10,10,10);  // filled with red

ctx.fillStyle="green";
ctx.fillRect(20,20,10,10);  // filled with green

ctx.fillStyle="blue";
ctx.fillRect(30,30,10,10);  // filled with blue

rect

.rect is part of the canvas's path commands.

Path commands are groups of drawings beginning with the beginPath() and continuing until another beginPath() is issued.

Within each group, only the last styling command wins.

So if you issue multiple .rect commands and multiple .fillStyle commands inside a path, only the last .fillStyle will be used on all the .rect's.

ctx.beginPath();  // path commands must begin with beginPath

ctx.fillStyle="red";
ctx.rect(10,10,10,10);  // blue

ctx.fillStyle="green";
ctx.rect(20,20,10,10);  // blue

ctx.fillStyle="blue";  // this is the last fillStyle, so it "wins"
ctx.rect(30,30,10,10);  // blue

// only 1 fillStyle is allowed per beginPath, so the last blue style fills all

ctx.fill()
markE
  • 102,905
  • 11
  • 164
  • 176
  • Is `ctx.rect` faster to draw a lot of pixels? –  Jun 15 '16 at 14:16
  • 1
    If you want to draw one big rectangle, then both methods are about the same. If you want to draw a lot of disbursed rectangles then you should use: `.beginPath` + many `.rect` + `.fill` – markE Jun 15 '16 at 14:59
  • 1
    To offer a counterpoint to @markE : [this benchmark](https://www.measurethat.net/Benchmarks/Show/2244/0/canvas-paths-performance) shows that a direct `fillRect` has significantly more operations per second than a direct draw or even direct with `beginPath` – chrabyrd Nov 14 '19 at 18:31
  • 1
    Just tried it out and here's what I got: https://jsperf.com/canvas-fillrect-vs-rect/1 Apparently fillRect is twice as fast for single use and twice as slow when it comes to thousands – waterplea Jul 02 '20 at 18:01
  • @waterplea - broken link? – ashleedawg May 19 '21 at 01:40
  • The benchmark [here](https://www.measurethat.net/Benchmarks/Show/2244/0/canvas-paths-performance#latest_results_block) is no good. It translates coordinates while using the existing path, while the path itself does already have the from x / from y coordinates in it. Then it translates them back. This of course is taking time to change the canvas state. So the "Cached path" is not informative in that benchmark. Instead, check [this one](https://jsfiddle.net/antonphp/1ghLn4a9/) that I crafted quickly to compare `fillRect()` vs `fill(path)`. For 1000+ elements, both perform comparably well. – Meglio Dec 13 '21 at 05:25
  • In [this test](https://jsfiddle.net/antonphp/1ghLn4a9/38/) we can see how one `fill(combined_path)` calls outperforms multiple `fill(path)` and multiple `fillRect()` calls. So if you need to draw multiple polygons with the same style, it looks like the most performant way would be to create a combined path and then filling once. – Meglio Dec 13 '21 at 09:02
  • Yet [another test](https://jsfiddle.net/antonphp/1ghLn4a9/57/) where it shows that even if you have to draw with different colors, combined paths outperform BOTH multiple paths (one rect per path) AND separate `fillRect()`. In this benchmark, it combined 1000 rects into 10 separate paths (instances of Path2D). On my laptop, the combined path is about 3x...4x faster. P.S. When running benchmarks, don't forget to turn off all browser settings that reduce time percision, e.g. `reduceTimerPrecision` in Firefox (see in `about:config`). – Meglio Dec 13 '21 at 09:56
6

As I know there are 3 "rect" functions for canvas: fillRect, strokeRect and rect.

ctx.rect(0,0,rectWidth,rectHeight); // create some shape, there is nothing on the canvas yet
ctx.stroke(); // draw stroke of the shape
ctx.fill();   // fill the shape

There are two shortcuts:

ctx.strokeRect(0,0,rectWidth,rectHeight); // shortcut to stroke rectangle
ctx.fillRect(0, 0, rectWidth, rectHeight); // shortcut to fill rectangle

So, your fill invocation could fill only your shape created with rect.

bru02
  • 360
  • 2
  • 10
  • I know about all these methods and what `fillRect` is supposed to do, but it works a little different from `rect` and then `fill` (at least for me). PS: `fill` will fill any shape, even the ones created by drawing lines – Sourabh Mar 21 '14 at 15:03
  • comment the `ctx.fillRect(0, 0, rectWidth, rectHeight);` line and uncomment the 2 lines under it in fiddle and see what happens. My question is why that happens – Sourabh Mar 21 '14 at 15:04
0

If you want different colors for different path commands, calling beginPath() before each command works.

ctx.beginPath();  
ctx.fillStyle="red";
ctx.rect(10,10,10,10);
ctx.fill()

ctx.beginPath()
ctx.fillStyle="green";
ctx.rect(20,20,10,10);  
ctx.fill()

ctx.beginPath()
ctx.fillStyle="blue";  
ctx.rect(30,30,10,10);  
ctx.fill()
Prathik
  • 474
  • 3
  • 13