How to center an image in a HTML Canvas Circle? - javascript

I am very new to the canvas so I have very little idea how it works.
This is the code for the circle that I have drawn in the middle of the screen.
ctx.save();
ctx.beginPath();
ctx.arc(canvas.width / 2, canvas.height / 2, radius, 0, 2 * Math.PI);
ctx.strokeStyle = "#2465D3";
ctx.stroke();
ctx.clip();
ctx.drawImage(imageFile, 200, 200, 500, 500);
ctx.restore();
ctx.stroke();
I want to show a picture inside of the circle.
The picture shows up in the circle in the wrong position with this code. I want the picture to be in the center of the circle like:
According to the syntax of drawImage(), I need dx and dy in this ctx.drawImage(image, dx, dy, dWidth, dHeight) to position the image correctly on the canvas (at the center of the circle).
Is there anything I can do to put the picture in the center of the circle without doing all sorts of
Pythagoras calculations?
Why I am doing it this way?
I cannot find one good coordinate for the picture and move on because the radius of the circle is not constant so that would not work. I would need to find the appropriate coordinate for the picture every time the radius of the circle changes to keep the picture in the middle of the circle.
My thought process was to find the corner coordinate of the square that the circle would make like in this picture
then to measure the distance between that point and the width of the canvas.
I think there must be a better way to do this. Is there a better way to do this? I am using this in a React App.

Your imagem must have the x and y position equals circle. The height and width imagem must have 2r.
x = canvas.width / 2;
y = canvas.height / 2
ctx.save();
ctx.beginPath();
ctx.arc(x,y, radius, 0, 2 * Math.PI);
ctx.strokeStyle = "#2465D3";
ctx.stroke();
ctx.clip();
ctx.drawImage(imageFile, x, y, 2 * radius, 2 * radius);
ctx.restore();
ctx.stroke();

Here canvas.width / 2 and canvas.height/ 2 are the coordinates for the center of the circle.
ctx.drawImage(
oneMoreRound,
canvas.width / 2,
canvas.height / 2,
radius * 2,
radius * 2
);
Only passing the above code puts the top left corner of the image to the center of the circle like this:
Subtracting the radius from the x and y coordinates puts that top left corner of the image in the top left corner of the square that the circle will create. That would put the image in the right position if the image is a square.
This is the solution
ctx.drawImage(
oneMoreRound,
canvas.width / 2 - radius,
canvas.height / 2 - radius,
radius * 2,
radius * 2
);

Related

Using trigonometry to animate HTML5 canvas, but how to position this square?

Sometimes you wish you could go back in time to tell your younger self that maths are indeed important! But I doubt I would've listened back then. I've been playing with trigonometry lately for animation purposes with the HTML5 canvas in this particular example.
It's a super simple animation: It positions an arc in a circular manner around the center of the canvas. The X and Y positions are calculated based upon the basic trigonometry functions sinus and cosinus. "SohCahToa". I think I'm starting to get it. But somehow I cannot figure out how to draw a square in the middle of one of the triangular sides.
let radius = 200;
let angle = 0;
x = centerX + Math.cos(angle) * radius;
ctx.beginPath();
ctx.fillRect(x/2, centerY, 20, 20);
https://codepen.io/melvinidema/pen/wvKPepa?editors=1010
So the arc is drawn by adding up the center of the canvas with the
rework formulas: (co)sinus - for X = Cos, for Y = Sin) of the angle times the radius of the circle.
If we only take the X position (red line) and want to draw the square half of the position of the arc. ( So in the middle of the red line ) we should just be able to divide the freshly calculated X position by two right? But if I do that, the square is magically drawn completely outside the circle.
What's happening? Why does it behave like this? And what calculation should I use instead for the square to position in the middle of the red line throughout the animation?
Thanks in advance!
Your x defined as:
x = centerX + Math.cos(angle) * radius;
but when you want to divide by 2, you just need to divide the Math.cos(angle) * radius, while the centerX is the zero point, and its stand as it is.
So the rect should be placed at:
centerX + Math.cos(angle)/2
Also, I think will be better if you reduce half of the rect width, and get:
centerX + Math.cos(angle)/2 - 10
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
let radius = 200;
function frame(angle) {
const cx = canvas.width / 2,
cy = canvas.height / 2,
x = Math.cos(angle) * radius,
y = Math.sin(angle) * radius;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.moveTo(cx, cy);
ctx.lineTo(cx+x, cy+y);
ctx.lineTo(cx+x, cy);
ctx.lineTo(cx, cy);
ctx.stroke();
ctx.closePath();
ctx.beginPath();
ctx.arc(cx+x, cy+y, 10, 0, Math.PI*2);
ctx.fill();
ctx.fillRect(cx + x/2 - 10, cy - 10, 20, 20);
ctx.closePath();
requestAnimationFrame(()=>frame(angle+.03));
}
frame(0)
canvas {
display: block;
max-height: 100vh;
margin: auto;
}
<canvas width="500" height="500"></canvas>

How to draw in canvas 2D this shape and fill it with color?

This should be without this diagonal. I want to draw two arcs one with x2 smaller radius, join them and fill(). I know how to calculate the end and beginning of an arc, problem is arcs are always drawn clockwise, so to draw second arc within same ctx.beginPath() I have to use something like ctx.moveTo() but problem with moveTo is I get two different paths as proof closePath() cause this diagonal.
So I want to know how to draw arc in other direction than clockwise or how to use moveTo() but still beeing able to fill this shape. Filling now cause following issue:
White diagonal visible.
Arcs are not always drawn clockwise. The sixth parameter to .arc is a boolean which when true will make the arc be drawn anti-clockwise.
However you will also need to reverse the start and end angles, otherwise the arc will attempt to go the long way around:
var canvas = document.getElementById('c');
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.arc(20, 20, 40, 30 * Math.PI / 180, 80 * Math.PI / 180);
ctx.arc(20, 20, 150, 80 * Math.PI / 180, 30 * Math.PI / 180, true);
ctx.closePath();
ctx.lineWidth = 3;
ctx.stroke();
ctx.fillStyle = '#e0e0ff';
ctx.fill();
<canvas id="c" width="200" height="200" />
Why not use the lineWidth property to make the arc thicker. In this case you'll only need one call to arc:
let canvas = document.getElementById("canvas"),
context = canvas.getContext("2d");
context.beginPath();
context.arc(20, 20, 40, 0, Math.PI / 4);
context.lineWidth = 15;
context.stroke();
<canvas id="canvas"></canvas>

Repeat texture to loop around with clip()

I have a texture which I use drawImage() over a clipping plane. But i have an issue where by I can't figure out how to wrap it around once it's moved more than the width of the texture so it loops indefinitely, it also does not clip for some reason.
My draw code looks like this:
var radius = 120;
var pos = {'x':canvas.width/2,'y':canvas.height/2};
var x = 0;
var offsetX = 0;
function draw() {
ctx.clearRect(0,0,canvas.width,canvas.height);
x += 1.1415;
ctx.beginPath();
ctx.arc(pos.x, pos.y, radius, 0, Math.PI * 2, false);
ctx.closePath();
ctx.clip();
var scale = (radius * 2) / img.height;
ctx.drawImage(img, pos.x+x, pos.y, img.width, img.height, pos.x - radius - offsetX * scale, pos.y - radius, img.width * scale, img.height * scale);
ctx.restore();
requestAnimationFrame(draw);
}
I have created the demo here so you can see what happens when the texture moves too far, it basically disappears and i need it to loop again without a gap between so its seamless: http://jsfiddle.net/dv2r8zpv/
What is the best way to draw the texture's position so it will wrap around, i don't quite understand how to do it.
I have created a seamless loop now. The code I change are below. Changing the y on drawImage encapsulates the whole circle
if (x > img.width) {
x = 0;
}
ctx.drawImage(img, x, 0, ...);
ctx.drawImage(img, -img.width+x,0, ...);
Updated answer with full circle

Make the half circle more elliptic

This is my jsfiddle:
http://jsfiddle.net/c4upM/103/
I have the gray arc and the blue/green arc.
I am trying to make them more Elliptic, like:
and I didn't succeed.
I read this one: http://jsbin.com/ovuret/2/edit and tried to make it on my jsfiddle but I didn't succeed because the arc-s are painted in the function of: DrawCircle and DrawEllipseForProjection.
There are two functions: blueArc and grayarc. by the radian angle, there is a calculation in function circleInArc that places the small lightblue circle and the small gray circle (when you mouse over the canvas) in the arc-s.
I want these functions to work after the change but I didn't succeed.
these are the blue and gray arc-s:
function grayArc(strokeColor, cx, cy, radius, ctx, linewidth) {
ctx.beginPath();
ctx.arc(cx, cy, radius, Math.PI, Math.PI * 2);
ctx.lineWidth = linewidth;
ctx.strokeStyle = strokeColor;
ctx.stroke();
}
function blueArc(strokeColor, radianStart, radianEnd, cx, cy, radius, ctx, linewidth) {
ctx.beginPath();
ctx.arc(cx, cy, radius, radianStart, radianEnd);
ctx.lineWidth = linewidth;
ctx.strokeStyle = strokeColor;
ctx.stroke();
}
and this is the circleInArc function:
function circleInArc(fillColor, radianAngle, cx, cy, radius, ctx, linewidth) {
var x = cx + radius * Math.cos(radianAngle);
var y = cy + radius * Math.sin(radianAngle);
ctx.beginPath();
ctx.arc(x, y, linewidth / 2, 0, Math.PI * 2);
ctx.closePath();
ctx.fillStyle = fillColor;
ctx.fill();
return ({
x: x,
y: y,
radius: linewidth / 2
});
}
Any help appreciated!
You can try draw arc with bigger radius and not from Math.PI to 2*Math.PI but this one
ctx.arc(cx, cy, radius, Math.PI*5/4, Math.PI * 7/4);
and don't forget move the arc down for radius size
grayArc("rgb(177,177,177)", cx, cy+radius, radius*2, ctx, linewidth);
hope it will help
I've fiddled around a bit, and you'll propably have to adjust some of the values, but here's an example: Fiddle (Only the gray arc was adjusted)
The basic strategy is to save the state of the context, then scale it to your liking. If you only scale one axis.. voilá, ellipsis.
Also, I translated the origin of the canvas to the center of the arc before scaling, because they get scewed with the scaling. The center of the arc is then 0, 0.
After that the context can be restored like nothing ever happened, an you can stroke your transformed arc.
ctx.save();
ctx.translate(cx,cy);
ctx.scale(1.3, 1);
ctx.arc(0, 0, radius, 1.2*Math.PI, 1.8*Math.PI);
ctx.restore();
You'll propably want to play around with the radius, the ratio of scaling and maybe the range in which the arc gets drawn to fit your needs.
Edit: I cut down on the range of the arc which gets drawn, to prevent the stubby ends of the ellipsis showing. From 1.2*PI to 1.8*PI instead of 1*PI to 2*PI seems to look alright in the current case. :)
For future questions, please try to extract the a more basic example of your problem, to make it easier for those who want to help out. :)

Draw image with x,y relating to the center point for the image?

I'm trying to draw an image at co-ordinate points x,y with x and y being in the center of the image.
I have this code that I found earlier for rotating an image:
function drawImageRot(img,x,y,width,height,deg) {
//Convert degrees to radian
var rad = deg * Math.PI / 180;
//Set the origin to the center of the image
context.translate(x + width / 2, y + height / 2);
//Rotate the canvas around the origin
context.rotate(rad);
//draw the image
context.drawImage(img, width / 2 * (-1), height / 2 * (-1), width, height);
//reset the canvas
context.rotate(rad * ( -1 ) );
context.translate((x + width / 2) * (-1), (y + height / 2) * (-1));
}
However it seems to draw the image below and to the right? i.e. the x,y co-ordinates relate to the top left corner of the image?
At the beginning of that method it translates the context from the top-left to the center.
If you want to pass in the center of the image. Then you can skip that step, resulting in the following code.
function drawImageRot(img,x,y,width,height,deg) {
//Convert degrees to radian
var rad = deg * Math.PI / 180;
//Set the origin to the center of the image
context.translate(x, y);
//Rotate the canvas around the origin
context.rotate(rad);
//draw the image
context.drawImage(img, width / 2 * (-1), height / 2 * (-1), width, height);
//reset the canvas
context.rotate(rad * ( -1 ) );
//
context.translate((x) * (-1), (y) * (-1));
}
Don't forget that you have to undo the translation in the same manner that you change the translation.
If you want to give this function coordinates of center instead of left top corner, just replace
context.translate(x + width / 2, y + height / 2);
with
context.translate(x, y);
Little late to the party but i find it easiest to shift the image before you render it.
function drawBorder(img, x, y, width, height, deg){
var xAdjust = width * .5,
yAdjust = height * .5;
ctx.drawImage(img, x - xAdjust, y - yAdjust, width, height);
}
As you know the width and height when its passed you can just move the image up half of its size and left half of its size. Quick and dirty but does the trick.

Categories

Resources