Merging two shapes inside html canvas - javascript

I'm writing a freehand drawing webapp using the HTML Canvas element. So far, I can draw shapes onto the sceeen that look like the following images.
I have stored all the points of the shapes into a JS array. For instance:
[
[
[118, 171],
[118, 170],
[118, 167],
...
],
[
[236, 131],
[236, 133],
[236, 135],
...
]
]
I'm now looking for a way to merge certain shapes, imagine that I would like to merge the two shapes in the following picture, here's how it would look like.
I think I have to redraw to the canvas but filter out all the points that are inside another shape.
The first idea that I had was to find a solution to check if a specific point was contained inside another shape and if it's the case, skip it. However, that would probably not work since moveTo/lineTo/stroke would create a line between the last independant point before a shape and the first independant after a shape.
I've looked up compositing operations that might be able to help, but haven't found a way to implement it yet.
How could I approach this feature ?
Input
Output

The following code is not a complete solution. It uses Path2D objects (still marked as an "experimental technology" by MDN) and the isPointInPath() canvas function.
// Canvas boilerplate
const canvas = document.querySelector('#mycanvas');
const ctx = canvas.getContext('2d');
// Create our shapes
const shape1 = [[100, 100], [70, 200], [150, 250], [300, 250], [300, 100], [150, 50]];
const shape2 = [[60, 60], [60, 160], [110, 210], [260, 210], [260, 60], [110, 10]];
// Create Path2D objects from our shapes
let p1 = new Path2D();
p1.moveTo(...shape1[0]);
for (let i = 1; i < shape1.length; i++) {
p1.lineTo(...shape1[i]);
}
p1.closePath();
let p2 = new Path2D();
p2.moveTo(...shape2[0]);
for (let i = 1; i < shape2.length; i++) {
p2.lineTo(...shape2[i]);
}
p2.closePath();
// Draw the shapes onb the canvas
ctx.fillStyle = 'white';
ctx.lineWidth = 4;
ctx.strokeStyle = 'green';
ctx.stroke(p1);
ctx.strokeStyle = 'blue';
ctx.stroke(p2);
// Just for this demo, fill the shapes to make them look joined together
ctx.fill(p2);
ctx.fill(p1);
// Discover whether coordinate points are inside/outside the other shape
let inside = [],
outside = [];
for (let i = 0; i < shape2.length; i++) {
if (ctx.isPointInPath(p1, ...shape2[i])) inside.push(shape2[i]);
else outside.push(shape2[i]);
}
for (let i = 0; i < shape1.length; i++) {
if (ctx.isPointInPath(p2, ...shape1[i])) inside.push(shape1[i]);
else outside.push(shape1[i]);
}
// Display coordinates inside
ctx.fillStyle = 'red';
for (let i = 0; i < inside.length; i++) {
ctx.beginPath();
ctx.moveTo(...inside[i]);
ctx.arc(...inside[i], 5, 0, 2 * Math.PI);
ctx.fill();
}
// ... and outside
ctx.fillStyle = 'black';
for (let i = 0; i < outside.length; i++) {
ctx.beginPath();
ctx.moveTo(...outside[i]);
ctx.arc(...outside[i], 5, 0, 2 * Math.PI);
ctx.fill();
}
<canvas id="mycanvas"></canvas>
To calculate a third shape which combines the coordinates of a path outlining the merged shape you need to work out which lines in each shape intersect, and where, then combine those new coordinates with your existing outside coordinates in the right order. Best of luck!

Related

How can I plot coordinates of x and y axes that are very close on html5 canvas?

I am trying to plot some points on canvas through x and y coordinates. First codepen I have created does not plot all points, I think it has scale issue. But can not figure out how to set proper scale.
Here in my second codepen all points are plotted very closely. The points plotted creates a text hello zap
Here in this jsfiddle I have plotted all points through scatter chart. Please refer codepen and fiddle for all data.Can anyone please suggest me a proper way to plot this points properly.
Thank You.
//html code
<canvas id="canvas"></canvas>
//js code
var canvas = document.getElementById('canvas'),
context = canvas.getContext('2d'),
width = canvas.width = 800,
height = canvas.height = 400;
// var stats = [40, 65, 72, 120, 250, 87, 100, 42];
var stats = [
{
"x": 54.75,
"y": 71,
"dotType": 17,
"pressure": 19,
"timestamp": 1535708931610
},
{
"x": 54.7599983215332,
"y": 71,
"dotType": 18,
"pressure": 27,
"timestamp": 1535708931619
}
]
context.translate(0, height);
context.scale(1, -1);
context.fillStyle = '#f6f6f6';
context.fillRect(0, 0, width, height);
var left = 0,
prev_stat = stats[0].y,
move_left_by = 100;
for(stat in stats) {
the_stat = stats[stat].y;
console.log(left, prev_stat);
console.log(left+move_left_by, the_stat)
context.beginPath();
context.arc(left+move_left_by, the_stat,1, 0, Math.PI * 2, true);
context.stroke();
prev_stat = the_stat;
left += move_left_by
}
Edit:
This data is from a neoPen, whenever I write something on a paper with that pen on A4 size paper it send me coordinates of that page.This points are near because just a text written on a page.
I am looking at your second codepen.
First you have the data string that you are breaking into points and you put those points in the canvasPts array.
Next you are redeclaring the points of the canvasPts, deleting all the pervious points.
Furthermore: the points of the canvasPts are all almost in the same spot. Please take a look at the values for the x and y.
And this is not all. You are dividing the values for x and y coordinates by 100, making them even nearer.
You do not declare a size for your canvas, making your canvas of 300/150 px.
Supposing I would try to draw the SVG path for your data, this appears to be a group of lines with a length of 0. There is nothing to draw.
Please edit your question explaining how did you get your data.
UPDATE:
In order to avoid cluttering I've put your data in an external file.
I'm doing it 2 ways:
first in svg: SVG is easily scalable, and was easier for me tu understand what happens. The vewBox for the svg is viewBox="53.5 68 12 5" which means that the svg canvas begins at x=53.5, y=68. The width of the svg canvas is 68 and the height is 5.
In Canvas-HTML5: I'm scaling the context 10 times since otherwise it would be extrmely small: ctx.scale(10,10); In order to achieve the same result in canvas and since I'm translating the context ctx.translate(-53.5, -68.0);
//SVG
let d=`M${data[0].x},${data[0].y}L`
for(let i = 1; i < data.length; i++){
d += `${data[i].x},${data[i].y} `
}
test.setAttributeNS(null, "d", d);
//canvas//////
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
let cw = canvas.width = 120;
let ch = canvas.height = 50;
ctx.lineWidth = .1;
ctx.strokeStyle = "black";
ctx.scale(10,10);
ctx.translate(-53.5, -68.0);
ctx.beginPath();
ctx.moveTo(data[0].x,data[0].y);
for(let i = 1; i < data.length; i++){
ctx.lineTo(data[i].x,data[i].y);
}
ctx.stroke();
svg,canvas{border:1px solid}
path{fill: none; stroke:black; stroke-width:.05}
<svg viewBox="53.5 68 12 5">
<path id="test" />
</svg>
<canvas></canvas>
<script src='https://codepen.io/enxaneta/pen/dd442277a45b6cf1b5cc690200cdb3cf.js'></script>

How to draw a line with repeated shapes/markers in canvas?

I'm looking for a way to lines with markers in a <canvas> element, like the picture bellow.
In SVG we can do that thanks the markers but I haven't found a similar way to do that with canvas. I know that we can create patterns in canvas thanks to the createPattern function but I doubt it could help to solve the issue.
EDIT: This is not a duplicate of this question since I'm looking for a way to repeat a shape/marker on a path. It's not about placing marker at a specific given point.
As I've commented bellow, I've discovered the svg-path-properties which is almost perfect for my solution.
There is unfortunately no native way to add markers to strokes in the canvas API. Even if we are able to set strokeStyle to a CanvasPattern, there is no way to make this pattern follow our path's direction.
This means that you'll have to make the calculations of the direction and position of your markers yourself...
For this, you can refer to many posts, like this one proposed by Tomàs Antunes in comments.
However, to do the exact triangle shape you shown to us, I've got an non-mathy hack, which doesn't work very well, but that I'd like to share anyway.
Strokes can have dash-arrays, which will create gaps in the stroke.
By offsetting multiple times this dash-array, and decreasing the lineWidth of our stroke, we can sort-of create these arrow shapes :
var ctx = c.getContext('2d');
// our default path
ctx.beginPath();
ctx.moveTo(50, 20);
ctx.bezierCurveTo(230, 30, 150, 60, 50, 100);
ctx.bezierCurveTo(250, 50, 150, 60, 150, 150);
ctx.stroke();
// the hack
for (var i = 0; i < 9; i += .5) {
// set an dasharray of 1px visible, then 40 invisible
ctx.setLineDash([1, 49]);
// offset the dash-array by one px
ctx.lineDashOffset = i;
// reduce the width of our stroke
ctx.lineWidth = i;
ctx.stroke();
}
<canvas id="c"></canvas>
But the main caveat of this method (apart from requiring to redraw the same path 20 times) is that it will really follow the path, and that in angles, it may not completely look like our triangle shape anymore.
var ctx = c.getContext('2d');
// our default path
ctx.beginPath();
ctx.moveTo(50, 20);
ctx.bezierCurveTo(230, 30, 150, 60, 50, 100);
ctx.stroke();
// the hack
for (var i = 0; i < 9; i += .5) {
// set an dasharray of 1px visible, then 20 invisible
ctx.setLineDash([1, 14]);
// offset the dash-array by one px
ctx.lineDashOffset = i;
// reduce the width of our stroke
ctx.lineWidth = i*2;
ctx.stroke();
}
<canvas id="c"></canvas>

How to track path of stickman on canvas?

I'd like to keep track of my stickman if I were to move it. However since my stickman is a bunch of lines and I believe the only way I can do this is by checking if certain pixels are of a certain color. Is there a better way of keep track of where my stickman is located on the canvas? I was told that if my stickman were an object my goal would be easier to reach. That said, I thought my stickman was of type object literal already. Any help would be appreciated thank you!
stickman = {head: [200, 200, 10,0, 2*Math.PI ],
body: [195, 210, 178, 250],
rightArm: [192,215,200,230,210,230],
leftArm: [192,215,178 ,222,178,230],
rightLeg: [178, 250,190,260,185,275,192, 275],
leftLeg: [178, 250, 168, 260, 155, 262,153, 268]
} ;
function costume1(){
context.strokeStyle = "rgb(0,0,0)";
context.beginPath();
//head
context.arc(stickman.head[0], stickman.head[1], stickman.head[2], stickman.head[3], stickman.head[4]);
//body
context.moveTo(stickman.body[0],stickman.body[1]);
context.lineTo(stickman.body[2],stickman.body[3]);
//right arm
context.moveTo(stickman.leftArm[0],stickman.leftArm[1]);
context.lineTo(stickman.leftArm[2] ,stickman.leftArm[3]);
context.lineTo(stickman.leftArm[4], stickman.leftArm[5]);
//left arm
context.moveTo(stickman.rightArm[0], stickman.rightArm[1]);
context.lineTo(stickman.rightArm[2], stickman.rightArm[3]);
context.lineTo(stickman.rightArm[4] , stickman.rightArm[5]);
//left leg
context.moveTo(stickman.rightLeg[0], stickman.rightLeg[1]);
context.lineTo(stickman.rightLeg[2],stickman.rightLeg[3]);
context.lineTo(stickman.rightLeg[4] , stickman.rightLeg[5]);
context.lineTo(stickman.rightLeg[6], stickman.rightLeg[7]);
//right leg
context.moveTo(stickman.leftLeg[0], stickman.leftLeg[1]);
context.lineTo(stickman.leftLeg[2], stickman.leftLeg[3]);
context.lineTo(stickman.leftLeg[4], stickman.leftLeg[5]);
context.lineTo(stickman.leftLeg[6] , stickman.leftLeg[7]);
context.stroke();
}
You are right - you have indeed created a stickman object. But if you want to move/track your stickman, it would be best to define the parts (the head, body, etc) in terms of a single, central point - for example you could use the centre of his head. Then to move/track the stickman, all you need to do is update those central points. The other parts of the stickman will then follow along.
Here is a demonstration of what I mean:
// set up a stickman, with a starting x and y
var Stickman = function(x, y) {
this.update(x, y);
}
// anytime you need to know the new positions for the
// stickman, call .update(newCenterX, newCenterY)
Stickman.prototype.update = function(x, y) {
this.centerX = x;
this.centerY = y;
this.head = [this.centerX, this.centerY, 10,0, 2*Math.PI ]
this.body = [
this.centerX-5,
this.centerY+10,
this.centerX-22,
this.centerY+50
]
this.rightArm = [ ];
this.leftArm = [ ];
// etc...
}
// here is how to make a new stickman
var man1 = new Stickman(200, 200);
// and move him!
console.log(man1.body);
man1.update(210, 200);
console.log(man1.body);`
Hope that helps! (I may not have got the offsets right :-) )
You typically use context.translate to move your fixed-coordinate stickman to a different position.
// move the context origin 100px rightward
context.translate(100,0);
// redraw the stickman (it will be 100px rightward of the original)
costume1();
But if you actually want a version of the stickman with your original fixed-coordinates changed to new "moved" fixed-coordinates, you can send your original stickman into a conversion function that changes the coordinates for you.
To make tracking any stickman easier, add an x: & y: property to every stickman that indicates how this stickman is offset-X & offset-Y from the original stickman.
Here's example code and a demo:
var canvas=document.getElementById("canvas");
var context=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
stickman = {
x:0,y:0,
head: [200, 200, 10,0, 2*Math.PI ],
body: [195, 210, 178, 250],
rightArm: [192,215,200,230,210,230],
leftArm: [192,215,178 ,222,178,230],
rightLeg: [178, 250,190,260,185,275,192, 275],
leftLeg: [178, 250, 168, 260, 155, 262,153, 268]
} ;
// draw original stickman
costume1(stickman,'black');
// move the stickman's x,y
stickman.x=-50;
stickman.y=-50;
// get the coordinates of the translated stickman
var stickman1={ x:stickman.x, y:stickman.y };
translateStickman(stickman,stickman1);
// draw the moved stickman1
costume1(stickman1,'red');
function costume1(stickman,strokecolor){
// move the canvas origin to the stickman's x,y
context.translate(stickman.x,stickman.y);
context.strokeStyle = strokecolor;
context.beginPath();
//head
context.arc(stickman.head[0], stickman.head[1], stickman.head[2], stickman.head[3], stickman.head[4]);
//body
context.moveTo(stickman.body[0],stickman.body[1]);
context.lineTo(stickman.body[2],stickman.body[3]);
//right arm
context.moveTo(stickman.leftArm[0],stickman.leftArm[1]);
context.lineTo(stickman.leftArm[2] ,stickman.leftArm[3]);
context.lineTo(stickman.leftArm[4], stickman.leftArm[5]);
//left arm
context.moveTo(stickman.rightArm[0], stickman.rightArm[1]);
context.lineTo(stickman.rightArm[2], stickman.rightArm[3]);
context.lineTo(stickman.rightArm[4] , stickman.rightArm[5]);
//left leg
context.moveTo(stickman.rightLeg[0], stickman.rightLeg[1]);
context.lineTo(stickman.rightLeg[2],stickman.rightLeg[3]);
context.lineTo(stickman.rightLeg[4] , stickman.rightLeg[5]);
context.lineTo(stickman.rightLeg[6], stickman.rightLeg[7]);
//right leg
context.moveTo(stickman.leftLeg[0], stickman.leftLeg[1]);
context.lineTo(stickman.leftLeg[2], stickman.leftLeg[3]);
context.lineTo(stickman.leftLeg[4], stickman.leftLeg[5]);
context.lineTo(stickman.leftLeg[6] , stickman.leftLeg[7]);
context.stroke();
// always clean up, unto the last translate
// == move the canvas origin back to 0,0
context.translate(-stickman.x,-stickman.y);
}
// create a new stickman with moved coordinates
function translateStickman(stickman,trxStickman){
var x=stickman1.x;
var y=stickman1.y;
var translate=function(a){
for(var i=0;i<a.length;i+=2){
a[i]+=x;
a[i+1]+=y;
}
}
trxStickman.head=stickman.head.slice();
trxStickman.body=stickman.body.slice();
trxStickman.rightArm=stickman.rightArm.slice();
trxStickman.leftArm=stickman.leftArm.slice();
trxStickman.rightLeg=stickman.rightLeg.slice();
trxStickman.leftLeg=stickman.leftLeg.slice();
trxStickman.head[0]+=x;
trxStickman.head[1]+=y;
translate(trxStickman.body);
translate(trxStickman.rightArm);
translate(trxStickman.leftArm);
translate(trxStickman.rightLeg);
translate(trxStickman.leftLeg);
}
body{ background-color: ivory; }
#canvas{border:1px solid red; margin:0 auto; }
<h4>Black == original stickman, Red == moved stickman</h4>
<canvas id="canvas" width=300 height=300></canvas>

How can I constrain drawing on the <canvas>?

I want to create a mobile web page where a shape appears on the screen, the user can only traces over the outline of the shape with his/her finger and then a new shape will appear. This library has a few good examples of what I am looking to do, just with more shapes. I have already found a couple of good examples for drawing on the canvas on a touch device here and here. The thing I don't know is how to constrain the line so you are only drawing on the path with a single continuous line. Is there something built in that will let me specify the only path you can draw, or do I have to write that logic by hand?
We can split the issue into two parts :
1) knowing if the user is on the path.
2) knowing if the user went on all path parts.
For 1), we can use the isPointInPath context2D method to know if the mouse/touch point (x,y) is on the curve. The constraint here is that you must build a closed surface, meaning a surface drawn by a fill(), not one built with a stroke(). So in case you are stroking thick lines, you have to do some little math to build the corresponding figures out of moveTo+lineTo+fill.
For 2) : build a list of 'check-points' for your shape. You might have, for instance 8 control points for a circle. Then decide of a distance at which the user will 'activate' the check point. Now the algorithm is, in pseudo-code:
mouseDown => check()
mouseMove => if mouse is down, check()
checkPointList = [ [ 10, 40, false ] , [ centerX, centerY, isChecked], ... ] ;
checked = 0;
function check() {
clear screen
draw the path
if (mouse down and mouse point on path) {
for ( checkPoint in CheckPointList) {
if (checkPoint near enough of mouse) {
checkPoint[3]=true;
checked++;
}
}
if (checked == checkPointList.length) ==>>> User DID draw all the shape.
} else
clear the flags of the checkPointList;
checked=0;
}
I did a moooost simple demo here, which quites work.
The control points are shown in red when disactivated, green when activated :
http://jsbin.com/wekaxiguwiyo/1/edit?js,output
// boilerplate
var cv = document.getElementById('cv');
var ctx = cv.getContext('2d');
function draw() {
ctx.clearRect(0,0,300,300);
drawShape();
drawCP();
}
// Shape
function drawShape() {
ctx.beginPath();
ctx.moveTo(30,5);
ctx.lineTo(80,5);
ctx.lineTo(80, 300);
ctx.lineTo(30,300);
ctx.closePath();
ctx.lineWidth= 16;
ctx.fillStyle='#000';
ctx.fill();
}
// Control points
var points = [ [50, 50, false], [50,120, false], [50, 190, false],[50,260, false ] ];
var pointsCount = 0;
function drawCP() {
for (var i=0; i<points.length; i++) {
var p = points[i];
ctx.fillStyle=p[2]?'#0F0':'#F00';
ctx.fillRect(p[0]-1, p[1]-1, 2, 2);
}
}
function resetCP() {
for (var i=0; i<points.length; i++) {
points[i][2]=false;
}
pointsCount=0;
}
function testCP(x,y) {
var d=30;
d=sq(d);
for (var i=0; i<points.length; i++) {
if (sq(points[i][0]-x)+sq(points[i][1]-y)<d) {
if (!points[i][2]) pointsCount++;
points[i][2]=true
};
}
}
function sq(x) { return x*x; }
//
draw();
// most simple event handling
addEventListener('mousemove', mouseMove);
var r = cv.getBoundingClientRect();
function mouseMove(e) {
var x = e.pageX-r.left;
var y = e.pageY-r.top;
draw();
ctx.fillStyle='#000';
if (ctx.isPointInPath(x,y)) { 
ctx.fillStyle='#F00';
testCP(x,y);
} else {
resetCP();
}
ctx.fillRect(x-3,y-3,6,6);
var pathDrawn = (pointsCount == points.length);
if (pathDrawn) ctx.fillText('Shape drawn!!', 150, 150);
}

How to create overlapping - not stacking - shapes with canvas?

I'm trying to create an array of shapes that overlap. But I'm having difficulty preventing those shapes stacking on top of one-another.
I guess I want them to mesh together, if that makes sense?
Here's the code:
var overlap_canvas = document.getElementById("overlap");
var overlap_context = overlap_canvas.getContext("2d");
var x = 200;
var y = x;
var rectQTY = 4 // Number of rectangles
overlap_context.translate(x,y);
for (j=0;j<rectQTY;j++){ // Repeat for the number of rectangles
// Draw a rectangle
overlap_context.beginPath();
overlap_context.rect(-90, -100, 180, 80);
overlap_context.fillStyle = 'yellow';
overlap_context.fill();
overlap_context.lineWidth = 7;
overlap_context.strokeStyle = 'blue';
overlap_context.stroke();
// Degrees to rotate for next position
overlap_context.rotate((Math.PI/180)*360/rectQTY);
}
And here's my jsFiddle:
http://jsfiddle.net/Q8yjP/
And here's what I'm trying to achieve:
Any help or guidance would be greatly appreciated!
You cannot specify this behavior but you can implement an algorithmic-ish approach that uses composite modes.
As shown in this demo the result will be like this:
Define line width and the rectangles you want to draw (you can fill this array with the loop you already got to calculate the positions/angles - for simplicity I just use hard-coded ones here):
var lw = 4,
rects = [
[20, 15, 200, 75],
[150, 20, 75, 200],
[20, 150, 200, 75],
[15, 20, 75, 200]
], ...
I'll explain the line width below.
/// set line-width to half the size
ctx.lineWidth = lw * 0.5;
In the loop you add one criteria for the first draw which is also where you change composite mode. We also clear the canvas with the last rectangle:
/// loop through the array with rectangles
for(;r = rects[i]; i++) {
ctx.beginPath();
ctx.rect(r[0], r[1], r[2], r[3]);
ctx.fill();
ctx.stroke();
/// if first we do a clear with last rectangle and
/// then change composite mode and line width
if (i === 0) {
r = rects[rects.length - 1];
ctx.clearRect(r[0] - lw * 0.5, r[1] - lw * 0.5, r[2] + lw, r[3] + lw);
ctx.lineWidth = lw;
ctx.globalCompositeOperation = 'destination-over';
}
}
This will draw the rectangles and you have the flexibility to change the sizes without needing to recalculate clipping.
The line-width is set separately as stroke strokes the line from the middle. Therefor, since we later use destination-over mode it means half of the line won't be visible as we first fill which becomes part of destination so that the stroke will only be able to fill outside the stroked area (you could reverse the order of stroke and fill but will always run into an adjustment for the first rectangle).
We also need it to calculate the clipping which must include (half) the line on the outside.
This is also why we initially set it to half as the whole line will be drawn the first time - otherwise the first rectangle will have double as thick borders.
The only way to do it to cut your rectangles and compute which sub rectangle goes over which one. But I think you will have to draw your borders and inner rectangles separately because separating rectangles will add additional borders.
Hope it helped
Sadly, the feature you want of setting z-indexes on part of an element using canvas is not available currently. If you just need it for the four rectangle object you could do something like this which hides part of the rectangle to fake the effect you want, however this is hard coded to only 4 rectangles.
var overlap_canvas = document.getElementById("overlap");
var overlap_context = overlap_canvas.getContext("2d");
var x = 200;
var y = x;
var rectQTY = 4 // Number of rectangles
overlap_context.translate(x, y);
for (j = 0; j < rectQTY; j++) { // Repeat for the number of rectangles
// Draw a rectangle
overlap_context.beginPath();
overlap_context.rect(-90, -100, 180, 80);
overlap_context.fillStyle = 'yellow';
overlap_context.fill();
overlap_context.lineWidth = 7;
overlap_context.strokeStyle = 'blue';
overlap_context.stroke();
if (j === 3) {
overlap_context.beginPath();
overlap_context.rect(24, -86, 72, 80);
overlap_context.fillStyle = 'yellow';
overlap_context.fill();
overlap_context.closePath();
overlap_context.beginPath();
overlap_context.moveTo(20, -89.5);
overlap_context.lineTo(100, -89.5);
overlap_context.stroke();
overlap_context.closePath();
overlap_context.beginPath();
overlap_context.moveTo(20.5, -93.1);
overlap_context.lineTo(20.5, 23);
overlap_context.stroke();
overlap_context.closePath();
}
// Degrees to rotate for next position
overlap_context.rotate((Math.PI / 180) * 360 / rectQTY);
}
Demo here
If you have to make it dynamic, you could cut the shapes like Dark Duck suggested or you could try to create a function that detects when an object is overlapped and redraw it one time per rectangle (hard to do and not sure if it'd work). Perhaps you could come up with some equation for positioning the elements in relation to how I have them hard coded now to always work depending on the rotation angle, this would be your best bet IMO, but I don't know how to make that happen exactly
Overall you can't really do what you're looking for at this point in time
Using pure JavaScript ...
<!DOCTYPE html>
<html>
<head></head>
<body>
<canvas id="mycanvas" width="400px" height="400px"></canvas>
<script>
window.onload = function(){
var canvas = document.getElementById('mycanvas');
var ctx = canvas.getContext('2d');
//cheat - use a hidden canvas
var hidden = document.createElement('canvas');
hidden.width = 400;
hidden.height = 400;
var hiddenCtx = hidden.getContext('2d');
hiddenCtx.strokeStyle = 'blue';
hiddenCtx.fillStyle = 'yellow';
hiddenCtx.lineWidth = 5;
//translate origin to centre of hidden canvas, and draw 3/4 of the image
hiddenCtx.translate(200,200);
for(var i=0; i<3; i++){
hiddenCtx.fillRect(-170, -150, 300, 120);
hiddenCtx.strokeRect(-170, -150, 300, 120);
hiddenCtx.rotate(90*(Math.PI/180));
}
//reset the hidden canvas to original status
hiddenCtx.rotate(90*(Math.PI/180));
hiddenCtx.translate(-200,-200);
//translate to middle of visible canvas
ctx.translate(200, 200);
//repeat trick, this time copying from hidden to visible canvas
ctx.drawImage(hidden, 200, 0, 200, 400, 0, -200, 200, 400);
ctx.rotate(180*(Math.PI/180));
ctx.drawImage(hidden, 200, 0, 200, 400, 0, -200, 200, 400);
};
</script>
</body>
</html>
Demo on jsFiddle

Categories