Hi,
I am making a touch enabled signing app using 2 HTML Canvas. In the screenshot attached below, i am able to sign on my tablet on 1st canvas and on the second canvas as well without scrolling the scroll-bar on the right. If i scroll and bring the 2nd canvas in complete focus, then my signing stops working on the 2nd Canvas. It seems that on scrolling i am not calculating the coordinates well.
Here is the code that i am using to get the co ordinates and pass that to my Drawer method.
var coors = {
x: event.targetTouches[0].pageX ,
y: event.targetTouches[0].pageY
};
// Now we need to get the offset of the canvas location
var obj = signCanvas;
if (obj.offsetParent) {
do {
coors.x -= obj.offsetLeft;
coors.y -= obj.offsetTop;
}
while ((obj = obj.offsetParent) != null);
}
Try the following instead:
var rect = signCanvas.getBoundingClientRect(); // absolute position of canvas
var coors = {
x: event.targetTouches[0].clientX - rect.left,
y: event.targetTouches[0].clientY - rect.top
};
and of course, comment out/remove the rest of the adjustment code for the coordinates.
Related
In my project, using Three.JS I have implemented the functionality where I want my camera to zoom to the cursor position. Below is the function for that. In this function, I am using a factor as static value 5.
onWheelMove = (event: WheelEvent): void => {
let rect: ClientRect = this.canvas.getBoundingClientRect();
var factor = 5;
this.mousePos.x = (event.clientX - rect.left) / this.renderer.domElement.width * 2 - 1;
this.mousePos.y = -(event.clientY - rect.top) / this.renderer.domElement.height * 2 + 1;
console.log("X-->", this.mousePos.x);
console.log("Y-->", this.mousePos.y);
var vector = new THREE.Vector3(this.mousePos.x, this.mousePos.y, 0.5);
vector.unproject(this.camera);
console.log("mouse wheel vector -- >", vector);
vector.sub(this.camera.position);
console.log("vector.setLength(factor)-- > ", vector.setLength(factor));
if (event.deltaY < 0) {
this.camera.position.addVectors(this.camera.position, vector);
} else {
this.camera.position.subVectors(this.camera.position, vector);
}
this.camera.updateMatrixWorld(true);
this.enableRender();
this.render(true);
}
Now Look at the above picture, this is my project viewer and the camera at initialization time is not perpendicular to the model. at this point, if I am keeping my cursor exactly on axis-helper and do mouse wheel up/down, then I am getting below the value of setLength(vector).
vector.setLength(factor)-- > Vector3 {x: 1.6930115334358085, y: -3.3042632885205743, z: -3.348963431838058}
-3.30 instead of 5.
Now look at another picture below
Here, you can see that I moved the camera on top view, now when I am doing mouse wheel up/down, then below value is coming
vector.setLength(factor)-- > Vector3 {x: 0.02806398289696144, y: -4.998806591956658, z: -0.10557020921938588}
see -4.99 which is closer to 5, that I am setting as a factor. but in this case also, if I am keeping my cursor somewhere else, let say the top right corner of the canvas and do mouse wheel up/down, the value gets changed
vector.setLength(factor)-- > Vector3 {x: -1.724124575016607, y: -4.527894564140576, z: -1.2351377517710418}
-.4.52
now all these values of 3.30, -4.52 which are not exactly 5, messing up my other function.
look below
private _setPivotPointOnMouseHit(mouseEvent: MouseEvent, shouldRerender: boolean = true): void {
this._setRaycasterFromMouse(mouseEvent);
let rect: ClientRect = this.canvas.getBoundingClientRect();
let intersects: THREE.Intersection[] = this.octree.raycast(this.raycaster);
let minTargetToCameraDistanceAllowed = 10;
intersects.sort(function (a: THREE.Intersection, b: THREE.Intersection): number {
return a.distance - b.distance;
});
if (!intersects || intersects.length === 0) {
var distance = new THREE.Vector3().subVectors(this.camera.position, this.controls.target).length();
console.log("camera position-->", this.camera.position);
console.log("this.controls.target.distanceTo(this.camera.position)",this.controls.target.distanceTo(this.camera.position));
var targetToCameraDistance = Math.max(minTargetToCameraDistanceAllowed,this.controls.target.distanceTo(this.camera.position));
console.log("target to camera dstce-->",targetToCameraDistance);
var vc2 = new THREE.Vector3();
this.camera.getWorldDirection(vc2);
var newTarget = vc2.setLength( targetToCameraDistance ).add(this.camera.position);
console.log("New Target-->",newTarget);
this.controls.target = newTarget;
this.controls.update();
this.camera.updateProjectionMatrix();
return;
}
this._setPivotPoint(intersects[0].point);
if(shouldRerender) {
this.render();
}
}
In the above function, I am trying to set the orbit controls target even if intersects value is 0. if intersection happening, then everything is working, but if intersection not happening, then my controls target in setting in the middle of object area, because of mismatch between factor 5 and length of vector-based on the cursor position.
if I check camera distance in above function, I clearly see 5 is getting add/subtract from an old distance but if I check the camera position, then it is variable based on cursor position and camera look At an angle.
It has something to do with the angle between camera and mouse cursor, but I am not able to understand what.?
How can I make sure that no matter the angle/position/cursor, it should always add/subtract factor 5 to camera-target distance and camera position.?
I'm searching for a way to "live" draw rectangles or circles on a canvas.
I found various ways with fillRect() to draw rectangles, but not live. What I mean is, to be able to mouseDown() on one point and move it to another point in the canvas, which defines the size of the canvas, just like for example in Microsoft Paint, OneNote and so on.
Can anybody help me out and give me an advice on how to get started? I might be thinking of a way on how to do it, without seeing the rectangle (or circle) size changing, something like:
$("canvas").mousedown(function(event){
var ctx = this.getContext("2d");
ctx.clearRect(0,0,$(this).width(),$(this).height());
var initialX = event.clientX - this.getBoundingClientRect().left;
var initialY = event.clientY - this.getBoundingClientRect().top;
$(this).mousemove(function(evt) {
ctx.strokeRect(initialX, initialY, evt.clientX - event.clientX, evt.clientY - event.clientY);
});
});
But I want to see it live, so how the rectangles size changes when the user moves the mouse.
https://jsfiddle.net/zb66mxra/2/
To do it live you need to keep a constant image of your Canvas. This is accomplished easily by keeping an array of objects to be drawn over and over again by your JavaScript.
let drawArr = [];
An example object contains an x and y coordinate to begin drawing, a width, and a height:
{ x: 100,
y: 100,
w: 10,
h: 10 }
when your mouse moves over the canvas you ONLY want it to change the array if the mouse is down. This means you need to set a flag to see if this case is either true or false:
let mousedown = false;
canvas.addEventListener('mousedown', function(e) {
mousedown = true;
...
});
canvas.addEventListener('mouseup', function(e) {
mousedown = false;
});
When your mouse is down you want to add an item to draw to the array:
canvas.addEventListener('mousedown', function(e) {
mousedown = true;
drawArr.push({
x: e.pageX,
y: e.pageY,
w: 0,
h: 0
});
});
The height and width are initially set to 0. What we want to do now, if you can imagine, is create a height and width of the rectangle while we're dragging the mouse over the canvas and the mouse is down. We want to adjust this on the fly so that when the screen is re-rendered it is seen as it's being drawn.
It's easy to manipulate the height and width because, as long as you're only able to draw one at a time, it will ALWAYS be the most recent object added to the draw array.
canvas.addEventListener('mousemove', function(e) {
if (mousedown) {
let i = drawArr.length -1;
let {
x,
y
} = drawArr[i];
drawArr[i].w = e.pageX - x;
drawArr[i].h = e.pageY - y;
}
});
Finally we use requestAnimationFrame to constantly draw any object within the draw array. We do this by calling it when the page is loaded:
requestAnimationFrame(draw);
And then recursively within the draw function:
function draw() {
...
requestAnimationFrame(draw);
}
Then we simply need to clear the previous screen render and iterate over the draw array and draw everything to the screen again.
function draw() {
ctx.clearRect(0, 0, window.innerWidth, window.innerHeight);
for (let obj of drawArr) {
let {
x,
y,
w,
h
} = obj;
ctx.strokeRect(x, y, w, h);
}
requestAnimationFrame(draw);
}
voila.
What I'm trying to create is a small canvas widget that would allow a user to dynamically create a shape onto an image and then place it above an area that caught their interest, effectively it is a highlighter.
The problem is with adding a zoom function, as when I zoom onto the image I would like to ensure that;
There is no possible way for the dynamically created shape to be
dragged anywhere outside the image area. (completed - ish, relies on 2nd step)
You cannot drag the image out of the page view, the canvas area cannot show white space. Part of the image must always be shown, and fill the entire canvas area. (problem)
Here are two examples that I've drawn up, neither of which work correctly;
First example - getBoundingRect does not update and is bound to the image
Second example - getBoundingRect does update and is bound to the grouped object
From the link description you can see that I think I've narrowed the problem down, or at least noticed a key difference between the scripts with how the getBoundingRect behaves.
The first plunk seems to work fine, until you try to zoom in multiple times and at a greater zoom level, then it seems to start bugging out (may take a few clicks and a bit of messing around, it is very inconsistent). The second plunk is very jittery and doesn't work very well.
I've been stuck on this for a week or so now, and I'm at breaking point! So really hoping someone can point out what I'm doing wrong?
Code snippet below for first plunk;
// creates group
var objs = canvas.getObjects();
var group = new fabric.Group(objs, {
status: 'moving'
});
// sets grouped object position
var originalX = active.left,
originalY = active.top,
mouseX = evt.e.pageX,
mouseY = evt.e.pageY;
active.on('moving', function(evt) {
group.left += evt.e.pageX - mouseX;
group.top += evt.e.pageY - mouseY;
active.left = originalX;
active.top = originalY;
originalX = active.left;
originalY = active.top;
mouseX = evt.e.pageX;
mouseY = evt.e.pageY;
// sets boundary area for image when zoomed
// THIS IS THE PART THAT DOESN'T WORK
active.setCoords();
// SET BOUNDING RECT TO 'active'
var boundingRect = active.getBoundingRect();
var zoom = canvas.getZoom();
var viewportMatrix = canvas.viewportTransform;
// scales bounding rect when zoomed
boundingRect.top = (boundingRect.top - viewportMatrix[5]) / zoom;
boundingRect.left = (boundingRect.left - viewportMatrix[4]) / zoom;
boundingRect.width /= zoom;
boundingRect.height /= zoom;
var canvasHeight = canvas.height / zoom,
canvasWidth = canvas.width / zoom,
rTop = boundingRect.top + boundingRect.height,
rLeft = boundingRect.left + boundingRect.width;
// checks top left
if (rTop < canvasHeight || rLeft < canvasWidth) {
group.top = Math.max(group.top, canvasHeight - boundingRect.height);
group.left = Math.max(group.left, canvasWidth - boundingRect.width);
}
// checks bottom right
if (rTop > 0 || rLeft > 0) {
group.top = Math.min(group.top, canvas.height - boundingRect.height + active.top - boundingRect.top);
group.left = Math.min(group.left, canvas.width - boundingRect.width + active.left - boundingRect.left);
}
});
// deactivates all objects on mouseup
active.on('mouseup', function() {
active.off('moving');
canvas.deactivateAll().renderAll();
})
// sets group
canvas.setActiveGroup(group.setCoords()).renderAll();
}
EDIT:
I've added comments and tried to simplify the code in the plunks.
The relevant code starts within the if (active.id == "img") { code block.
I've put irrelevant code as functions at the bottom, they can largely be ignored. ( createNewRect() + preventRectFromLeaving() )
I've removed one of the plunks to avoid confusion.
Let me know if it helps, or If I should try to simplify further.
Thanks!
I think that the grouping was messing with the position of the background image. So, I tried removing the group when the image is moving and manually updating the position of the rect instead.
It sets the last position of the image before moving
var lastLeft = active.left,
lastTop = active.top;
And then it updates those and the position of the rect every time the image moves
rect.left += active.left - lastLeft;
rect.top += active.top - lastTop;
// I think this is needed so the rectangle can be re-selected
rect.setCoords();
lastLeft = active.left;
lastTop = active.top;
Since the image has to stay within the canvas, the rect stays inside the canvas, too, whenever the image moves. The rest of the code you wrote seemed to work fine.
http://plnkr.co/edit/6GGcUxGC7CjcyQzExMoK?p=preview
I have a firefox extension that needs to get the exact screen co-ordinates of a DOM element and passes it to a native DLL via js/c-types.
Now I have it mostly covered :
var gDomWindowUtils = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(nsIDOMWindowUtils);
function getScreenRect(oElem)
{
var rc =
{
x : 0,
y : 0,
w : 0,
h : 0
};
var o = oElement;
while(o != null)
{
rc.y += o.offsetTop;
rc.x += o.offsetLeft;
o = o.offsetParent;
}
var x = {}, y = {};
gDomWindowUtils.getScrollXY(false, x, y);
rc.x -= x.value;
rc.y -= y.value;
var scale = gDomWindowUtils.screenPixelsPerCSSPixel;
rc.x *= scale;
rc.y *= scale;
rc.w *= scale;
rc.h *= scale;
return rc;
};
This handles scrolling and zooming, but the values I get are relative to the browser window, and not the screen.
How do I detect the offset of the client area of the actual rendering area of the browser?
I can even use native code (Win32) via js/ctypes so I tried to see if I could use FindWindow() / GetWindowRect() to get it, but the whole of firefox is a single HWND, the controls are all not native windows.
So one idea I have is, since the UI of firefox is an XUL document, I should be able to get the menubar, tab bar etc etc and find the browser areas absolute offset. But, I have no clue how to access the XUL tree that defines the browser UI.
Can someone give me a pointer?
[Edit]
Ignore rc.w and rc.h being undefined in the above code , it is irrelevant to the question.
You mostly got it already but I would recommend using getBoundingClientRect() instead of offsetLeft/offsetTop:
var rect = oElement.getBoundingClientRect();
var rc = {
x: rect.left,
y: rect.top,
w: rect.width,
h: rect.height
};
getBoundingClientRect() considers scrolling so that you no longer need to add it. You get the coordinates relative to screen using window.mozInnerScreenX and window.mozInnerScreenY:
rc.x += window.mozInnerScreenX;
rc.y += window.mozInnerScreenY;
And after that you multiply the values with screenPixelsPerCSSPixel. That should give you proper screen coordinates.
I will add one more "trick" that helped me with a similar problem.
Whenever my code receives a mousemove event, I capture the mouse-cursor positions in ALL the coordinates that event provides, which includes at least "screen", "client" and "pageXY" coordinates. Then for any OTHER purpose I can compute the difference between these coordinates simply by subtracting the appropriate two of those.
In your case, you'd probably compute an offset like this:
deltaX = event.screenX - event.clientX;
deltaY = event.screenY - event.clientY;
or
offsetX = event.screenX - event.pageX;
offsetY = event.screenY - event.pageY;
Then just add deltaX and deltaY or offsetX and offsetY to those coordinates to get screen coordinates.
I'm working with this script "Drawing polygons with the mouse" and it works very well.
The issue I have is when I put the canvas in the design of my site. The canvas is thus now in relative position and the coords are wrong. I have a lag between my cursor and the draw…
If I set the div in position: fixed, there is no problem.
The positions are declared as follows:
canvas.addEventListener("click", function(e) {
var x = e.clientX-canvas.offsetLeft;
var y = e.clientY-canvas.offsetTop;
How to fix this? How to put the canvas in my design and have the right coords?
Thank you very much!
Try my "simple" mouse code (simple because it does not take into account border/padding/HTML offset):
function getMouse(e, canvas) {
var element = canvas, offsetX = 0, offsetY = 0, mx, my;
// Compute the total offset
if (element.offsetParent !== undefined) {
do {
offsetX += element.offsetLeft;
offsetY += element.offsetTop;
} while ((element = element.offsetParent));
}
// This isn't the best code because I am not adding padding and border style widths to offset. I'm just keeping it simple.
mx = e.pageX - offsetX;
my = e.pageY - offsetY;
return {x: mx, y: my};
}