Does anyone know of a really good Javascript pinch-zooming algorithm?
This is trickier than I had imagined, as you have to scale -AND-
translate to keep the pinch zooming centered between your fingers.
And I want it to do pure translation when the user moves two fingers -
kept an equal distance apart from each other - around the screen,
i.e., this moving two fingers in unison should act like a single-
finger "pan".
scale factor = distance between end point cursor / distance between initial point cursor
distance between start cursor pointer = √((start cursor pointerx2-start cursor pointerx1)^2 * (start cursor pointery2-start cursor pointery1)^2)
Related
I have a mobile web app written in JavaScript, using alloyfinger.js.
I tried Hammer.js, but it didn't work with certain iPhone models (eg iPhone 7+).
I suspect my question is the same for various gesture detection libraries.
My app detects rotate events, but I can't seem to write the correct code to ignore rotate events below a minimum angle of rotation. The code below hides the rotated element, even when the amount of rotation is very small.
handleRotatEvent(evt) {
const angle = evt.angle;
const absAngle = Math.abs(angle);
const minAngle = (90 * Math.PI / 180);
// convert 90 degrees to radians
if (absAngle < minAngle)
return;
hideItem(evt);
// use the event to find the target DOM element and hide it
}
My intent is to ignore rotations smaller than 90 degrees. Otherwise, hide the rotated element.
What am I misunderstanding or doing wrong here?
Thanks!
Adam Leffert
https://www.leffert.com
#dntzhang, the author of AlloyFinger.js answered this question on Twitter. He said that the angle resets as the user rotates the element. My more fundamental intent was to detect small rotations as a way to distinguish rotate events from swipe or pan events. He said that the correct way to do that is to check the number of fingers touching the element. One for swipe or pan. Two for rotate. Much simpler.
I am writing a script with Fabric.js to zoom an image at the current mouse position. I have made some progress but there is an error somewhere.
Case 1: Keep the mouse at one point and zoom with the mouse wheel.
Result: Works perfectly, image zooms at that particular pixel.
Case 2: Zoom in a little at one position (3-5 times with mouse wheel), then move the mouse to a new position and zoom in there.
Result: Works fine for the first point, but after moving to another point and zooming, the image position is incorrect.
My code is in this fiddle:
https://jsfiddle.net/gauravsoni/y3w0yx2m/1/
I suspect there is something wrong with the image positioning logic:
imgInstance.set({top:imgInstance.getTop()-newMousY,left:imgInstance.getLeft()-newMousX});
What is going wrong?
The key to solving this puzzle is to understand how the image gets enlarged. If we're using a zoom factor of 1.2, the image becomes 20% larger. We assign 1.2 to the variable factor and do the following:
image.setScaleX(image.getScaleX() * factor);
image.setScaleY(image.getScaleY() * factor);
The upper left corner of the image stays in the same place while the picture is enlarged. Now consider the point under the mouse cursor. Every pixel above and to the left of the cursor has become 20% larger. This displaces the point under the cursor by 20% downward and to the right. Meanwhile, the cursor is in the same position.
To compensate for the displacement of the point under the cursor, we move the image so that the point gets back under the cursor. The point moved down and right; we move the image up and left by the same distance.
Note that the image might have been moved in the canvas before the zooming operation, so the cursor's horizontal position in the image is currentMouseX - image.getLeft() before zooming, and likewise for the vertical position.
This is how we calculate the displacement after zooming:
var dx = (currentMouseX - image.getLeft()) * (factor - 1),
dy = (currentMouseY - image.getTop()) * (factor - 1);
Finally, we compensate for the displacement by moving the point back under the cursor:
image.setLeft(image.getLeft() - dx);
image.setTop(image.getTop() - dy);
I integrated this calculation into your demo and made the following fiddle:
https://jsfiddle.net/fgLmyxw4/
I also implemented the zoom-out operation.
I have a graph rendered within the HTML5 canvas. The working is good till this point. Now I need to implement pinch zoom on the graph for touch devices. The logic is as the two finger stretches apart the graph zooms in and as the finger moves together the graph zooms out. In this case we need to constantly update the axis value. The problem here is how do we get the individual X and Y axis value of both the fingers and then calculate the amount of zoom to be done. As for example, for zooming using mouse we can get the start X and Y value on mouse down and on mouse up we get the end X and Y axis value. Using this start and end value of X and Y axis the graph can be zoomed accordingly. The canvas should not zoom in/out. The zoom in can be infinite but the zoom out will be till the default plotting of the graph. Any idea or help would be really appreciable. I am not getting the proper calculation.
I have implemented it in the following way. Any suggestions are welcome.
First I have taken the screen co-ordinates of the two fingers touch on start and converted it to its corresponding view co-ordinates by calculating its distance from the topmost and leftmost position. After that I have calculated the scale and new co-ordinates for X-axis in the following way.
Let
d1 and d2 be the dataspace coordinates of the initial/starting touches.
newx1 and newx2 are the x positions of the new touches.
screenW is the current screen width (i.e. width of plot in screen space).
Then
scale = (d2 - d1) / (newx2 - newx1)
If we use newd1, newd2 to denote the new datarange min and max values that we're trying to compute:
newd1 = d1 - newx1 * scale
newd2 = newd1 + screenW * scale
Similarly, we can do the calculation for new datarange min and max values of Y-axis.
Variant A:
Take a JavaScript library like Hammer.js which abstracts away all then event handling and gives you an event in case of a pinch. This should look like this:
var element = document.getElementById('test_el');
var hammertime = Hammer(element).on("pinchout", function(event) {
console.log("Zoom out!!");
// event.scale should contain the scaling factor for the zoom
});
Variant B:
Read about the touch events and how to identify if it is a multitouch. Figure out when it is a pinch and if how far the fingers have moved. There is nice write up here.
I am working on a page where I can view images. I want to create a rotation tool. I've done that, but, it's not working consistently. When I set up the centre point to rotate by, the image jumps slightly, and it gets worse each time. I was experimenting, and, I have code to add a wedge to the top left corner of my top level group ( so, at 0,0 ). If I rotate the image by 45 degrees and drag it so that half of it is off the left edge of my canvas, then I call getAbsolutePosition on the wedge and on the group, I get these values:
layer.getAbsolutePosition()
Object {x: 104.66479545850302, y: 279.2748571151325}
wedge.getAbsolutePosition()
Object {x: 180.2684127179338, y: -73.48773356791764}
I think this means my y position is actually the bottom of the image, which is off screen.
What I want to do, is calculate the absolute position of the middle of my image, when the mouse moves over it, regardless of it's rotation. I have some code that works out points with rotation, which seems like it works at first, almost, but it just gets more and more broken the more I use the tool. I feel like there's something about how Kinetic is tracking these things and what it's reporting, that I am missing. Any hints would be most appreciated. Tutorials I can read are even better ( yes, I've read everything linked from the KineticJS site and searched the web ).
In a nutshell, the question is, if I have an image inside a group, and it's rotated, how do I work out the centre point of the image, taking the rotation in to account, and how do I set the offset so it will rotate from that point, and stay in the same place ?
Thanks
As you've discovered about KinetiJS:
rotation is easy
dragging is easy
dragging+rotation is difficult
After you drag your image you must reset its rotation point (offsetX/offsetY).
KineticJS makes dragging+rotation more difficult than it has to be.
Resetting the offset points of your image will cause KineticJS to automatically move your image (Noooo!!).
That's what's causing your jumping.
The solution to the "jumping" problem:
When you reset the image's rotation point (offsetX/OffsetY) you must also reset the image's X/Y position.
This code resets both XY and Offsets for an image after dragging:
A Demo: http://jsfiddle.net/m1erickson/m9Nw7/
// calc new position and offset
var pos=rect.getPosition();
var size=rect.getSize();
var offset=rect.getOffset();
var newX=pos.x-offset.x+size.width/2;
var newY=pos.y-offset.y+size.height/2;
// reset both position and offset
rect.setPosition([newX,newY]);
rect.setOffset(size.width/2,size.height/2);
Need some inspiration. I've got a picture (blue) and want it to move proportional to the mouse position inside an invisible area (orange). So, if the mouse moves in top-left direction, the image should follow the movement.
I don't want to simply copy the mouse position, rather create an Joystick like behaviour, so if the mouse moves, the image should move stepwise in the desired direction.
But how? I've no idea how to set the right x+y coordinates for the image or how to establish a formula to calculate them.
Incremental (vectored) positions. Consider:
Let's call the dead center of your invisible circle the vector reference point (0,0) or VRP.
You move your mouse away form the VRP. Let's use your image as a reference and say that your mouse is at (-3x -2y) relative to the VRP. You keep it there; It creates a -3 X vector and a -2 Y vector.
For as long as you keep your mouse there, those vectors will be applied to the square's current coordinate at each cycle, like this:
Assume Picture starter position is absolute 100,100.
Cycle 1: [x]:100 -3 = 97;[Y]:100 -2 = 97. New picture position = 97x98y.
Cycle 2: [x]:97 -3 = 94;[Y]:98 -2 = 96. New picture position = 94x96y.
And so forth. If you want to stop the movement, just bring the cursor back to the VRP.
You may sophisticate the mechanism creating acceleration intermediate vectors, or a dead zone around the vector reference point.