Set bottom position of element based on rendered height of another element - javascript

Following is the url to my website
http://projectilepixels.com/beta/
I need the space shuttle to appear "naturally positioned" i.e slightly below the grass at all resolutions above 786 x 1024. However as the grass image as it's width set to 100%, the height is dynamic. Thus the bottom value for the shuttle would also be dynamic. I had initially tried using a simple css % value but that didn't help.
My current attempt uses JavaScript. Following is the code
<script>
$(document).ready(function() {
var grass = $( '#grass' );
var grassHeight = grass.outerHeight() - grass.outerHeight()/100 * 74;
$( '#shuttle_1' ).css("bottom",grassHeight);
});
</script>
The initial script just used
var grassHeight = grass.outerHeight();
however it ended up really messed up.
So, as a temp fix I added the mathematics's that calculates 74% (a solution I didn't really want to use as it uses magic numbers), it seemed to work "fine" on Mozilla at 786 x 1024, however I tested it on chrome at a slightly higher resolution (Can't remember it right now, will check the specific resolution and edit this part soon). I'm new to JavaScript and am under as to what would be the best practice to solve this problem across all browsers and resolutions
Would really appreciate if someone guided me around this problem. I'm open to using css,Javascript as well as jquery.

i would have deffently use $.position in here,
$("#Grass").position({
of: $(shuttle),
my: "top center",
at: "bottom center"
});
that will locate the the grass top line of the grass (its center), below the center of the bottom line on the shuttle

Why dont you develop a few constant values that can be applied to the actual rendered values of the grass image to adjust the placement. Something like:
Lets say the grass image is 500px x 237px but the height of the grass is only 158px – If we want the shuttle to sit right at the crown of the grass, it needs to be offset from the bottom of the screen by 158px – 158 divided by 237 works out (almost magically) to .66667
Since this is a fixed ratio, we can use .66667 to calculate the offset from the bottom of the screen for any size grass image, as long as we know the dimensions of the grass image by multiplying the height and .6667
$(function(){
var OFFSET = .66667; //this value is our shuttle adjustment constant
$( '#shuttle_1' ).css("bottom",($('#grass').height() * OFFSET)); //sets the bottom offset of the shuttle
$( '#shuttle_1' ).css("left",(($('#grass').width()/2)-($( '#shuttle_1' ).width()/2))); //centers the shuttle
});
Here is a JSfiddle demonstrating it.

Related

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

How to recalculate x,y coordinates based on screensize

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.

Maths algorithm for scaling to a percentage from variable values

Right, you will have to stick with me because I am confusing myself at the moment.
I am trying to work out the scale of an svg when I add it to a rapheal paper element.
Here's what needs to happen:
All of my SVGs are variable widths.
When I add them to my paper object I want them to be 600px wide. I need to calculate the scale ratio of the original width that will always work out at 600px, whether the original width is 2000px or 10px.
What I have so far is...
var maxWidth = 600;
var vWidth = vector.getBBox().width //this could be anything;
//Instert algorithym to work out the scale ratio
//I am not smart enough to work this part out. Please help...
//Currently this scale s set to what the original width is, but I want it to always be 600
vector.scale(1, 1);
I hope this makes sense to somebody! I am a designer who turned programmer (as you can probably tell)
Thanks!
If you divide 600 by the width of your element, it'll tell you by how much you need to scale it to make 600. I'm sure you're smart enough for the coding part :)
Examples:
Element has a width of 100px, so you need it to make it 6 times bigger. 600/100 is 6.
Element has a width of 1800px, so you need to make it 3 times smaller. 600/1800 is 1/3, so you're good too.
Try this:
var maxWidth = 600;
var vWidth = vector.getBBox().width;
var xscale=600/vWidth;
vector.scale(xscale, 1);

Calculate new width when skewing in canvas

I'm using canvas for a project and I have a number of elements that I'm skewing. I'm only skewing on the y value and just want to know what the new width of the image is after skewing (so I can align it with another canvas element). Check out the code below to see what I mean
ctx.save();
//skew the context
ctx.transform(1,0,1.3,0,0,0);
//draw two images with different heights/widths
ctx.drawImage(image,0,0,42,60);
ctx.drawImage(image,0,0,32,25);
The goal would be to know that the 42 by 60 image was now a X by 60 image so I could do some translating before drawing it at 0,0. It's easy enough to measure each image individually, but I have different skew values and heights/widths throughout the project that need to be align. Currently I use this code (works decently for images between 25 and 42 widths):
var skewModifier = imageWidth*(8/6)+(19/3);
var skewAmount = 1.3; //this is dynamic in my app
var width = (skewModifier*skewAmount)+imageWidth;
As images get wider though this formula quickly falls apart (I think it's a sloping formula not a straight value like this one). Any ideas on what canvas does for skews?
You should be able to derive it mathematically. I believe:
Math.atan(skewAmount) is the angle, in radians, that something is skewed with respect to the origin.
So 1.3 would skew the object by 0.915 radians or 52 degrees.
So here's a red unskewed object next to the same object skewed (painted green). So you have a right triangle:
We know the origin angle (0.915 rads) and we know the adjacent side length, which is 60 and 25 for your two images. (red's height).
The hypotenuse is the long side thats being skewed.
And the opposite side is the triangle bottom - how much its been skewed!
Tangent gets us opposite / adjacent if I recall, so for the first one:
tan(0.915) = opposite / 60, solving for the opposite in JavaScript code we have:
opposite = Math.tan(0.915)*60
So the bottom side of the skewed object starts about 77 pixels away from the origin. Lets check our work in the canvas:
http://jsfiddle.net/LBzUt/
Looks good to me!
The triangle in question of course is the canvas origin, that black dot I painted, and the bottom-left of the red rectangle, which is the original position that we're searching for before skewing.
That was a bit of a haphazard explanation. Any questions?
Taking Simon's fiddle example one step further, so you can simply enter the degrees:
Here's the fiddle
http://jsfiddle.net/LBzUt/33/

Positioning SVG Elements

In the course of toying with SVG for the first time (using the Raphael library), I've run into a problem positioning dynamic elements on the canvas in such a way that they're completely contained within the canvas. What I'm trying to do is randomly position n words/short phrases.
Since the text is variable, its position needs to be variable as well so what I'm doing is:
Initially creating the text at point 0,0 with no opacity.
Checking the width of the drawn text element using text.getBBox().width.
Setting a new x coordinate as Math.random() * (canvas_width - ( text_width/2 ) - pad).
Altering the x coordinate of the text to the newly set value (text.attr( 'x', x ) ).
Setting the opacity attribute of the text to 1.
I'll be the first to admit that my math acumen is limited, but this seems pretty straightforward. Somehow, I still end up with text running off beyond the right edge of my canvas. For simplicity above, I removed the bit that also sets a minimum x value by adding it to the Math.random() result. It is there, though, and I see the same problem on the leading edge of the canvas.
My understanding (such as it is), is that the Math.random() bits would generate a number between 0 and 1 which could then be multiplied by some number (in my case, the canvas width - half of the text width - some arbitrary padding) to get the outer bound. I'm dividing the width of the text in half because its position on the grid is set at its center.
I hope I've just been staring at this for too long, but is my math that rusty or am I misunderstanding something about the behavior of Math.random(), SVG, text or anything else that's under the hood of this solution?
The answer turned out to be how I was thinking about the Math.random() equation. It's not quite as simple as multiplying by the max and then adding the minimum value (of course). It's really more like establishing a double wide gutter on the right end of the container and then shifting the entire boundary to eat up half of that gutter:
var x = Math.random() * ( canvas_w - 20 - ( text.getBBox().width ) ) + ( text.getBBox().width/2 + 10 );
In English...
You have to double the width of each element you want to account for so you can shift the entire range back by that width to keep things nice and equal. In my case, I want to account for half of the width of the text plus a padding of 10.
For example...
Given a canvas width of 500, a text width of 50 and a desired "gutter" of 10, I create a random number between 0 and 430 (500 - 20 - 50). By adding back the widths I need to account for--half of the text width (25) + the padding (10)--I'm left with a random number between 35 and 465. If my text sits at the outer edges of that boundary, it can only reach as far as the 10 or 490.
Hopefully that's clear enough to make sense. Although it makes sense when I think about it, this kind of thing isn't immediately intuitive to me, so I'm sure I'll be referring back here often.

Categories