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
Related
I am developing a VueJS project and have created a set of cards appearing on the page, when one of these cards is selected, I wish for it to move to centre screen but keep the position it has moved from in the list of options.
I know that by changing the position from 'unset' to 'relative' the card now has move functionality with 'left', 'top' etc. but I still need to find a way to automatically move the card to centre screen regardless of where on the screen the card is moving from.
Does anyone know how to achieve this with the use of JS?
I imagine there is a way of receiving the current location of the node and moving it to the center of the screen, but I am not sure on the specifics of how to achieve it...
Image for context:
CardsProject
EDIT: I have for now gone with rendering an absolute position for the card which means there's no CSS transition from the card's original place to the centre of the screen and the card also temporarily loses its place within the deck.
Before click: click here for image
After click: click here for image
I found the answer after many, many hours of scouring the internet and deepfrying my code.
The answer: Don't use 'relative' positioning!
There's a far nice option to hold the position the element is moving from, but allow for the item to move freely with the use of CSS' top or left etc. and this option is position:sticky;!
With this and the use of JavaScript's coordinates documentation
.getBoundingClientRect()
...I managed to solve the mystery. The function I made to pull a vector between the current object and it the centre of the screen can be found here, returning an array of size 2 of X and Y vectors respectively.
function calcCenterMove(element){
/*
X and Y are the current position of the element to be moved (top left corner).
Width and Height are the width and height of the element to be moved.
CX and CY are the X and Y coordinates of the centre of the screen.
*/
var x = element.getBoundingClientRect().x;
var y = element.getBoundingClientRect().y;
var width = element.getBoundingClientRect().width;
var height = element.getBoundingClientRect().height;
var cx = window.innerWidth / 2;
var cy = window.innerHeight / 2;
var xVector = cx-(width/2)-x;
var yVector = cy-(height/2)-y;
return [xVector, yVector];
}
var xAxisMove = calcCenterMove(element)[0];
var yAxisMove = calcCenterMove(element)[1];
element.style = "transform: translate("+xAxisMove+"px,"+yAxisMove+"px);";
I have paired the above code with a z-index to place the element above all others, and a screen dimming cover, to prevent the user from scrolling elsewhere or interacting with any other options.
Issues still arise here if the user resizes the screen, but I believe that is a different issue to address, possibly by using an event listener to assess a window resize and translate the element from the previous centre to the new centre using the same cx and cy properties above (or perhaps even the entire function!).
Nevertheless, I have come to the answer I was looking for, anyone feel free to use the code above, if needed!
Here are images for reference:
Before click
After click
Regards!
I am creating a 2d platformer right now, and I decided that I would use parallax scrolling.
So what I did is I had 3 tile maps, one for the background, one for the foreground, and one for on front of the foreground.
Each tile map would display the tiles at a different size.
Background -> 48px
Foreground -> 64px
On front of foreground -> 80px
I got the pixels from looking at other 3d games(such as Minecraft), when cubes are further a way. I wanted each tile to be as if the size of it is a cube.
I used the below code to calculate where each tile goes on the screen, based on its x, y, and size.
x - (cameraX * size / 64) + canvas.width / 2; // 64 is the size of the texture.
y - (cameraY * size / 64) + canvas.height / 2;
Here was the result:
As you can see the tiles in the background are moving slower.
However, when you jump, there would be white space between the tiles:
I understand why this is happening, it is because there should be a top face on the tile (as if it is a cube), but since it is only a square, it shows white space.
So I used this code to make it look as if it is a 3d cube.
var i = (size - size * 0.25);
while (i < size)
{
ctx.drawImage(images[this.type], getProperX(x*(i), i), getProperY(y*i, i), i, i);
// The first argument of getProperX/getProperY is the x/y, the second argument is the size of the tile.
i += 1;
}
This keeps drawing loads of squares, with a size slightly smaller than the last, so that the getProperX/Y function will display it in the right place.
Now they looks like cubes, so the problem is nearly fixed:
The only problem now, is that the length of the cubes are very long when you are looking on top/bottom of it, or to the right/left of it (as if it is distorted).
Why are the cubes/tiles so long, can anyone explain to me?
How would you go about fixing something like this?
Did I do something wrong in my parallax scrolling function getProperX/Y()? because I don't see why there is so much space between the distant tiles and the close ones.
Thanks for all help, very appreciated.
I am working on a Web UI which will be used for monitoring events, the events will popup on the UI as pictures, these picture will disappear after a certain amount of time. The UI will be opened in full screen on a wall-mounted monitor.
Now, given a number of pictures N, how can I re-size the pictures to use the screen optimally? The original picture size is 1280*720, the same ratio should be kept when scale up or down, and all the picture should be the same size.
The target screen Height and Width are variables. The number pictures N will be less than 100. Can someone suggest an effective, easy to implement algorithm?
Thanks to Sorin, here is the code I got, it works pretty well for the problem:
function resize(){
var ratio=1280/720;
var w=document.getElementById("container").clientWidth;
var h=document.getElementById("container").clientHeight;
var n=$("video").length;
var R=ratio/(w/h);
var width,height;
var column=1;
if(n>0){
while(column*Math.ceil(column*R)<n){
column++;
}
var row=Math.ceil(column*R);
height=Math.min(h/row,w/column/ratio)-12;//12 pixel margin
width=height*ratio;
$("video").height(height).width(width)
}
}
First figure out how to change the ratio. Pictures have a ratio of 1280/720 while the display has width/height. So you'll need 1 by 1280/720 / (width/height) pictures in your grid to correct the ratio (let's call this number R). Now you need a multiplier to get the number of picture you are showing to be the number of pictures you have. You need to find an M such that (M *floor(M *R) > N) since N is small a simple iteration will do.
So now you are going to show M pictures by M*R pictures in a grid.
All you need now is to scale all the pictures down. To make sure we leave as little whitespace as possible we take S as min (width / (1280 * M), height / (720 * floor(M *R))).
So scale all pictures by S and show them in a grid with M columns and floor(M*R) lines.
This all assumes that 1280 and 720 include any whitespace you may want to leave between the pictures. Otherwise you need to revise the calculations.
I'm have a heat map application and store I store the x,y coordinates of a click and also the viewport width and height. Real data for 2 clicks:
x, y, width, height
433, 343, 1257, 959
331, 823, 1257, 959
The issue is when I resize the screen on the responsive site, the displayed clicks are now all off. I'm coming up empty on my searches but is there a formula or algorithm to recalculate the x and y coordinates for different resolutions. For example, the first click, if the width goes from 1257 to 990 and the height goes from 959 to 400, how to I recalculate the x and y so they line up in the same spot?
EDIT:
I added 2 fields to the database, width_percentage and height percentage
to store the x percentage of the width and the y percentage of the height. So if x was 433 and the width of the screen was 1257 then x was 35% from the left edge of the screen. I then used the same theory for the height and ran the calculations but it did not scale the click dot to the same spot as I though the percentages would do for scaling resolutions. I testing this by clicking on full resolution 1257 width then reopening at 900 width. See below for code to display click dots at lower resolution.
Ajax PHP
while ($row = mysql_fetch_array($results)) {
if( $_GET['w'] < $row['width'] ) {
$xcorr = $row['width_percentage'] * $_GET['w'];
$ycorr = $row['y'];
}
}
This uses the $_GET variable, passing the width and height of the screen resolution on page load. Then it gets the click dots from the database as $results. Since I only scale the resolution width from 1257 to 900 I did not put in calculation for height and its the same pixel as the initial click. The new width I multiplied by the percentage and set the dot that percentage margin from the left of the screen. Since the percentage is 35%
the new x coordinate becomes 900 *.35 = 315px from the left edge. It did not work and I'm still scratching my head on head to keep click in the same spot for responsive sites.
Have you tried this mathematical formula to change the range of a number?
And also instead of storing this:
x, y, width, height
433, 343, 1257, 959
331, 823, 1257, 959
You could store it normalized between 0 and 1 so it works for any width/height (calculated by dividing each x by its width and each y by its height):
x, y
0.344, 0.357
0.263, 0.858
Then you don't need to know the width/height you used when you stored them, and when you want to translate them to the size of the current screen you just multiply each one by the current width/height
You can acheive this by jquery:
$( window ).resize(function() {
//ur code
});
javascript
window.onresize = resize;
function resize()
{
alert("resize event detected!");
}
if you are working on mobile devices use this one also
$(window).on("orientationchange",function(event){
alert("Orientation is: " + event.orientation);
});
I think you are on the right track with the percentages. Are you including the offset of the map image. I wonder if your algo is working but the visual representation appears wrong because the offset is changing in the viewport.
$(window).resize(function() {
var offset = yourMap.offset();
myLeft = offset.left();
myTop = offset.top();
});
You need to add the offsets every time to get the proper placement.
This is what you should do. Sometimes the resize event fires when the document is being parsed. It is a good idea to put the code inside an onload event function. The orientation change function is taken from #Arun answer.
window.onload = function() {
$(window).on("orientationchange", function(event) {
alert("Orientation is: " + event.orientation);
});
window.onresize = function() {
alert('window resized; recalculate');
};
};
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
for this you need to do some calculation. Here is the function which will return new x and y potion based on the height and width
function getNewX(xVlaue, oldWidth, newWidth){
return xVlaue * newWidth / oldWidth;
}
newX = getNewX(10, 150, 100); // Use
You can use the common function for height and width calc.
DEMO
The whole question highly depends on the page you want to use this on.
Most pages have a centered block and/or some self-resizing (read "responsive") elements. If the page is not very responsive, e.g. having a fixed width, you have an easier job. If the page is centered, you might want to save the cursor's X-position relative to the center of the page. this way the window width doesn't matter. The same also applies to left- and right aligned pages of course - in this case you would save the X-pos relative to the left or right edge of the window respectively.
The following image shows a center-oriented click position. Note that the x- and y properties of the click don't change here if you resize the window.
Now to the more generic methods
If you save the window dimensions, the cursor position AND the scroll offsets on every click, you will most probably be able to reproduce it alongside the layout, but you'll need to reproduce it for every unique dimensions set. If you used the trick from above you might be able to overlay all layouts and find a common denominator. For example, if your page is centered in the window, has a max-width, and you saved the X-pos relative to the center of the window, you can overlay all clicks that happened in windows that were at least that width.
You could do some trickery however, and save the clicked elements alongside the informations you already do save. If you also save the click position relative to the element, you can evaluate this data to something like "the submit button is rather pressed on the bottom right side" or "people often click on the far end of that drop-down and sometimes mis-click by a few pixels".
Try both of the following:
1. Padding and margins might not scale. Use "* {padding:0;margin:0}" at the end of your stylesheet and check if that fixes it.
2. Ensure outer and inner (that means all) elements scale. Any single element failing to scale will make many other elements fall out of place. This generally happens with text inputs. Use "*{border:solid 2}" at the end of your stylesheet to visually observe the effect of scaling on each element.
I'm sure your problem will be resolved.
I have a situation where I need to output some code that simultaneously zooms a div from 0 to it's "final" size whilst moving it down from the top of the screen.
The x and y final sizes could be different every time but my code WILL know them (the div will contain a user-supplied image so I can just read the image size).
Now, I can do the code to "zoom" the div and also move it down the screen. I know how to find the y-center of the browser window etc...
I am moving it down the screen by adding "yvalue" to the topMargin.
Here is where I am stuck and I guess it is more of a math question rather than code??
Q: How do I calculate what "yvalue" (the y position increment) should be so that the div arrives in the center of the screen at the same time (roughly) that the div zooms to maximum size. Also I need to find out what the corresponding div_xvalue and div_yvalue should be to make the "zoom" happen at the same time (if that makes sense)
Please assume the following variables :
yvalue = increment to add to div topMargin each iteration
zoom_x_final = final width of div
zoom_y_final = final height of div
browser_center_y = center position of browser window (y direction)
x = current x size of div
y = current y size of div
div_xvalue = value to add to x for increasing div size per iteration
div_yvalue = value to add to y for increasing div size per iteration
stop_y = center screen position stop for slide down calc'd from browser_center_y-(zoom_y_final/2)
I think this might help you.
I have tried to find y_increment_per_iteration and got a very simple answer:
y_increment_per_iteration
= [{(browser_width) or (working_area_width)} / 2 ] / zoom_y_final
Please note that this answer might not be correct as it is not tested.