Lodash find with a range instead of exact match of numbers - javascript

I'm implementing a system in react-native where I use the onScroll handler to detect the Y offset from the top of the viewport. When the current scroll position equals a number stored in a separate object, it has to be detected.
This is my code now:
onActivitiesScroll = (event) => {
let positionYTrack = _.find(
this.state.trackHeaderCardPositionsY,
{ positionY: Math.floor(event.nativeEvent.contentOffset.y) }
);
console.log(positionYTrack);
}
The problem is this part: { positionY: Math.floor(event.nativeEvent.contentOffset.y) }
The event.nativeEvent.contentOffset.y is an exact number (floored) and positionY is a property in a state object. The problem is that when I scroll, it skips many scroll positions because of the limited number of frames the onScroll handler handles.
What this code is doing right now is:
if Math.floor(event.nativeEvent.contentOffset.y) exactly equals one of the positionY property values in the state, it should log it. I want to do it like this:
if Math.floor(event.nativeEvent.contentOffset.y) is within the range of current position - 10 and current position + 10, it should detect it.
Any idea how this can be achieved? I just can't seem to find a solution for this, even after sleeping over it.

_.find also accepts a function to compare values. Here's a simple example you can use to find if y is in range:
onActivitiesScroll = (event) => {
let positionYTrack = _.find(
this.state.trackHeaderCardPositionsY,
position => {
const y = Math.floor(event.nativeEvent.contentOffset.y);
const min = y - 10;
const max = y + 10;
return min >= position <= max;
}
);
}

Related

how to discriminate two numbers that are very near?

So my case goes like this, my software that is being develop on JavaScript needs to manipulate exact numeric values but sometimes it can happen that the values are way too near and I need to discriminate.
This is a case example:
0:(2) [112.02598008561951, 9.12963236661007]
1:(2) [112.02598008561952, 9.129632366610064]
2:(2) [9.751846481442218, 3.5376744911193576]
In this array position 0 and 1 has similar values but with an slight difference at the end of the decimals, but the one that counts is the position 0, because the two numbers are way near it messes with the process that follows next.
So, how do I do to discriminate near numbers and just use the first of the similar numbers given?
In the end the end result would be an array like this:
0:(2) [112.02598008561951, 9.12963236661007]
1:(2) [9.751846481442218, 3.5376744911193576]
I tried doing a truncation but I need the whole number to work with.
Edit: as one of the comments asked about if the points can vary or not, in my real problem I get a series of numbers that I sort normally I get like 3 points or best case scenario I get 2 points.
Sometimes this problem happens when I get near numbers and the first layer of sorting doesn't work as intended and the next part doesn't work well.
In short, you need to consider that it is always like 3 positions of coordinates.
In short your easiest option is to round to a fixed number of decimal places. This is because floats in JS (and in computer science in general) can be a tricky thing. For example, this should make you want to throw your computer:
var x = 0.1 * 0.2; //-> 0.020000000000000004
There are different use cases for needing super exact precision (eg. when dealing with money, trajectory of a satellite, etc), but most use cases only need "good enough" precision. In your case, it's best to round all of your numbers to a fixed decimal length so that you don't encounter the low-level inaccuracies.
var ACCURACY = 100000000;
var round= (num) => Math.round(num * ACCURACY) / ACCURACY;
var x = round(0.1 * 0.2); //-> 0.2
If you trust the numbers you have and you're just needing to filter out a pair which is close to another pair, you will need to write a little function to apply your heuristics.
var areClose = (x, y) => Math.abs(x - y) < 0.0000000001;
var filterPoints = (arr) => {
return arr.filter(([x, y], i) => {
for(var n = i - 1; n >= 0; n--) {
if (areClose(x, arr[n][0]) && areClose(y, arr[n][1])) {
return false;
}
}
return true;
});
}
filterPoints([
[112.02598008561951, 9.12963236661007],
[112.02598008561952, 9.129632366610064],
[9.751846481442218, 3.5376744911193576],
]);
// [
// [112.02598008561951, 9.12963236661007],
// [9.751846481442218, 3.5376744911193576]]
// ]
Note: this will keep the "first" set of values. If you wish to keep the "last" set, then you can flip the inner loop to crawl upwards:
for(var n = i + 1; n < arr.length; n++) { ...
Let's see if I understood correctly, you have this array with vertex points, usually it's just a 2 elements bidimensional array, but sometimes it might receive an extra vertex points array, with a slight different value (differ of 1*10^-14) and you want to discard the higher extra values.
I came up with something like this:
const arr = [
[112.02598008561951, 9.12963236661007],
[112.02598008561952, 9.129632366610064],
[9.751846481442218, 3.5376744911193576],
];
for (let i = 0; i < arr.length-1; i++) {
const diff = Math.abs(arr[i][0] - arr[i + 1][0])
if (diff <= 0.00000000000002) arr.splice(i + 1, 1);
}
console.log("NEW ARR", arr)
This just checks the first element of the array, since if I understood correctly it automatically means even the second element differs of a similar amount.
I'm using a (2*10-14) threshold since 1 is not enough, not sure if it's due to JS issues with float precision.
You could sort and reduce
let arr = [
[112.02598008561952, 9.129632366610064],
[112.02598008561951, 9.12963236661007],
[9.751846481442218, 3.5376744911193576]
]
arr.sort((a,b) => a[0]-b[0]); // swap a and b for descending
const precision = 0.00000000000002;
arr = arr.reduce((acc,cur,i) => {
if (i===0) { acc.push(cur); return acc}
const diff = Math.abs(acc[acc.length-1][0]-cur[0])
if (diff > precision) acc.push(cur)
return acc
},[])
console.log(arr)
What about something like
const removeDecimalPlaces = (num) => Math.floor(num * 10000000000000) / 10000000000000;
console.log(removeDecimalPlaces(112.02598008561951) === removeDecimalPlaces(112.02598008561952))

How do I generate a random X value for each "projectile" in my falling objects game using Javascript?

I am coding a game that is currently in its very early stages for a project to try to learn more about coding. In my game, objects generate randomly (green squares), and the player (red square), avoids them. I am having trouble trying to get the green squares to generate from a random position on the x-axis. I already have a formula to generate a random number for X, but after it selects a number randomly, all the "projectiles" generate there, rather than all generating from a different area. How would I get all the "projectiles" to generate from different positions on the x-axis randomly?
var randomX = Math.floor(Math.random() * 480) + 15;
function updateGameArea() {
var x, y;
for (i = 0; i < projectiles.length; i += 1) {
if (player.crashWith(projectiles[i])) {
gameArea.stop();
return;
}
}
gameArea.clear();
gameArea.frameNo += 1;
if (gameArea.frameNo == 1 || everyinterval(150)) {
x = randomX;
y = gameArea.canvas.height;
projectiles.push(new component(40, 40, "green", x, y));
}
for (i = 0; i < projectiles.length; i += 1) {
projectiles[i].y += -1; // the shape is using its coordinates to build downward from its y position
projectiles[i].update();
}
player.newPos();
player.update();
}
function everyinterval(n) {
if ((gameArea.frameNo / n) % 1 == 0) {return true;}
return false;
Expected: Green squares generate in random positions on the x- axis every 3 seconds and move upwards
Actual: Green squares all generate from the same random position on the X-axis.
You should reset X every time you're adding a new projectile:
if (gameArea.frameNo == 1 || everyinterval(150)) {
randomX = Math.floor(Math.random() * 480) + 15;
x = randomX;
y = gameArea.canvas.height;
projectiles.push(new component(40, 40, "green", x, y));
}
Otherwise, the randomX value stays constant as the value originally evaluated on line 1 when the interpreter reached it.
Here's your problem:
var randomX = Math.floor(Math.random() * 480) + 15;
// Generates a random number and stores it to randomX
// Called using 'randomX'
You need to turn it into a function if you want it to run each time:
var randomX = function() { Math.floor(Math.random() * 480) + 15 };
// Returns a new number each time
// Called using 'randomX()'
Both shivashriganesh mahato and natelouisdev have, essentially responded to how to fix the issue but since you are learning coding here is a tip. When you code, the code will run in a particular order. If you want something to be reassigned repeatedly, in this case a randomized number being used, and you want it to occur only after an event, you need to make sure that it gets trigger within each event.
natelouisdev has a good approach because, by using it as a function, you can call your randomizer more cleanly in your code and make it reassign the value of x each time.
Since you are building a game, it is also a good idea to compartmentalize your code. It'll make it easier to keep your ideas in order for each event trigger.
Example:
function gameLoss(){} - Define event return upon game loss. You can
then create editable rules to reason for loss without having to edit
the loss
function gameActive(){} - Defines what is normal gameplay. everything that occurs during normal gameplay should be managed here.
function gameArea(){} - Defines game canvas that function more for UI than for gameplay (scores, lifes, size of screen, etc)
Had you created individual functions you'd realize you only need a randomized 'x' value upon regular play thus you'd assign it within the gameActive() function and not as a global variable. Then you'd call the gameActive() function as many times as needed within a time interval to ensure a unique value is created each time.
-Side note: Don't litter unnecessary global variables. It'll make a mess off of your code when debugging. -

requestAnimationFrame javascript alert increment jumps from 1 to 9 or 13

I've got this weird problem, I'm incrementing by 1, and yet, the increment that appears when the javascript window pops up, shows that I have incremented either by 9 or 13, the either comes from whether I am incrementing by 1 or -1 respectively. what is up with that?
This the function being called by the requestAnimationFrame
function stream1() {
if (y > origin_y){
var xOffset = -1;
} else if (y == origin_y){
var xOffset = 1;
} else {
var xOffset = 1;
}
var offset = $( "#widget1" ).offset();
var x = offset.left;
var y = offset.top;
console.log(' X - '+x+' Y - '+y);
$( "#widget1" ).offset({ top: y-yOffset, left: xInitial+xOffset });
}
This is the animation frame
var globalID;
function repeatOften() {
stream1();
requestAnimationFrame(repeatOften);
}
It probably doesn't make sense that in the time for the alert to disappear and reappear, 9 iterations have been complete right? It's supposed to be 60 times a second supposedly and it has been like 1 second so shouldn't it be 60 and not 9 or 13? I don't know where these arbitrary numbers come from.
To summarize again, initially xInitial is located at 1114 px, then it goes to 1105 or 1103 and then 9 or 13 gaps subsequentially every time so why is that?
First, avoid expensive operation in frame callback. Like $( "#widget1" ).offset(). Mind that it is DOM operation, it can be slow and broke all timing. You can get DOM id and offset before animation starts and then remember just current offset left and top.
Second, if you want to be super precise, you can use handler urgument, which is timestamp and if you store animation start timestamp you can compute exact position regardless of real frame ratio.

Using JavaScript to increment top/left/bottom/right values

I am trying to increment the position of an element by, say, x pixels. Here is what I've tried so far:
var top = document.getElementById("something").style.top;
top = top + "300px"
I know that this is not going to work, but I was wondering if it was possible to increment a position value like this.
Because style.top is a string with units on the end of it like "300px" you can only do math with it when you convert just the numeric part to an actual number.
Assuming you have a positioned element (so setting the top value will do something) and you already have a top style set directly on the element and not set via CSS (so getting obj.style.top will actually get you something), you can do it by parsing the number out of the style value like this:
var obj = document.getElementById("something");
var topVal = parseInt(obj.style.top, 10);
obj.style.top = (topVal + 300) + "px";
Working example: http://jsfiddle.net/jfriend00/pt46X/
That won't work fine because, for example, if top had a value of 200px, it would become "200px300px". Try this:
var elem = document.getElementById("something");
elem.style.top = parseInt(elem.style.top, 10) + 300 + "px"
Demo WEEEE!!!!
let top = 0;
let left = 0;
let text = document.getElementById("TextToTranslate");
text.setAttribute("style","top:"+top+"px; "+left+":px;");
use this in a while loop and it works fine, i'm just figuring out how to slow it down so i can see the transition

Javascript style.left is empty string

next.onclick = function() {
move('left', li_items[0]);
};
var move = function(direction, el) {
pos = el.style[direction].split('px')[0];
pos = parseInt(pos, 10) + 10;
el.style[direction] = pos + 'px';
};
I'm using the simple code above to try and move an element. Now when I breakpoint on this, the value of el.style[direction] is: " ". So then when i try to do anything with it, it breaks. Why would this be? Isn't style.left supposed to return an integer?
Why would this be?
Presumably because it hasn't been set to anything.
Isn't style.left supposed to return an integer?
No. It is supposed to return a string containing the value of the CSS left property as set directly on the element (either by setting the JS property itself or by using a style attribute). It does not get a value from the cascade and it should only be an integer if the value is 0 (since all other lengths require units).
See How to get computed style of a HTMLElement if you want to get the computed value for the property rather than what I described in the previous paragraph.
style provides the original style as calculated from the CSS, not the updated and possibly dynamic style. You probably want currentStyle instead.
next.onclick = function() {
move('left', li_items[0]);
};
var move = function(direction, el) {
var lft = document.defaultView.getComputedStyle(el)[direction];
pos = parseFloat(lft);
pos = parseInt(pos, 10) + 10;
el.style[direction] = pos + 'px';
};
Note: like Elliot said you'll have to get the currentStyle/computedStyle. Here's a way to make it cross-browser, however when applying styles via JS, this is one good case where some sort of framework (eg Prototype [Scriptaculous], jQuery) would be useful.
Just a comment.
In your code:
> pos = el.style[direction].split('px')[0];
> pos = parseInt(pos, 10) + 10;
The split in the first line is superfluous, in the second line parseInt will convert (say) 10px to the number 10 just as effectively (and more efficiently) than what you have.
pos = parseInt(el.style[direction], 10);

Categories

Resources