How do I adjust panning after zoom? - javascript

I have a view built in React. Simplified it consists of a workspace which is positioned absolute in relation to the viewport. The viewport is the same size as the browser window. Normally (but not always) the workspace will be larger than the viewport, like in the figure below.
The user can pan the workspace revealing different areas of it in the viewport. The panning sets a negative offset on the workspace in relation to the viewport expressed in pixels (PanX and PanY).
The user can also zoom in and out. Zooming is done in fixed steps, in this example with a factor of 0.25, i.e. +0.25 for zooming in and -0.25 for zooming out. This affects the scale which in turn affects the size of the workspace. In figure 2 below the user has zoomed in one step (scale +0.25).
Every object in the workspace scale proportionally to the workspace. In this example the green triangle represents an object in the workspace. So after zooming in, the triangle will be larger. When the object becomes larger its offset in the viewport will change. In this example it will look like it grows downwards and to the right.
What I want to accomplish is that the center of the viewport should still be centered after the user has zoomed in or out. To accomplish this I need to adjust the PanX and PanY so the viewport remains centered over the point in workspace where it was centered before the zoom.
So my question is: How do I keep the viewport centered over the same point in the workspace when zooming in and out?
The calculation should result in new values that I can set for PanX and PanY for the workspace that centers the viewport. Keep in mind that the viewport can be positioned anywhere as long as none of its edges are outside the workspace (like in the figure below).
Thank you in advance!

If you want to keep whatever is in the center of the screen in the center of the screen, then define:
const centerX = screen.width / 2;
const centerY = screen.height / 2;
Then when you scale by a value scale (e.g. 0.25 or -0.25), you need to adjust PanX and PanY:
PanX -= scale * centerX;
PanY -= scale * centerY;
I wrote a tutorial here, which uses an SVG and a slightly different transformation system, but it is basically the same thing.

Related

Don't effect element position while scaling stage in konva.js

I am creating page creation software online using amazing konva.js so the problem is when I was scaling the stage the whole elements are changing there position. Before scaling whole stage
After scaling the stage the line text is not in center.
the background at back is just rect having same width height of stage and having background image.
This is my scaling code.
function scaleboth(_x,_y){
stageBackgroundRect.scale({x:_x,y:_y});
stage.scale({x:_x,y:_y});
layer.batchDraw();
}
if you want to see demo here it is,
https://mypagemaker.s3.amazonaws.com/index.html
Thanks, I promise I will give bounty to correct answer.
As I have to offset it,I have done this but no change
function minusStage(){
stage.offsetX(stage.width() / 2);
stage.offsetY(stage.height() / 2);
scaleboth(stage.scaleX() - 0.01,stage.scaleY() - 0.01);
}
Your background rectangle is inside a stage. So scaling the stage will affect the absolute scaling of all its children including the background rectangle.
You just need to apply scale once on one of the parents (it can be the stage).

Complex mathematical horizontal parallax easing function

I'm currently diving into parallax effects on the web.
Parallax scrolling is a technique in computer graphics and web design, where background images move by the camera slower than foreground images, creating an illusion of depth in a 2D scene and adding to the immersion.
~ Wikipedia
I want to create a little container (could be an image, or any block level element) and move it across the screen horizontally as the user scrolls.
The effect should be scalable across all viewports. Meaning that the hight and the width of the element that the element is moving across should not matter.
When the user has scrolled half of the height of the screen the "moving element" should be in the exact center. Since the user will have scrolled half of the screen the element will be vertically already. We're only worried about horizontally right now.
I've thought about this question for a while and came up with a pretty good idea of how.
Take the hight and the width of the element you want the "moving element" to move across. For example a screen that is 1000px tall and 600px wide.
Divide the width by the height. For example (600px / 1000px = 3/5 = 0.6)
Take the amount of pixels the user scrolled and multiply it by the number we just created. For example (500px * 0.6 = 300px). As you can see this is the exact center.
Move the element across the screen by the amount of pixels just calculated.
This calculation works fine even for every screen size, however it's linear. Meaning that the element will move at the same speed across the screen all the time. Let me show you what I mean.
Let's draw out a screen size. (Let's say 1000 * 500)
Calculate two points for this graph ->
screen factor: (500 / 1000) = 0.5
1. The first point is going to be easy. Let's say we scrolled exactly 0px -> (0.5 * 0) = 0
The "Moving element" will not have moved at all.
2. For the second element we'll take the center. Just for convenience.
The vertical center is at 500px -> (0.5 * 500) = 250 px (Exactly the horizontal center)
Put the results in a graph and draw a line through the points.
In the graph above you can see that whenever the user scrolls down the "moving element" will follow the line (the values on the x-axis).
My question
I really hope I described all that well enough to understand. Now on to my question.
What I want to create is a moving element that would go faster on the edge of the screen and slow down a bit in the middle. If we were to draw that out in the same way we just did. (Creating a graph where we can take the amount of pixels scrolled and see where the element should be positioned horizontally) it would look like this:
Sorry for the poor quality of the image but this is the part I'm having problems with.
As you can see in this graph the "moving element" wouldn't be moving all that much in the middle of the graph. (I over did it a bit in my drawing but you get the general idea.)
What I need is a mathematical function that takes three parameters (Screen height, width and the amount of pixels scrolled) and returns the horizontal position of the "moving element".
My idea:
My idea was to position the element in the dead center of the page and then to move it left and right (translations using CSS and JavaScript) based on how far there has been scrolled.
The graph would look something like this:
The (Hand drawn) graph above would be true for a screen that's 1000x600px since the "moving element" translates -300px when no scrolling has been done and 300px when 100% has been scrolled.
However I have no idea on how to create a mathematical function that would be true for every screen size.
To be clear I need a function that "always" starts at Y= (-screen-width/2) and X = 0. It should always cross the point (sreen-height; (screen-width//2)) and the function should be in a form of x^3 (To get the right easing)
I really hope I explained myself well enough and I really hope somebody can help me out here.
Answer from math.stackexchange
Besides asking my question here I also went ahead and posted this question on math.stackexchange.com. Stackoverflow's mathematical sister site. Somebody there helped me find an answer to my question.
The function had to be a function that would output the moving-element it's horizontal position in pixels relative to the horizontal center of the page based on the amount of pixels scrolled since the element was first visible. The function would have to be "steeper" on the edges and ease into a short stop in the middle and be usable across every possible screen size. Meaning that the mathematical function would have to be positioned based on two variables, screen height and -width.
The answer they came up with on math.stackexchange:
In this example s-width is the width of the screen in pixels. s-height is the height of the screen in pixels. px-scrolled is the amount of pixels scrolled since the element was first visible.
The output pos is the moving elements horizontal position in pixels relative to the center of the screen.
If you put all this math into JavaScript you get the following:
var pos = ((4*win_width)/(Math.pow(win_height, 3))) * Math.pow(px_since_visible - (win_height/2),3)
There is a working example available on codepen. You can find it here.
You can position it with a function f that actually draws that trajectory.
This is the idea I propose:
Create the function trajectory f such that f(0) = 0, and f(1) = 1 (add more constraints in order to reproduce the effect you are looking for, ex: f(0.5) = 0.5)
Within each scroll event, set x as the amount scrolled and position the element using the coordinates (f(x) * (w - s), x * (h - s)), where w is the document width, h is the document height and s is the size of the element
I can see that cubic functions are plotted like the trajectory you want, so I've been testing with different functions and I've got this working example https://codepen.io/anon/pen/YZJxGa
var element = $('.element')
var height = $(document).height()
var scrollable = $('body').height() - $(window).height()
var width = $('body').width();
$(window).scroll(function () {
var winH = $(window).scrollTop()
var x;
// Determine the amount of px scrolled since the element first came into view.
console.log(winH, scrollable)
x = (winH/scrollable) > 1 ? 1 : (winH/scrollable);
var posY = x * (height - 120);
var posX = (-1.000800320128*x
+6.0024009603841*x**2-4.0016006402561*x**3)*(width - 120)
console.log(posY)
if (x > 0) {
console.log(`translate(${posX}px, ${posY}px, 0)`)
element.css({
'transform': `translate3d(${posX}px, ${posY}px, 0)`
})
}
})
You can generate more cubic functions using this tool I've just found http://skisickness.com/2010/04/28/ or solve a couple of systems of linear equations using the fact that you just want to find values for a, b, c and d for f(x) = ax^3 + bx^2 + cx + d

Initial Sprite Size in Three.js

Can someone help me understand how three.js initially determines the size/scale of a sprite?
At the moment I'm working with 4 sprites (PNGs with transparency that are 3000px × 1830px) stacked in 3D space, but I'm having to scale them up between 16x and 22x. In order to keep the sprites from looking squashed, though, I have to scale the y-axis 75% of the x-scale.
Eventually, I want to be able to pull in images systematically, and have them scale appropriately.
It's possible I just haven't set this thing up correctly. It looks right, but it's super hacky-feeling right now. I pretty much changed a bunch of numbers, until it looked right to me. I don't like that. I want to understand.
Here's what I'm working with currently:
http://valorink.com/3d-test/stackoverflow/
Looking into the code of the Sprite class reveals that a simple plane with width and height of 1 is created in the constructor. I wouldn't have expected anything else, because the geometry size is usually not defined by the texture size.
You probably want them to fill the viewport, so you have to scale them. With perspective camera its a bit of math, because the amount of x-scale (or y-scale) to fit the viewport size relates to the distance to the camera. So, it should be something like
var halfHeigt = distanceToCamera / Math.tan( camera.fov/2 * Math.PI / 180 );
y-scale = halfHeight * 2;
And of course you need to consider the aspect ratio in order to not looking squashed. So, x-scale should be y-scale * textureWidth / textureHeight, or the other way round y-scale = x-scale * textureHeight / textureWidth.

Image zoom centered on mouse position

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.

How to compute rotation/scale pivot coordinates for SVG transformations (I'm using Raphael.js)?

I'm having serious trouble understanding how to compute the coordinates of rotation/scale pivots (e.g. rotation point) for SVG transformations, using Raphael.js. In short, if you apply a transformation such as image.transform("S1.5R45"), the transformations are applied in relation to the default rotation & scale pivot, which I'm not sure how to calculate.
To exemplify, I've put together a fiddle (jsfiddle.net/GVEqf/), where the aim is to have exactly the same output in both the viewports, for a couple of transformations on an image object. In the first viewport, I don't specify the rotation point, but in the second one I do. However, I can't get the same results. What I need is to input the rotation coordinates for the second viewport, so that the output is identical with the first case.
Please help.
When not specified the pivot is the center of the element.
Here you have to take care of the position you have applied to the images and the scaling that will be done. Since in this case your scaling is relative to the top left corner of the image, we can just multiply the center coordinate by it.
// Compute rotation pivot coordinates
var scaling = 1.5;
rx = (x + (img_width / 2)) * scaling;
ry = (y + (img_height / 2)) * scaling;
// Apply transformations
image1.transform("S1.5,1.5,0,0R45");
image2.transform("S1.5,1.5,0,0R45,"+rx+","+ry);
http://jsfiddle.net/TYCJ7/

Categories