Questionable Positioning of drawn Lines in Canvas - javascript

I have some question regarding HTML 5 Canvas and I hope you can help me to clear them.
Situation:
I have a canvas and some absolute positioned divs which are children of a relative positioned div.
<div class="relativeposition">
<canvas id="drawarea" height="700" width="800"></canvas>
<div id="absoluteposition1"></div>
<div id="absoluteposition2"></div>
<div id="absoluteposition3"></div>
</div>
So you can see the canvas and on the canvas there are the 3 different divs. Now I want to draw a polygon under every div in an each loop. The position of this polygon should almost be centered under the div. Under every div this position should be equal to the others divs.
The red caros are the positioned divs and just imagine that they are at the same position under every div. The Pseudo Code for drawing these caros is the following:
$.each(absolutepositionedDivs, function(index, value) {
var divtop = absolutepositionedDiv.position().top;
var divleft = absolutepositionedDiv.position().left;
context.beginPath();
context.moveTo(playerLeft + 76, playerTop + 154);
context.lineTo(playerLeft + 46, playerTop + 174);
context.lineTo(playerLeft + 55, playerTop + 190);
context.lineTo(playerLeft + 97, playerTop + 190);
context.lineTo(playerLeft + 106, playerTop + 174);
context.lineTo(playerLeft + 76, playerTop + 154);
context.closePath();
context.lineWidth = 2;
context.strokeStyle = "red";
context.stroke();
});
The initialization of canvas and so isn't in the code but I wrote it and it worked. Just ignore that the outcome of the draw isn't a caro...So the problem now is that the drawn objects aren't in an equal position as you can see in the following picture:
So I don't understand why every drawn object has a different position, even when I wrote that they all start from the same position of their div but end up in a different. Can you tell me why this is happening or have a solution do position all drawn polygons in the same position under their div?
EDIT: Here is a fiddle https://jsfiddle.net/asfga/yjjhqpyL/

Your fiddle is a mess so I needed to clean it up...
First, you have magic numbers spread across HTML javaScript and CSS which is a maintenance nightmare. So I cleaned it up and put all the positioning information into the JS file as that is where it is needed.
The shape was hand coded and with crazy offset that had no meaning. So made the shape an array of points with its origin at center top. (don't need the last point as you were closing the path when not needed).
Then removed jquery as you don't need that and you were not using it properly so effectively slowing your whole page down.
Added divs in code as that is a lot easier than typing all that by hand every time you want to make a change. Stored divs in array so you don't have to query the DOM when you need them later.
You need the canvas position. So I got that via the getBoundingClientRect function, so we can adjust the div positions to fit the canvas coordinates.
Then iterate each div and find its top Left with getBoundingClientRect subtracted the canvas top left and thus had the canvas coordinates of that div. Then as I needed to know where the background image was I added a function to load that background image and query its dimensions.
THAT is when I found out what your problem is.
Your background image is a full sized image with all the pentagons already drawn. It has no relationship the the divs as you don't clip it. You could never line it up unless you had the div coordinates correctly matching the image pentagon positions.
You need to use an image editor and locate each pentagon on the image. Add the locations as an array and draw the shapes at those positions.
I gave up at this point and just added a rectangle over the text in the canvas to show that it is lined up. You will have to workout what you want for the background image. If you want the whole image you will have to find the coordinates for each pentagon by hand. If you just want the first pentagon, clip the image and save one pentagon as an image. Then uncomment the background position rule I commented out and add the magic number offset to the code which will be constant for all divs.
You can find the resulting fiddle here but it will only be up for a short time as I will remove it once you have had a look.

Related

Drawing on canvas in Bootstrap

So I have been having this problem with HTML5 canvas and Bootstrap. Esentially, I inserted a canvas inside a Bootstrap template (put it in an element with the class of "jumbotron"). And I want to be able to draw inside that canvas using my mouse. But, my mouse position and the editable-canvas area seem off. I took a snap-shot (hope I uploaded it correctly), the canvas is the pale-yellow thing with the purple border and the actual mouse position is the arrow I drew with blue, the blue dot is where the page sees the mouse position and begins drawing the line. Also, it won`t let me use the whole canvas area, I can only draw in the green square I outlined in the photo...
HTML code:
<div class="jumbotron">
<canvas id="draw" width="800" height="800"></canvas>
</div>
JS code to activate the drawing:
function draw(e) {
ctx.beginPath();
ctx.moveTo(lastX, lastY);
ctx.lineTo(e.offsetX, e.offsetY);
ctx.stroke();
[lastX, lastY] = [e.offsetX, e.offsetY];
}
I also have to add that if I get the canvas into a new HTML (without Bootstrap), the code works perfectly (dot/line is following the mouse cursor fine). Also, if I delete the class "jumbotron", the mouse Position will be read correctly. I tried looking into the styles for "jumbotron", digged it up, tried using BoundingRect and others, but I could not figure this one out.
I am new here, so take me slow :) Many thanks in advance!
This happens because mouse position is relative to window. You must make some math to calculate coordinates relative to canvas. Some code for understanding
rect = canvas.getBoundingClientRect()
x = e.offsetX - rect.x
y = e.offsetY - rect.y
Then use x, y to draw something on canvas.
Also you can check my example where I draw with mouse without some additional calculations.
https://codepen.io/Profesor08/pen/aYJWRZ

How to clear rectangle on Image in Canvas

I need to clear a rectangle Drawn on Image in Canvas with out damage existing image. I can draw small rectangle points and clear that out. But the problem is when I clear rectangle it remains as white small patch on image.
Can someone tell me how to clear a rectangle on image without damage the existing image.
I have used following methods to clear rectangles but didn't work.
1) context.fillStyle ="white";
2) context.clearRect(xCoordinate, yCoordinate, 10, 08);
Thanks in advance!
Canvas doesn't work that way. It's a single layer, its also transparent by default. So with that in mind, you might be able to achieve what you want by simply giving the canvas element a CSS background. That way anything you draw on top of that background can easily be removed and the background will show through.
#backed-canvas{
background-image: url(http://www.placebear.com/300/200);
}
JSFiddle example: https://jsfiddle.net/yLf5erut/
There is one thing you can do.
When create a rectangle on the canvas just get the image data like:
var imgData = context.getImageData(xCoordinate, yCoordinate, 10, 8);
and draw the rectangle.
When clearing out the rectangle just place then image data back like this:
context.putImageData(imgData, xCoordinate, yCoordinate);
I suggest using 2 canvas elements one over another.
So you can have the original image drawn on the bottom canvas with low zIndex, and the top one with highter zIndex can be used to draw / clear whatever needed. This is a common practice, and for more complecated animations you will end up with better performance.

Circular canvas corners clickable in Chrome

I have two canvases. I have made them circular using border-radius. The 2nd is positioned inside the first one (using absolute position).
I have click events on both circles. If you click on inside canvas, the color at the point of the click is loaded in the outside canvas with opacity varying from white to the picked color and finally to black. If you click on outer canvas the exact color value at that point is loaded in the text-box at the bottom
I am unable to click in red zones (as shown in figure below) of the outer canvas when using chrome. I tried z-idex, arcs but nothing is helping me. But In Firefox everything is working fine.
Note: You can drag the picker object in the outer circle. But if you leave it in red zones, you would not be able to click it again in Chrome. Clicking in green zone will get you its control again
Code in this JSFiddle
Edit
I excluded all irrelevant code to make it easy. Now there is only a container having two canvas.
Filled simply with two distinct colors. Open following fiddle link in both chrome and firefox. Click on both cirles in different zones and see difference in chrome and firefox. I want them to behave in chrome as they do in firefox
Note I will ultimately draw an image in inner canvas.
Updated Fiddle Link
-
Your problem is because canvases currently are always rectangular, even if they don't look rectangular. Border radius makes the edges except the circle transparent, but it still doesn't stop events in Chrome on the corner areas. This is why you cannot click the bottom circle in those areas
I even tried putting it inside of a container that had a border-radius instead but the click event still goes through
With that being said, you have two options. You could either change your code to only use one canvas with the same type of layout, just drawing the background circle before the other each time. Essentially you'd draw a circle, draw your black to color to white gradient, use the xor operation to combine the two into one circle, then do the same with the rainbox gradient. You must draw the background circle first because canvas paints over the old layers every time
or
You could use javascript to only detect clicks in the circular area which takes just a little bit of math (: This solution is featured in edit below
In the future, CSS Shapes may allow canvases to be non-rectangular elements to be used, I'm actually not sure, but we don't have that capability yet at least
Edit
Alright, so after going through your code a bit it seems there are some things I should cover before I offer a solution
Setup all your finite variables outside of the functions that run every time. This means you don't put them (like radiuses, offsets, etc.) in the click function or something that runs often since they don't change
Your "radius"es are actually "diameter"s. The format of .rect goes .rect(x, y, width (diameter of circle), height (diameter of circle))
Almost always when overlaying canvases like you are you want to make them equal dimensions and starting position to prevent calculation error. In the end it makes it easier, doing all relative positioning with javascript instead of mixing it with CSS. In this case, however, since you're using border-radius instead of arc to make a circle, keep it like it is but position it using javascript ....
jQuery isn't needed for something this simple. If you're worried about any load speed I'd recommend doing it in vanilla javascript, essentially just changing the .click() functions into .onclick functions, but I left jQuery for now
You can declare multiple variables in a row without declaring var each time by using the following format:
var name1 = value1,
name2 = value2;
Variables with the same value you can declare like so:
var name1 = name2 = sameValue;
When children have position:absolute and you want it to be positioned relative to the parent, the parent can have position:relative, position:fixed, or position:absolute. I would think you'd want position:relative in this case
When you don't declare var for a variable it becomes global (unlessed chained with a comma like above). For more on that read this question
Now, onto the solution.
After talking with a friend I realized I could sort do the math calculation a lot easier than I originally thought. We can just calculate the center of the circles and use their radiuses and some if statements to make sure the clicks are in the bounds.
Here's the demo
After everything is set up correctly, you can use the following to detect whether or not it's in the bounds of each
function clickHandler(e, r) {
var ex = e.pageX,
ey = e.pageY,
// Distance from click to center
l = Math.sqrt(Math.pow(cx - ex, 2) + Math.pow(cy - ey, 2));
if(l > r) { // If the distance is greater than the radius
if(r === LARGE_RADIUS) { // Outside of the large
// Do nothing
} else { // The corner area you were having a problem with
clickHandler(e, LARGE_RADIUS);
}
} else {
if(r === LARGE_RADIUS) { // Inside the large cirle
alert('Outer canvas clicked x:' + ex + ',y:' + ey);
} else { // Inside the small circle
alert('Inner canvas clicked x:' + ex + ',y:' + ey);
}
}
}
// Just call the function with the appropriate radius on click
$(img_canvas).click(function(e) { clickHandler(e, SMALL_RADIUS); });
$(wheel_canvas).click(function(e) { clickHandler(e, LARGE_RADIUS); });
Hopefully the comments above and code make enough sense, I tried to clean it up as best as I could. If you have any questions don't hesitate to ask!

Raphael Rectangle Border

When I draw a simple rectangle using the following code the bottom and right edge borders are thicker that the top and left edge borders. Why is this and can I stop it?
var paper = Raphael(10, 50, 500, 500);
var rect = paper.rect(100, 100, 100, 100);
Your rectangle's top and left borders, which are using the default 1 pixel stroke-width, are falling exactly on the top and left borders of your SVG element (as represented by a Raphael paper object. As opposed to pixel based drawing solutions, this means the line is essentially straddling the element's border, resulting in 0.5 pixels of your border stroke being clipped.
To solve, you simply need to shift your drawing over or shift the beginning offset of your SVG element's coordinates.
Here's a fiddle that shows one solution.
The square looks fine to me: http://jsfiddle.net/cMXBC/2/
Could you have some rogue css somewhere that is modifying the stroke of the rect? Try right-clicking the square and inspecting the rectangle in Firebug or with the Chrome inspector to see if there is any style that has been added.

Canvas coordinates in Fabric.js have offset

I just started with fabric.js and I have a (probably beginner) error:
I am using fabric with jquery with the following code:
$(document).ready(function () {
canvas = new fabric.StaticCanvas('scroller');
canvas.add(
new fabric.Rect({ top: 0, left: 0, width: 140, height: 100, fill: '#f55' })
);
});
This code should draw a 140x100 Rectangle on the canvas.
Sadly, only a quarter of the Rectangle appears.
If I change the top and left values to higher numbers, more of the Rectangle appears on the canvas.
So it seems, that the canvas origin is not at 0/0, but at sth. higher.
Does someone know how to fix this or what I am doing wrong?
Thanks in advance,
McFarlane
Here is a jsfiddle link with some examples http://jsfiddle.net/pukster/sdQ7U/2/
My guess is that fabric.js calculates everything from the origin (middle point) since it is drawing exactly one quarter of a rectangle even with a canvas 10 times the size of the rectangle. My guess is that top and left actually refer to the origin and not the top and left sides of the imaginary bounding box. Trouble is there is very little documentation on fabricjs. Is there any reason you are using fabricjs and not easeljs
EDIT Here's the same fiddle but with squares instead of rectangles (it is more clear) http://jsfiddle.net/pukster/sdQ7U/3/
EDIT OK I am now almost absolutely certain that fabric.js uses the center as the top/left. I ripped their example off of their site and overlayed it with the transparent couterpart to those shapes had they been coded in pure canvas http://jsfiddle.net/pukster/uathZ/2/ (blue border is the limit of the canvas element).
What you see is that the boxes are exactly offset by half but the circle (I only drew a semi circle otherwise it would not have been discernable) is perfectly overlapped. This is b/c in HTML Canvas, the circle coordinates (x,y) refer to the origin and not the top left. I did not bother with the triangle b/c my trigonometry is a bit rusty. I personally think it's misleading to use the terms top and left, when x and y would have been more representative and shorter.
Yes, this is highly unexpected and even more undocumented.
Workaround:
Set
originX: "left"
originY: "top"
for each created object.
edit: or use kangax simpler solution in the comment below.
I want to comment but lack the reputation to do so. So anyway, here is what happens when I do the following:
fabric.Object.prototype.originX = "left";
fabric.Object.prototype.originY = "top";
The shape gets rendered fine but when I select it for resizing or moving, it gets offset to a different location. The best approach seems to be to set the coordinates for every object separately using the set() method.

Categories