Browser zoom and cursor coordinate problem - javascript

I'm making a project using P5.js and I'm having a problem. I need to let the user zoom out, but not zoom in, above 100% zoom. I achieved this with CSS (max-width: 100%, max-height: 100%). But there is a need to somehow compensate for the difference in coordinates. Here is a link to a sketch showing the problem.
What's the best way to keep the ratio of the square and cursor coordinates when scaling on any screens?
UPD: I found a solution:
let cnv;
function setup() {
cnv=createCanvas(windowHeight, windowHeight );
cnv.style("width", 'unset');
}

This will make the mouse coordinate between 0 and 1:
text("Mouse position = " + mouseX / width, 30, 40);
The variable width is the canvas width.

Related

Threejs orthographic camera viewport

I use orthographic camera and i struggle with correct viewport calculation.
I want to push camera to the left for 100px to render something else in extra area.
In the following example there's a rendered green cube (for which i want to fix my viewport) and red square (expecting result made with css).
I can't change canvas size - it has to be 100% width and 100% height.
Best solution so far was to add extra value to camera left like:
camera.left = - frustumSize * correctionAspect + 10;
But of course it doesn't scale. Other solution was to calculate camera.left with different aspectRatio like in the commented code:
correctionAspect = (width + 200) / height;
But it doesn't work well either.
Example code: https://stackblitz.com/edit/js-nydhjw

Scaling and repositioning with SVG.js difficulties

I’m using svg.js to manipulate SVGs within the browser. Most of what I’m doing is relatively simple, but I’m having some difficulties with scaling/positioning a few objects.
I have an SVG that contains a “Pin” icon. You can click on the SVG to zoom it (which just resizes it to fill the browser viewport), in turn resizing all of its children, including the Pin. I need to scale this icon back down to its original size of 36px x 36px and reposition it so the bottom center of the Pin sits where it originally sat. I’ve got the resizing down, but the repositioning piece escapes me.
Some example states:
Scaled at 100% with Pin at base size of 36px x 36px.
Scaled up by 9.77241379 with pin scaled down to its base size of 36px x 36px. Using svg.js the scale() method scales at the center point of the pin, leaving it floating in space.
What I’m using to scale the Pin when the parent container is scaled up:
scaleHotspot(hotspot) {
const child = hotspot.first();
const bbox = child.bbox();
const rbox = child.rbox();
const scale = bbox.w / rbox.w;
hotspot.scale(scale);
}
Because the Pin is scaled using its center point it’s now sitting up higher than it’s supposed to be. I need to determine how much to move the Pin down to have the point of the Pin sitting in its original position.
I originally thought this worked but testing in various places yielded strange results.
const newY = -(bbox.height / 2 - (bbox.height - (1 / scale)));
Suggestions around getting the Pin positioned so that its bottom center point sits where the unscaled version was?
With .transform(), you can define a center around which to scale. Use .bbox() to find that point in current user space. Since these coordinates already include pre-existing transformations, set the relative flag to add the scaling on top:
scaleHotspot(hotspot) {
const bbox = hotspot.bbox();
const rbox = hotspot.rbox();
const scale = bbox.w / rbox.w;
const center = bbox.x + bbox.w / 2;
const bottom = bbox.y + bbox.h;
hotspot.transform({scale: scale, cx: center, cy: bottom}, true);
}

Zoom to cursor without canvas in javascript

I have an <img> that is zoomed upon mousewheel scrolling, by adjusting transform: scale(). I want the zooming to be like in Google Maps, where you zoom to where the mouse cursor is, not to the center of the image. I'd like to not use canvas though, just for the learning experience (that's also why the other questions I found did not really help).
I set up a JSFiddle to demonstrate the problem. My thought process was as follows: when zooming in by 10%, the image expands in all directions, from the center of the image, by 10%. That means that e.g., the left and right edge will travel 5% of the original width in each direction. I therefore tried to solve the problem like so:
Calculate mouse offset from image center
Calculate new image offset (top and left) by multiplying mouse offset with zoom factor and divide by two
Apply offset and watch it all blow up it my face with the power of a million burning suns
It seems that I just can't find a formula or algorithm that fits.
Eventually I figured it out myself, although only by looking at existing solutions. Here is the JSFiddle that contains only the essentials.
The idea is to first set transform-origin: 0 0. This makes sure that, upon zooming, the image expands down and right, instead of distributing the increase in width over all four sides. Note that it does not reposition the image, it just changes the origin for all transformations.
Additionally, this JSFiddle assumes that the top and left margins of the image are aligned with the top and left margins of the container element. If the image should be repositioned before zooming occurs, this should be done through transform: translate() and the translateX and translateY values need to be updated accordingly.
The heart of the logic is this:
// Track the percentage change between the old
// and the new scale of the image
const ratio = 1 - nextScale / currentScale
// get the current mouse offset
const {
clientX,
clientY
} = event
// The += here is extremely important!
// The new 2D translation values are derived from the difference
// between mouse cursor position and current (!) 2D translation.
// So where is the mouse cursor relative to the translated image
// This difference is then adjusted by the % change of the scaling
translateX += (clientX - translateX) * ratio
translateY += (clientY - translateY) * ratio
/*
This would work for the first wheel scroll. But afterwards, the
image will not be translated enough to offset the zooming because
we're not taking into account the existing translation
translateX += (clientX - translateX) * ratio
translateY += (clientY - translateY) * ratio
*/
So to summarize the required steps:
Calculate the next scale
Calculate the current mouse offset relative to the translated image
Adjust the mouse offset for the change in scaling, e.g., const percentChange = 1 - nextScale / currentScale
Add the adjusted mouse offset to the existing values for translate()
Apply the transformation (scaling and the translation)
The linked JSFiddle also includes Lodash and transition: transform 330ms ease-in-out; to make the scrolling a little smoother and not affect browser performance too much.
You could use transform-origin : <position of your mouse pointer> :
transform-origin : 0% 0% points on the top left corner.
transform-origin : 100% 100% points on the bottom right corner.
Here's an example I made : https://jsfiddle.net/zez538L8/4/
The javaScript :
var currentzoom = 1;
function zoom(delta, e) {
var img = document.getElementById("test");
var width = img.offsetWidth; //calculating the size of the img (in px)
var height = img.offsetHeight;
var x = event.offsetX; //calculating the position of the mouse pointer on the picture (in px)
var y = event.offsetY;
var xpercent = x*100/width; //calculating the position of the mouse pointer on the picture (in %)
var ypercent = y*100/height;
img.style.transform = "scale("+currentzoom+")"; //scaling the picture
img.style.transformOrigin = xpercent + "% "+ ypercent +"%"; //transform-origin
currentzoom += delta;
}

Canvas, how to take aspect ratio into account with mouse events?

I have seen a few related posts on here but, from what I can understand, non that really fit my use case.
I have been reading this post on how to make a responsive canvas game and have followed the logic in step 1 and 2 and have positioned my canvas in the centre of the screen using the following:
<div id='game-screen'></div>
#game-screen {
position: absolute;
top: 50%;
left: 50%;
translate: transform(-50%, -50%);
}
The div is what contains the canvas created in my Javascript.
I have my canvas width and height different to the "styled" width and height as per this tutorial. My canvas width and height is 1280 x 1024 pixels.
Now the problem I am having is that when binding my mouse move event to the canvas, the event.pageX and event.pageY variables are not proportionate to the scaled canvas. I have taken into consideration the offset left and top but I am unsure as to how I could "scale" the mouse x and y to relate to the canvas aspect ratio?
There seems to be around a 5-20px difference based on the stretched canvas size.
Thank you in advance, I hope this makes sense.
You can use getBoundingClientRect() to get the game screen position into the document. So with a substraction, you can get the relative mouse position.
var gameScreen = document.getElementById('game-screen');
gameScreen.addEventListener('click', function(event) {
var gameScreenPosition = gameScreen.getBoundingClientRect();
var x = event.clientX - gameScreenPosition.left;
var y = event.clientY - gameScreenPosition.top;
alert(x + ' ' + y);
})
Example : https://jsfiddle.net/tzi/2cj8n8xt/

Scaling text relative to position in browser window.

I'm currently stuck on a little bit of math for my project. I'm trying to scale a div in my page based on how close it is to the center of the browser window, so when it is in the center of the window it is full size, but as you scroll down or up it scales down as if to disappear. I'm just struggling on how to calculate this value.
Thanks in advanced,
Harry.
let x and y be the position of your div relative to the browser window
window.innerHeight and window.innerWidth will give you the current visible window height and width.
var w = window.innerWidth;
var h = window.innerHeight;
The center would be
var center = (w/2, h/2);
the distance from the center is:
var distance = Math.sqrt((w/2 - x)*(w/2 - x), (h/2 - y)*(h/2 - y));
Now you want to scale the div so that it's maximum size when its distance from the center is 0 and smaller when it's further away.
The simplest thing to do is to use a width of w - distance and a height of h - distance. That will give you a linear scale, you can use other scaling functions as well, but I'll leave that to you to play with for now. :)

Categories

Resources