I need to center an object in a canvas based on its rotation. I can't figure out the maths.
What information do I have?
x, and y coordinates of the top left corner (see red circle of image)
width and height of the object
the rotation value in degrees
What have i tried so far?
// center horizontally
if (curretElement === null) return;
curretElement.x((canvas.width() / 2) - ((curretElement.width() * curretElement.scaleX()) / 2));
canvas.draw();
// center vertically
curretElement.y((canvas.height() / 2) - ((curretElement.height() * curretElement.scaleY()) / 2));
canvas.draw();
This centers the image when it's not rotated.
currentElement is the selected object.
canvas is the room where the object should be centered in.
You can calculate the coordinates this way:
imagine that you have your object centered on the canvas
calculate the coordinates of the top left corner relative to the center of the canvas
rotate the object around the center of the canvas and calculate where the top left corner ends up relative to the center of the canvas
translate the relative coordinates of the top left corner back to absolute coordinates
Here is a function that does the calculation:
function calculateXY(canvasWidth, canvasHeight, width, height, angle) {
//calculate where the top left corner of the object would be relative to center of the canvas
//if the object had no rotation and was centered
const x = -width / 2;
const y = -height / 2;
//rotate relative x and y coordinates by angle degrees
const sinA = Math.sin(angle * Math.PI / 180);
const cosA = Math.cos(angle * Math.PI / 180);
const xRotated = x * cosA - y * sinA;
const yRotated = x * sinA + y * cosA;
//translate relative coordinates back to absolute
const canvasCenterX = canvasWidth / 2;
const canvasCenterY = canvasHeight / 2;
const finalX = xRotated + canvasCenterX;
const finalY = yRotated + canvasCenterY;
return { x: finalX, y: finalY };
}
UPDATE :first try is really bad so i update code. which actually work and keep your left corner in center of container, just fill angle input
i want to comment and ask more but i can't and this thing(bounty) is tempting
Not heavy depend on java script. may be it what you need second attempt.
Corner is container and content in before. Rotate but it remains there. does it help? comment it.
<!DOCTYPE html>
<html lang="en">
<body>
<style>
body{
margin:0;
}
#container{
display :flex;
position: absolute;
width:100%;
height:100%;
}
#ram{
display:flex;
background-color:black;
position:absolute;
margin:auto;
top:50%;
right:50%;
}
#ram::before{
content: "";
position:absolute;
height:40px;
width:400px;
background-color: #000;
}
</style>
<input type="number" id="a" onchange = "forf()">
<div id = "container">
<div id = "ram">
</div>
</div>
<script>
function forf(){
var a = document.getElementById("a").value;
document.getElementById("ram").style.transform = "rotate(" + a + "deg)";
}
</script>
</body>
</html>
Related
so I have an HTML button and a JavaScript randomizer that randomizes a location for an image to 'spawn' when you click the said button. I also have a grass field image (which has a randomizer to choose from 12 different fields upon reloading the page and display one of them). Currently, it can 'spawn' an image anywhere on the page, however, I want it to only be able to spawn it onto the field.
my javascript:
function show_image(src, width, height, alt) {
var img = document.createElement("img");
img.src = src;
img.width = width;
img.height = height;
img.alt = alt;
// set the position
img.style.position = 'absolute';
img.style.top = document.body.clientHeight * Math.random() + 'px';
img.style.left = document.body.clientWidth * Math.random() + 'px';
document.body.appendChild(img);
}
document.getElementById('foo').addEventListener('click', () =>
show_image("cow-sprites/cowtest.png", 56, 54, 'foo')
);
my html:
<button id="foo">
get a cow
</button>
my css:
.cow-spawning {
z-index: 1;
}
.field-image {
position: fixed;
left: 28.25%;
top: 16%;
z-index: -1;
-webkit-transform: scale(0.85);
-moz-transform: scale(0.85);
-ms-transform: scale(0.85);
-o-transform: scale(0.85);
transform: scale(0.85);
}
what i've tried:
replacing
img.style.top = document.body.clientHeight * Math.random() + 'px';
img.style.left = document.body.clientWidth * Math.random() + 'px';
with
img.style.top = meadows.naturalHeight * Math.random() + meadows_box.top + 'px';
img.style.top = meadows.naturalHeight * Math.random() + meadows_box.left + 'px';
and creating a variable to hold the field image (meadows)
You have the coordinates for a container (the field) and another box (the image). Each has a left (x) and top (y) position, as well as a width (x) and height (y).
You want to randomize the coordinates for the image so that all 4 corners of that image are inside the outer containers bounds. It can be difficult to think about positioning all 4 corners at once, but we can simplify the problem.
Let's only think of the left,top (x,y) coordinate. If the image stays within the container, then the lowest the top can be is the same as the bottom position of the container plus the height of the image. Similarly, the furthest right the image can go is one image width from the right side of the container.
For example, let's assume the outer container left,top is 0,0, and its width,height are 100,100. The inner image width,height is 10,10. The inner image can be at 0,0 inside the container extending to 10,10, which is the leftmost and topmost position. If the image is in the furthest bottom, right corner, then the bottom right coordinate is at 100,100. Since the image is 10,10 then the top,left corner is at 90,90.
In the end, this just tells us to calculate the random position using a reduced range. Instead of being able to put the top or left coordinate anywhere from 0-100, we need to restrict them to 0-90, allowing for the image height and width padding it on the right and bottom.
img.style.top = (containerHeight - imageHeight) * Math.random() + 'px';
img.style.left = (containerWidth - imageWidth) * Math.random() + 'px';
Where containerHeight and containerWidth are the outer container height at width, assuming that the containerTop and containerLeft are both 0. If you have to account for the offset position of the outer container, then we can account for that as well.
img.style.top = ((containerHeight - containerTop - imageHeight) * Math.random() + containerTop) + 'px';
img.style.left = ((containerWidth - containerLeft - imageWidth) * Math.random() + containerLeft) + 'px';
For example:
containerLeft: 100
containerTop: 200
containerWidth: 100
containerHeight: 200
imageWidth: 10
imageHeight: 20
Randomized left/X positions can range from 100 to 190 and the new top/Y position ranges can be from 200 to 280.
Need help understanding this code, which is used to make an image move in an elliptical shape. What I don't understand, is the formula for e, px and py variable. What exactly is the e variable defined the complex way it is? I know it uses some mathematical formulas but i don't know which ones.
var b = 125;
var h = 115;
var rx = 7;
var ry = 4;
var e = 0;
function update() {
setInterval(function() {
e = (e + Math.PI / 360) % (Math.PI * 2);
rotate(e);
}, 10);
var lyd = new Audio("Vedlegg/skoytelyd.mp3");
lyd.play();
}
function rotate(e) {
var px = b + rx * Math.cos(e)*b/2;
var py = h + ry * Math.sin(e)*h/2;
document.getElementById("punkt").style.left = px + "px";
document.getElementById("punkt").style.top = py + "px";
}
</script>
<style>
div {
position: fixed;
}
#sentrum {
background: black;
left: 100px;
top: 50px;
}
#skoyteloper {
position: absolute;
top: 190px;
left: 450px;
width: 60px;
height: 60px;
}
</style>
</head>
<body>
<div id="sentrum"></div>
<img src="Vedlegg/bane.jpg" id="imgBane"></img>
</div>
<div id="punkt">
<img src="Vedlegg/skoyteloper.png" id="skoyteloper"></img>
</div>
TILBAKE
</body>
Sinus and cosinus:
Think of a triangle with one 90° angle. 1 line is horizontal, another line is at the right side and is vertical, the third line goes from bottom-left to top-right.
The angle on the left we call e (yes,it is the e in your function)
sinus of e is defined as the vertical line on the right divided by the diagonal line; cosinus e = horizontal line (which is touching the angle e) / diagonal.
--
Now draw a circle with middle at angle e and radius = the length of the diagonal.
If you raise the angle e you will see the vertical line get bigger and the horizontal line get smaller (keep the length of the diagonal constant), until you reach 90°. Then of course you can go beyond 90°, then the vertical line can be on the left. Further than 180° the vertical line will point down (negative coordinate), ...
So that's one of the uses of sin and cos: if you set an angle they give you an y-value and a x-value, showing you 1 point on a circle. It's always a number between -1 and +1. example: sin(0) = 0 (no vertical component), cos(0) = 1
This code here below gives you a circle around center (0,0) and radius 100. Feed this function a bunch of values for e and you get as many points on a circle
function rotate(e) {
var px = 100 * Math.cos(e);
var py = 100 * Math.sin(e);
}
Now, if instead of 100 * cos(e) you put 200 * cos(e), then it's not a circle anymore. Every x coordinate will be twice as far (compared to the circle). A different rx and ry will result in an ellipse.
your variables b & h are for pushing the center of the ellipse to somewhere inside the image/div/canvas/... rather than in a corner (then you clip most of the ellipse).
Does this help?
This question is a followup to the question: width/height after transform.
I am posting a new question because that question only solves the width and not the height.
The formula:
var x = $('#box').width()*Math.cos(rotationAngle)
+ $('#box').height()*Math.sin(rotationAngle);
works well for the width, what is the equivalent formula to calculate the height?
Thanks.
This is the best working formula I have come up with:
var h = $(obj).height() * Math.abs(Math.cos(deg)) + $(obj).width() * Math.abs(Math.sin(deg));
var w = $(obj).width() * Math.abs(Math.cos(deg)) + $(obj).height() * Math.abs(Math.sin(deg));
The below code will work out the bounding box for a rotated object in JavaScript.
var obj = document.getElementById("obj"); // Get object
var bx = obj.clientWidth; // Width of rectangle
var by = obj.clientHeight; // Height of rectangle
var t = /[0-9]+/.exec(obj.style.transform)[0] * Math.PI / 180; // Convert to radians
var x = Math.sin(t) * by + Math.cos(t) * bx; // The bounding box width
var y = Math.sin(t) * bx + Math.cos(t) * by; // The bounding box height
document.write(x.toFixed(2), " ", y.toFixed(2)); // The width and height of finished bounding box of item rounded to 2 decimal places
<div id="obj" style="width: 200px; height: 200px; transform: rotate(30deg); background: red;"></div>
The solution from #ChiMo will not work if the angle value is negative.
Fixed version:
var obj = document.getElementById("obj"); // Get object
var bx = obj.clientWidth; // Width of rectangle
var by = obj.clientHeight; // Height of rectangle
var t = /[0-9]+/.exec(obj.style.transform)[0] * Math.PI / 180; // Convert to radians
var x = Math.sin(t) * by + Math.cos(t) * bx; // The bounding box width
var y = Math.sin(t) * bx + Math.cos(t) * by; // The bounding box height
document.write(x.toFixed(2), " ", y.toFixed(2)); // The width and height of finished bounding box of item rounded to 2 decimal places
var wrapper = document.getElementById("wrapper"); // Get object
wrapper.style.width = Math.round(x) + 'px';
wrapper.style.height = Math.round(y) + 'px';
#obj {
width: 100px;
height: 100px;
background-color: red;
}
#wrapper {
border: 1px solid black;
display: flex;
flex-flow: row nowrap;
align-items: center;
justify-content: center;
}
<div id="wrapper">
<div id="obj" style="transform: rotate(-30deg)"></div>
</div>
Well, your previous question involved a square. Squares have the same dimensions all the way around. So, unless there's something funky going on, your height should be exactly the same as your width.
I have a div element that gets rotated with -webkit-transform: rotate(45deg). If I try to access it's current position after rotation like this
var x = $('#tap-area').offset().left;
var y = $('#tap-area').offset().top;
it returns me not the actual values of where the original top left corner is, but where is the top left corner of the bounding box of the whole div (which is physically furthest top left, outside of the DIV).
How can I calculate/get the original top left corner of the div after it gets rotated
Eg:
If I rotate it by 90deg, the value I would like to get now would be top right.
If I rotate it by 180deg, the value I would like to get now would be bottom right.
I need this, so I could set another DIV element to always stay attached to the original top left corner of the element, so that it would change it's position depending on how it was rotated.
Thanks.
You can always get the absolute position of an element using:
<element>.getBoundingClientRect()
This gives you the AABB (Axis Aligned Bounding Box, or Min-Max-Box) with the top left corner of the screen as origin. (MDN)
If you need to access the position of the top left corner of your element you can rotate its original position vector too, what is a simple matrix multiplication. You should than keep in mind that the center of rotation is by default the element's center, but it also possible to set this to a different location with css.
Good Luck!
This is a solution I come to after being faced with the same problem:
// Initial data with TOP AND LEFT OF THE BOUNDING BOX (not x,y of top left point of the box but left margin of the bounding box and top margin of the bounding box)
var top = Math.ceil($('#' + id).position().top);
var left = Math.ceil($('#' + id).position().left);
var wi = $('#' + id).css("width").replace("px","");
var he = $('#' + id).css("height").replace("px","");
var rot = $(this).data(id + ".ROTACION"); //There I have the applied rotation stored ...
// CALULATIONS
var swapheightwidth= false;
//Let's keept it first cuad.
if (rot > 270)
{
rot = rot - 270;
swapheightwidth= = true;
}
else if (rot > 180)
{
rot = rot - 180;
}
else if (rot > 90)
{
swapheightwidth= = true;
rot = rot - 90;
}
if (swapheightwidth)
{
var calculatedX= left + (wi * sin(rot));
var calculatedY= top + (wi * cos(rot));
}
else
{
var calculatedX= left + (he * sin(rot));
var calculatedY= top + (he * cos(rot));
}
var xbott = left;
var ybott = Math.ceil(calculatedY);
var xtop= Math.ceil(calculatedX);
var ytop= top;
// FINAL CALCULATED POINTS
// xtop ytop -> top left
// xbott ybott -> bottomleft
function sin(x) {
return Math.sin(x / 180 * Math.PI);
}
function cos(x) {
return Math.cos(x / 180 * Math.PI);
}
Cheers
I have a draggeable image contained within a box. You can zoom in and zoom out on the image in the box which will make the image larger or smaller but the box size remains the same. The box's height and width will vary as the browser is resized. The top and left values for the image will change as it is dragged around.
I'm trying to keep whatever the point the box was centered on in the image, in the center. Kind of like how zoom on Google Maps works or the zoom on Mac OS X zooms.
What I'm doing right now is calculating the center of the box (x = w/2, y = h/2) and then using the top and left values for the image to calculate the position of the image in the center of the box. (x -= left, y -= top).
Then I zoom the image by growing or shrinking it and I use the scale change to adjust the coordinates (x = (x * (old_width/new_width), y = (y * (old_height/new_height)).
I then reposition the image so that its center is what it was before zoom by grabbing the coordinates it is currently centered on (that changed with the resize) and adding the difference between the old center values and the new values to the top and left values (new_left = post_zoom_left + (old_center_x - new_center_x), new_top = post_zoom_top + (old_center_y - new_center_y).
This works ok for zoom in, but zoom out seems to be somewhat off.
Any suggestions?
My code is below:
app.Puzzle_Viewer.prototype.set_view_dimensions = function () {
var width, height, new_width, new_height, coordinates, x_scale,
y_scale;
coordinates = this.get_center_position();
width = +this.container.width();
height = +this.container.height();
//code to figure out new width and height
//snip ...
x_scale = width/new_width;
y_scale = height/new_height;
coordinates.x = Math.round(coordinates.x * x_scale);
coordinates.y = Math.round(coordinates.y * y_scale);
//resize image to new_width & new_height
this.center_on_position(coordinates);
};
app.Puzzle_Viewer.prototype.get_center_position = function () {
var top, left, bottom, right, x, y, container;
right = +this.node.width();
bottom = +this.node.height();
x = Math.round(right/2);
y = Math.round(bottom/2);
container = this.container.get(0);
left = container.style.left;
top = container.style.top;
left = left ? parseInt(left, 10) : 0;
top = top ? parseInt(top, 10) : 0;
x -= left;
y -= top;
return {x: x, y: y, left: left, top: top};
};
app.Puzzle_Viewer.prototype.center_on_position = function (coordinates) {
var current_center, x, y, container;
current_center = this.get_center_position();
x = current_center.left + coordinates.x - current_center.x;
y = current_center.top + coordinates.y - current_center.y;
container = this.container.get(0);
container.style.left = x + "px";
container.style.top = y + "px";
};
[Working demo]
Data
Resize by: R
Canvas size: Cw, Ch
Resized image size: Iw, Ih
Resized image position: Ix, Iy
Click position on canvas: Pcx, Pcy
Click position on original image: Pox, Poy
Click position on resized image: Prx, Pry
Method
Click event position on canvas -> position on image: Pox = Pcx - Ix, Poy = Pcy - Iy
Position on image -> Pos on resized image: Prx = Pox * R, Pry = Poy * R
top = (Ch / 2) - Pry, left = (Cw / 2) - Prx
ctx.drawImage(img, left, top, img.width, img.height)
Implementation
// resize image
I.w *= R;
I.h *= R;
// canvas pos -> image pos
Po.x = Pc.x - I.left;
Po.y = Pc.y - I.top;
// old img pos -> resized img pos
Pr.x = Po.x * R;
Pr.y = Po.y * R;
// center the point
I.left = (C.w / 2) - Pr.x;
I.top = (C.h / 2) - Pr.y;
// draw image
ctx.drawImage(img, I.left, I.top, I.w, I.h);
This is a general formula that works for zooming in or out, and can handle any point as the new center. To make it specific to your problem:
Pcx = Cw / 2, Pcy = Ch / 2 (alway use the center)
R < 1 for zooming out, and R > 1 for zooming in