Canvas javascript clear path - html5-canvas

I am testing out a little Canvas programming and I have a question about how to clear previous paths when drawing a new one.
Example:
Paint a filled circle with no stroke and a stroked line.
I have the following code:
graphicContext.save();
graphicContext.beginPath();
graphicContext.arc(95,50,40,0,2*Math.PI);
graphicContext.fillStyle="rgb(50, 200, 200)";
graphicContext.fill();
graphicContext.restore();
graphicContext.save();
graphicContext.moveTo(0,0);
graphicContext.lineTo(200,100);
graphicContext.stroke();
graphicContext.restore();
However, both the circle and the line are stroked. How can I stroke the line without also stroking the circle?

First of all, there is no need to use save and restore method.
Second, use beginPath method before drawing the line, as you are creating a new path. beginPath will reset / clear the previous path ...
var graphicContext = document.querySelector('#canvas').getContext('2d')
// circle
graphicContext.beginPath();
graphicContext.arc(95,50,40,0,2*Math.PI);
graphicContext.fillStyle="rgb(50, 200, 200)";
graphicContext.fill();
// line
graphicContext.beginPath();
graphicContext.moveTo(0,0);
graphicContext.lineTo(200,100);
graphicContext.stroke();
<canvas id="canvas"></canvas>

Related

draw line from one canvas to another canvas

I have multiple canvases and want to draw line between two points on canvas,in which one point is on one canvas and other point is on second(different) canvas.
How to draw line with above condition?
ctx.beginPath();
ctx.moveTo(10,30);//point on canvas1
ctx.lineTo(100,70);//point on canvas2
ctx.stroke();
To do this use the ctx.setTransform function of the second canvas to that of the first
function matchCoordinateSpace(ctx1,ctx2){
// get the window positions of both canvases
var can1Loc = ctx1.canvas.getBoundingClientRect();
var can2Loc = ctx2.canvas.getBoundingClientRect();
// get the relative offset
var can2Rel = {
x:can1Loc.left-can2loc.left,
y:can1Loc.top-can2loc.top,
};
// set the transforms
ctx2.setTransform(1,0,0,1,0,0); // just to make sure the first canvas coordinate space
// is correctly set.
ctx2.setTransform(1,0,0,1,can2Rel.x,can2Rel.y); // translates the canvas to the
// same coordinates as the firstChild
// The second canvas now is in the same relative coordinate space as the first;
// All drawing methods on the second canvas will be in the first canvas coordinate space.
// You only need to do this once, or whenever the two canvases change relative position.
return can2Rel; // incase you need the offset
}
//ctx1 is the first canvas
//ctx2 is the second
var can2Offset = matchCoordinateSpace(ctx1,ctx2);
// You will have to draw the line on each canvas in turn
ctx1.beginPath();
ctx1.moveTo(10,30);
ctx1.lineTo(100,70);
ctx1.stroke();
ctx2.beginPath(); // this line will match up with the line drawn on the first.
ctx2.moveTo(10,30);
ctx2.lineTo(100,70);
ctx2.stroke();

Hit testing against text shapes

I want to know whether a given point is inside or outside of a text shape. As you will notice in the sample I provided below, hitTest will return true as soon as the point is inside of the TextItem's bounds, and not only if the point is inside of the character itself. (You can experience this behavior best when you place your mouse pointer in the middle of the #)
Sample: Hit-testing against TextItem
I also tried drawing the character based on paths (as Raphaƫl is doing in their font samples) to use the paths itself for hit-testing but stumbled upon some quite strange behavior where (some) characters are not drawn correctly. (If you copy the path definition into a vector image software like Inkscape the text shapes are drawn correctly)
Sample: Drawing text as path
What is the most promising way to find out whether a given point is inside or outside of a text shape?
You can hit-test a text shape (or any other mathematically irregular shape) by texting whether the pixel under the mouse is transparent or not.
You can get the pixel-array for the entire canvas using:
var data=context.getImageData(0,0,canvas.width,canvas.height).data;
Then you can fetch the opacity (alpha) value for the pixel under the mouse like this:
var pixelIsTransparent = data[(mouseY*canvas.width+mouseX)*4+3]==0
If the pixel is not transparent then you're over the text shape.
If you have other non-text drawings on the canvas then those non-text drawings might give false-positives for your hit-tests. A workaround for that is to use a second in-memory canvas containing only your text-shape and then do hit testing against the pixels on that second canvas.
Here's example code and a Demo:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
var $canvas=$("#canvas");
var canvasOffset=$canvas.offset();
var offsetX=canvasOffset.left;
var offsetY=canvasOffset.top;
ctx.strokeStyle='gray';
ctx.font='300px verdana';
var wasHit=false;
var isHit=false;
draw();
var data=ctx.getImageData(0,0,canvas.width,canvas.height).data;
$("#canvas").mousemove(function(e){handleMouseMove(e);});
function draw(){
ctx.fillStyle=(isHit)?'green':'lightgray';
ctx.fillText("M",25,250);
ctx.strokeText("M",25,250);
}
function handleMouseMove(e){
e.preventDefault();
e.stopPropagation();
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
isHit=(data[(mouseY*cw+mouseX)*4+3]>10)
if(!isHit==wasHit){
draw();
wasHit=isHit;
}
}
body{ background-color: ivory; }
#canvas{border:1px solid red;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<p>Hit test: Move mouse over letter</p>
<canvas id="canvas" width=300 height=300></canvas>
After spending quite some time debugging paper.js code I finally found the solution for this problem.
Instead of using Path you are supposed to use CompoundPath:
A compound path contains two or more paths, holes are drawn where the paths overlap. All the paths in a compound path take on the style of the backmost path and can be accessed through its item.children list.
I also updated the example from above:
http://jsfiddle.net/64v7s6L9/1/

Need to draw arrows and circles on canvas without clearing the whole canvas

I'm trying to draw arrows and circles on a canvas, currently the whole canvas is cleared on mousemove and mousedown or whenever the draw function is called, I am not able to draw multiple arrows and circles. Is there any other to accomplish this task?
heres a fiddle: http://jsfiddle.net/V7MRL/
Stack two canvases on top of each over, and draw the temporary arrows/circle on the one on top, and do the final draw on the canvas below.
This way you can clear the top canvas with no issue, and your draws 'accumulate' in the lower canvas.
http://jsfiddle.net/V7MRL/5/ (updated)
I changed your draw function so it takes a 'final' flag that states wether the draw is final.
For a final draw, lower canvas is used, for an temporary draw, upper canvas is cleared then used.
function draw(final) {
var ctx = final ? context : tempContext ;
if (final == false) ctx.clearRect(0, 0, canvas.width, canvas.height);
Edit : for the issue #markE mentionned, i just handled mouseout event to cancel the draw if one is ongoing :
function mouseOut(eve) {
if ( mouseIsDown ) {
mouseIsDown = 0;
cancelTempDraw();
}
}
with :
function cancelTempDraw() {
tempContext.clearRect(0, 0, canvas.width, canvas.height);
}
Rq that your undo logic is not working as of now. I changed it a bit so that in draw, if the draw is final, i save the final canvas prior to drawing the latest figure to quickly have a 1-step undo. i just created a third temp canvas to/from which you copy.
Rq also that you cannot store one canvas for each stroke, or the memory will soon explode. Store the figures + their coordinates in an array if you want a full undo... or just allow 1-step undo for now.
i updated the jsfiddle link.

d3 autospace overlapping tick labels

Is there a way in d3 to not draw overlapping tick labels? For example, if I have a bar chart, but the bars are only 5 pixels wide and the labels are 10 pixels wide, I end up with a cluttered mess. I'm currently working on an implementation to only draw the labels when they do not overlap. I can't find any existing way to do that, but wasn't sure if anyone else had dealt with this problem.
There is no way of doing this automatically in D3. You can set the number of ticks or the tick values explicitly (see the documentation), but you'll have to figure out the respective numbers/values yourself. Another option would be to rotate the labels such that there is less chance of them overlapping.
Alternatively, like suggested in the other answer, you could try using a force layout to place the labels. To clarify, you would use the force layout on the labels only -- this is completely independent of the type of chart. I have done this in this example, which is slightly more relevant than the one linked in the other answer.
Note that if you go with the force layout solution, you don't have to animate the position of the labels. You could simply compute the force layout until it converges and then plot the labels.
I've had a similar problem with multiple (sub-)axis, where the last tick overlaps my vertical axis in some situations (depending on the screen width), so I've just wrote a little function that compares the position of the end of the text label with the position of the next axis. This code is very specific to my use case, but could adapted easily to your needs:
var $svg = $('#svg');
// get the last tick of each of my sub-axis
$('.tick-axis').find('.tick:last-of-type').each(function() {
// get position of the end of this text field
var endOfTextField = $(this).offset().left + $(this).find('text').width();
// get the next vertical axis
var $nextAxis = $('line[data-axis="' + $(this).closest('.tick-axis').attr('data-axis') + '"]');
// there is no axis on the very right, so just use the svg width
var positionOfAxis = ($nextAxis.length > 0) ? $nextAxis.offset().left : $svg.offset().left + $svg.width();
// hide the ugly ones!
if (endOfTextField > positionOfAxis) {
$(this).attr('class', 'tick hide');
}
});
The ticks with color: aqua are the hidden ones:

HTML Canvas clip area - Context restore?

Am trying to set a "dirty zone" on my canvas to prevent the repainting of unmoved items (background image, static items, etc.)
i.e. only the background painted behind a moving player needs to be redrawn
EDIT: As suggested, here's the jsfiddle of it
http://jsfiddle.net/7kbzj/3/
The "update" method doesn't work out there, so it's moveSprite() you can get run by clicking the "move sprite" link... Basically, the clipping zone shouldmove by 10px to the right each time you click. Clipping mask stays at initial position, only the re-paint occurs. Weird o_O
So as I init my canvas, once the background is painted, set I use the ctx.save() method:
function init() {
canvas = document.getElementById('kCanvas');
ctx = canvas.getContext('2d');
ctx.fillStyle = "rgb(0,128,0)";
ctx.fillRect (0,0,320,240);
ctx.save();
setInterval(function () { update(); }, tpf);
}
In order to see the clipping works, I draw a different color background (blue one) in the area that I wanted clipped... the result is bad, only the first clipped area is painted blue :(
function update() {
setDirtyArea(x,y,w+1,h)
ctx.fillStyle = "rgb(0,0,128)";
ctx.fillRect (0,0,320,240);
x++;
// paint image
ctx.clearRect(x,y,w,h);
ctx.drawImage(imageObj, x, y);
}
function setDirtyArea(x,y,w,h) {
ctx.restore();
// define new dirty zone
ctx.beginPath();
ctx.rect(x, y, w, h);
ctx.clip();
}
I'd love to se the blue zone propagate itself towards the right of the screen... please help, I don't understand what's wrong!
Thanks,
J.
You need to wrap the actual drawing and clipping of the box with the save and restore methods. and include the closePath method. I have modified your fiddle to work the way I believe you are trying to make it.
http://jsfiddle.net/jaredwilli/7kbzj/7/
ctx.save(); // save the context
// define new dirty zone
ctx.beginPath();
ctx.rect(x, y, w, h);
ctx.clip();
ctx.restore(); // restore the context
I also have learned that using save and restore can get really complex, and confusing to know which context your in. It came up with a pretty huge canvas app im working on, and i found that indenting your canvas code helps immensely. Especially the save/restores. I have even decided it should be considered a best practice, so the more people who know and do it the better.
Hope this helps.

Resources