简体   繁体   中英

Canvas polygons not touching properly

So I've been fiddling with the canvas element, and I seem to have run into a situation that is highly irritating, yet I haven't been able to find a solution. Say that two polygons are drawn on a canvas, and that they should be touching each other. Where one polygon is drawn like this:

ctx.beginPath();
ctx.moveTo(oX,oY);
ctx.lineTo(oX=oX+k,oY=oY-h);
ctx.lineTo(oX=oX+k,oY=oY+h);
ctx.lineTo(oX=oX-k,oY=oY+h);
ctx.lineTo(oX=oX-k,oY=oY-h);
ctx.fill();

A simple version is implemented in this fiddle .

As you can probably see there is a thin line between these shapes. How can I avoid it? I have tried the solutions here , but they don't really seem to refer to this case, because I'm dealing with diagonal lines.

One solution

You could always use the stroke-line trick, but depending on your goal:

If it is to show many polygons next to each other, you could look at the polygons as simple squares.

  • Draw them in as such in an off-screen canvas next to each other. This will produce a result with no gaps.
  • Then transform the main canvas into the position you want those polygons to appear. Add rotation and/or skew depending on goal.
  • Finally, draw the off-screen canvas onto the main canvas as an image. Problem gone.

This will give you an accurate result with no extra steps in stroking, and the calculations for the boxes becomes very simple and fast to do (think 2d grid).

You have to use an off-screen canvas though. If you transform main canvas and draw in the shapes you will encounter the same problem as already present. This is because each point is transformed and if there is need for interpolation it will be calculated for each path shape separately. Drawing in an image will add interpolation on the whole surface, and only where there are gaps (non-opaque alpha). As we already are "gap-free" this is no longer a problem.

This will require an extra step in planning to place them correctly, but this is a simple step.

Example

Step 1 - draw boxes into an off-screen canvas:

This code draws on the off-screen canvas resulting in two boxes with no gap:

卡

(the example uses an on-screen to show result, see next step for usage of off-screen canvas)

 var ctx = document.querySelector("canvas").getContext("2d"); ctx.fillStyle = "red"; ctx.fillRect(10, 10, 50, 50); ctx.fillRect(60, 10, 50, 50); 
 <canvas/> 

Step 2 - transform main canvas and draw in off-screen canvas

When drawn into main canvas with transformation set, the result will be (pseudo-random transformation just to show):

snap2

 var ctx = document.querySelector("canvas").getContext("2d"); // off-screen canvas var octx = document.createElement("canvas").getContext("2d"); octx.fillStyle = "red"; octx.fillRect(10, 10, 50, 50); octx.fillRect(60, 10, 50, 50); // transform and draw to main ctx.translate(80, 0); ctx.rotate(0.5, Math.PI); ctx.transform(1, 0, Math.tan(-0.5),1, 0,0); // skew ctx.drawImage(octx.canvas, 0, 0); 
 <canvas /> 

Step 3 (optional) - Interaction

If you want to interact with the boxes you simply apply the same transform, then add path for a box and hit-test it against the mouse position. Redraw a single state, erase by clearing and draw back the off-screen canvas on top:

 var ctx = document.querySelector("canvas").getContext("2d"); // off-screen canvas var octx = document.createElement("canvas").getContext("2d"); octx.fillStyle = "red"; octx.fillRect(10, 10, 50, 50); octx.fillRect(60, 10, 50, 50); // allow us to reuse some of the steps: function getTransforms() { ctx.setTransform(1,0,0,1,0,0); ctx.translate(80, 0); ctx.rotate(0.5, Math.PI); ctx.transform(1, 0, Math.tan(-0.5),1, 0,0); // skew } function clear() { ctx.setTransform(1,0,0,1,0,0); ctx.clearRect(0,0,300,150); } function redraw() { ctx.drawImage(octx.canvas, 0, 0); } getTransforms(); redraw(); ctx.canvas.onmousemove = function(e) { var r = this.getBoundingClientRect(), x = e.clientX - r.left, y = e.clientY - r.top; // box 1 (for many, use array) ctx.beginPath(); ctx.rect(10, 10, 50, 50); clear(); // these can be optimized to use state-flags getTransforms(); // so they aren't redraw for every move... redraw(); // just one box check here if (ctx.isPointInPath(x, y)) { ctx.fill(); } }; 
 <canvas /> 

Yes, it's annoying when filled polygons result in that tiny gap. It's especially common on diagonals that should theoretically meet.

A common workaround is to put a half-pixel, same-colored stroke around the polygons:

//Some basic setup ...
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var oX = 50;
var oY = 50;
var h = 33;
var k = 50;
ctx.fillStyle = 'red';
ctx.strokeStyle='red';
ctx.lineWidth=0.50;

//Draw one polygon
ctx.beginPath();
ctx.moveTo(oX,oY);
ctx.lineTo(oX=oX+k,oY=oY-h);
ctx.lineTo(oX=oX+k,oY=oY+h);
ctx.lineTo(oX=oX-k,oY=oY+h);
ctx.lineTo(oX=oX-k,oY=oY-h);
ctx.fill();
ctx.stroke();
//Draw another polygon
oX = oX+k;
oY = oY+h;
ctx.beginPath();
ctx.moveTo(oX,oY);
ctx.lineTo(oX=oX+k,oY=oY-h);
ctx.lineTo(oX=oX+k,oY=oY+h);
ctx.lineTo(oX=oX-k,oY=oY+h);
ctx.lineTo(oX=oX-k,oY=oY-h);
ctx.fill();
ctx.stroke();

 var canvas=document.getElementById("canvas"); var ctx=canvas.getContext("2d"); //Some basic setup ... var canvas = document.getElementById("canvas"); var ctx = canvas.getContext("2d"); var oX = 50; var oY = 50; var h = 33; var k = 50; ctx.fillStyle = 'red'; ctx.strokeStyle='red'; ctx.lineWidth=0.50; //Draw one polygon ctx.beginPath(); ctx.moveTo(oX,oY); ctx.lineTo(oX=oX+k,oY=oY-h); ctx.lineTo(oX=oX+k,oY=oY+h); ctx.lineTo(oX=oX-k,oY=oY+h); ctx.lineTo(oX=oX-k,oY=oY-h); ctx.fill(); ctx.stroke(); //Draw another polygon oX = oX+k; oY = oY+h; ctx.beginPath(); ctx.moveTo(oX,oY); ctx.lineTo(oX=oX+k,oY=oY-h); ctx.lineTo(oX=oX+k,oY=oY+h); ctx.lineTo(oX=oX-k,oY=oY+h); ctx.lineTo(oX=oX-k,oY=oY-h); ctx.fill(); ctx.stroke(); 
 #canvas{border:1px solid red;} 
 <canvas id="canvas" width=300 height=300></canvas> 

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