I am trying to create a drag and drop image within a pdf document,
get the coordinates and later on use these coordinates to sign a document.
I have a scenario just as in the picture below:
Sample picture
Now I need to get the coordinates of left x right x top y bottom y ( lx rx ty by ) and convert them to PDF points.
So far I have tried as below:
const rect = canvas.getBoundingClientRect();
const x = e.client.x - rect.left;
const y = e.client.y - rect.top;
const ptX = x * 0.75;
const ptY = y * 0.75;
this.coordinates = { x: ptX, y: ptY };
however this only get the lx and ty how I can get the rx and by
I am using interact.js as a library for drag and drop and have created this stackblitz as a sample what I am doing
https://stackblitz.com/edit/angular-ivy-ekxpyh?file=src/app/interact.service.ts
Thank you
Im not sure what the client object is here, but if its a DOMRect or something of the like
const rect = canvas.getBoundingClientRect();
const left_x = e.client.left - rect.left;
const top_y = e.client.top - rect.top;
const right_x = e.client.right - rect.left;
const bottom_y = e.client.bottom - rect.top;
or even
const right_x = left_x + e.client.width;
const bottom_y = top_y + e.client.height;
Related
So I have this code here that I'm using to make the eyeball follow the cursor
document.addEventListener('mousemove', (e) => {
const mouseX = e.clientX;
const mouseY = e.clientY;
const anchor = document.getElementById('anchor')
const rekt = anchor.getBoundingClientRect();
const anchorX = rekt.left + rekt.width / 2;
const anchorY = rekt.top + rekt.height / 2;
const angleDeg = angle(mouseX, mouseY, anchorX, anchorY);
const eyes = document.querySelectorAll('.eye')
eyes.forEach(eye => {
eye.style.transform = `rotate(${90 + angleDeg}deg)`;
})
})
function angle(cx, cy, ex, ey) {
const dy = ey - cy;
const dx = ex - cx;
const rad = Math.atan2(dy, dx);
const deg = rad * 180 / Math.PI;
return deg;
}
but it's rotating in a sphere shape, like the image can tell
eye sphere shape
and if you put your cursor on the side it will be something like this
eye max X
but I want the eye to be able to go further on the X axis.
OBS: the frame is a .png with a hole on the middle, so don't worry about the eyeball leaving the eye.
I tried to change the radius of the sphere nut it didn't helped me the way I intended. I think an ellipse shape will be better, but I'm struggling to do it so.
I am using the following function to get mouse coordinates on canvas after performing rotations.
function getWindowToCanvas(canvas, x, y) {
const ctx = canvas.getContext("2d");
var transform = ctx.getTransform();
var rect = canvas.getBoundingClientRect();
var screenX = (x - rect.left) * (canvas.width / rect.width);
var screenY = (y - rect.top) * (canvas.height / rect.height);
if (transform.isIdentity) {
return {
x: screenX,
y: screenY
};
} else {
console.log(transform.invertSelf());
const invMat = transform.invertSelf();
return {
x: Math.round(screenX * invMat.a + screenY * invMat.c + invMat.e),
y: Math.round(screenX * invMat.b + screenY * invMat.d + invMat.f)
};
}
}
I used the inverted transform matrix after reading html5-canvas-transformation-algorithm and best-way-to-transform-mouse-coordinates-to-html5-canvass-transformed-context
I am letting the user draw rectangles with the mouse, and I need to get the mouse x,y coordinates after transformations, but once the canvas is rotated (say by 90 deg) then the rectangles no longer follow the mouse pointer.
Does anyone know what I'm doing wrong?
Thanks to #MarkusJarderot and jsFiddle getting mouse coordinates from un-rotated canvas I was able to get a solution that is close to perfect. I don't quite understand it, but it works much better.
function getWindowToCanvas(canvas, e) {
//first calculate normal mouse coordinates
e = e || window.event;
var target = e.target || e.srcElement,
style = target.currentStyle || window.getComputedStyle(target, null),
borderLeftWidth = parseInt(style["borderLeftWidth"], 10),
borderTopWidth = parseInt(style["borderTopWidth"], 10),
rect = target.getBoundingClientRect(),
offsetX = e.clientX - borderLeftWidth - rect.left,
offsetY = e.clientY - borderTopWidth - rect.top;
let x = (offsetX * target.width) / target.clientWidth;
let y = (offsetY * target.height) / target.clientHeight;
//then adjust coordinates for the context's transformations
const ctx = canvas.getContext("2d");
var transform = ctx.getTransform();
const invMat = transform.invertSelf();
return {
x: x * invMat.a + y * invMat.c + invMat.e,
y: x * invMat.b + y * invMat.d + invMat.f
};
}
The only issue remaining is that, when rotated say 45deg, drawing a rectangle with ctx.rect() draws a rectangle that parallels with respect to the canvas, not to the window, so the rectangle is slanted even though it is finally in the right place. I want to draw rectangles with respect to the window, not the canvas. However, this may just be how ctx.rect() works, and I'll need to update later. For now, this could help others.
UPDATE
Figured out original bug.
Since I didn't understand why my original function was not working, used the above solution to start trouble-shooting it. It turns out that the reason the above code did not work is because I was calling console.log(transform.invertSelf()) to see the transform while I was debugging. This mutated the transform. So, when I called var invMat = transform.invertSelf() right after, I inverted it yet again! I should have paid attention to the 'self' in 'invertSelf'.
This function now works
function getWindowToCanvas(canvas, x, y) {
var rect = canvas.getBoundingClientRect();
var screenX = (x - rect.left) * (canvas.width / rect.width);
var screenY = (y - rect.top) * (canvas.height / rect.height);
const ctx = canvas.getContext("2d");
var transform = ctx.getTransform();
if (transform.isIdentity) {
return {
x: screenX,
y: screenY
};
} else {
// console.log(transform.invertSelf()); //don't invert twice!!
const invMat = transform.invertSelf();
return {
x: Math.round(screenX * invMat.a + screenY * invMat.c + invMat.e),
y: Math.round(screenX * invMat.b + screenY * invMat.d + invMat.f)
};
}
}
ive drawn a checker board on a canvas and i want to highlight the square which the mouse is over. I have given it a go but the furthest i can get is with it half a square out of sync.
Here is my code:
canvas.addEventListener('mousemove', function(evt)
{
const position = getGridPoint(evt);
drawBoard(); //Clears the last highlight
context.lineWidth='3'; //Draws the new highlight
context.strokeStyle = 'yellow';
context.rect(position.x * board.squareW, position.y * board.squareH, board.squareW, board.squareH);
context.stroke();
})
function getGridPoint(evt)
{
const rect = canvas.getBoundingClientRect();
//board.w = width of the board
//board.squareW = width of each tile on the board
const x = Math.round((evt.clientX - rect.left) / (rect.right - 2 - rect.left) * board.w);
const y = Math.round((evt.clientY - rect.top) / (rect.bottom - 2 - rect.top) * board.h);
const roundX = Math.round(x / board.squareW);
const roundY = Math.round(y / board.squareH);
return {
x: roundX,
y: roundY
};
}
Its something in the 2nd function where im using math.round
So it will work if i manually subtract half a tile's width from x and y but it seems a hacky way and id rather do it properly in the first place
JSfiddle: http://jsfiddle.net/5toudex0/3/
try this for getTile
function getTile(evt)
{
const rect = canvas.getBoundingClientRect();
//board.w = width of the board
//board.squareW = width of each tile on the board
const x = Math.floor((evt.clientX - rect.left) / board.squareW);
const y = Math.floor((evt.clientY - rect.top) / board.squareH);
return {
x: x,
y: y
};
}
With a little change made to the mousemove handler for the canvas, you can (0) directly get the client position of the mouse and then (1) compute the col/row indices for the tile to highlight.
Consider the following change to your code:
canvas.addEventListener('mousemove', function(evt)
{
const position = getTile(evt);
var xPos = evt.clientX, yPos = evt.clientY;
var xTileIndex = (xPos / board.squareW)>>0;
var yTileIndex = (yPos / board.squareH)>>0;
console.log(`x,y = ${xPos},${yPos}`);
console.log(`x,y = ${xTileIndex},${yTileIndex}`);
In the example fiddle: https://jsfiddle.net/krazyjakee/uazy86m4/ you can drag the mouse around underneath the vector point shown as a blue square. You will see the line draw a path from the vector, through the mouse and to the edge of the viewport where a green square is shown, indicating it has found the edge. However, above the vector, the green square disappears as it fails to detect the edge of the viewport.
Here is the current logic I am using to detect the edge.
const angle = Math.atan2(mouse.y - vectorCenter.y, mouse.x - vectorCenter.x);
const cosAngle = Math.abs(Math.cos(angle));
const sinAngle = Math.abs(Math.sin(angle));
const vx = (viewport.width - vectorCenter.x) * sinAngle;
const vy = (viewport.height - vectorCenter.y) * cosAngle;
const vpMagnitude = vx <= vy ?
(viewport.width - vectorCenter.x) / cosAngle :
(viewport.height - vectorCenter.y) / sinAngle;
const viewportX = vectorCenter.x + Math.cos(angle) * vpMagnitude;
const viewportY = vectorCenter.y + Math.sin(angle) * vpMagnitude;
const viewPortEdge = {
x: viewportX,
y: viewportY,
};
Please help me figure out how to correctly detect the position in the top edge of the viewport.
I didn't look into why exactly this fails for the top because there's an easier approach to this than dealing with angles. You can get the position by some simple vector calculations.
First, for the sake of explicitness and to prevent hardcoding any values into the computation I've extended your viewport
const viewport = {
x: 0,
y: 0,
width: window.innerWidth,
height: window.innerHeight,
get left(){ return this.x },
get right(){ return this.x + this.width },
get top(){ return this.y },
get bottom(){ return this.y + this.height },
};
now the calculation:
//prevent division by 0
const notZero = v => +v || Number.MIN_VALUE;
let vx = mouse.x - vectorCenter.x;
let vy = mouse.y - vectorCenter.y;
//that's why I've extended the viewport, so I don't have to hardcode any values here
//Math.min() to check wich border I hit first, X or Y
let t = Math.min(
((vx<0? viewport.left: viewport.right) - vectorCenter.x) / notZero(vx),
((vy<0? viewport.top: viewport.bottom) - vectorCenter.y) / notZero(vy)
);
const viewPortEdge = {
x: vectorCenter.x + vx * t,
y: vectorCenter.y + vy * t,
};
so t is the factor by wich I have to scale the vector between the mouse and the vectorCenter to hit the closest edge in that particular direction.
I am have now this code: http://jsfiddle.net/DK67k/2/
In here is 2D tile map and when you click on tile you get coordinates on alert.
But for get precises coordinate you need click on top left tile(tiles is 16x16) and if I click on bottom right tile I am get second tile coordinates.
Maybe anyone have idea how to fix this?
The canvas point (0,0) is at mouse coords (10,10), i think is due to parent of canvas has a padding area.
function mouseCheck(e) {
x = e.pageX-10;
y = e.pageY-10;
mouseX = Math.floor(x / 16);
mouseY = Math.floor(y / 16);
}
Blaus' answer is correct.
Though you might want to subtract the canvas offset left- and top position to make your canvas element available for dynamic positioning, and not relative to the 10px padding.
function mouseCheck(e) {
var x = e.pageX - canvas.offsetLeft;
var y = e.pageY - canvas.offsetTop;
mouseX = Math.floor(x / 16);
mouseY = Math.floor(y / 16);
}