I'm trying to make a scrolling image that switches to a different 16:9 section using navigation buttons. Everything works, except jQuery seems to be limiting if(igBase.css('left') > igMaxLeft) to -1200px or greater, even though igMaxLeft is, for example, -2600px. Is there any reason for this behavior? The math looks right to me.
var igWrap = $('.infographic-wrapper');
var igBase = $('.infographic-base');
var igPrev = $('.infographic-previous');
var igNext = $('.infographic-next');
var igFrameWidth = igWrap.css('width');
var igMaxLeft = parseInt(igBase.css('width'), 10) + parseInt(igFrameWidth, 10);
igMaxLeft = '-' + igMaxLeft + 'px';
igPrev.click(function(){
if(igBase.css('left') < '0px'){
igBase.css('left','+=' + igFrameWidth);
}
});
igNext.click(function(){
if(igBase.css('left') > igMaxLeft){
igBase.css('left','-=' + igFrameWidth);
}
});
Here's the full example: https://codepen.io/arinthros/pen/YejRey
Converting the css pixel strings into integers solved the problem. Thanks user2864740 for the tip.
var igMaxLeft = (parseInt(igBase.css('width'), 10) - parseInt(igFrameWidth, 10)) * -1;
igPrev.click(function(){
if(parseInt(igBase.css('left'), 10) < 0){
igBase.css('left','+=' + igFrameWidth);
}
});
igNext.click(function(){
if( 0 >= parseInt(igBase.css('left'), 10) && parseInt(igBase.css('left'), 10) > igMaxLeft){
igBase.css('left','-=' + igFrameWidth);
}
});
Related
What is the right way to increment an array in a loop? I'm passing in a non zero based number and want to get the right starting position for the Array.
I can get it to work using eval:
arrayPosition = "articleArray[" + (goTo - 1) + "][0]";
window.scroll(eval(arrayPosition), 0);
But I know this isn't proper coding and now that I'm using //"use strict"; it will no longer run.
I've tried variations of:
var x = arrayPosition[goTo - 1][0];
window.scroll(x,0);
But these don't work.
function scrollToArticle(referrer, goTo, showSection) {
// referrer, goTo are required
var showSection = (typeof showSection) !== "undefined" ? showSection : false;
var tmpNum = Math.round(articleArray.length / 2);
if (goTo !== tmpNum && goTo !== articleArray.length) {
arrayPosition = "articleArray[" + (goTo - 1) + "][0]";
window.scroll(eval(arrayPosition), 0);
} else if (goTo === tmpNum) {
window.scrollTo(mainContentCenterScrollTo - (window.innerWidth / 2), 0);
} else {
arrayPosition = "(articleArray[" + (goTo - 1) + "][0]) + (articleArray[" + (goTo - 1) + "][1])";
window.scrollTo(eval(arrayPosition), 0);
}
}
There should be no need for eval, just a reference to the array:
var arrayPosition = articleArray[goTo - 1][0];
window.scroll(arrayPosition, 0);
Sorry to all it was my on fault as was noted above the value being past into to array was a string and not a number which is why eval worked and [i-1] didn't.
I recently tried to work on a jQuery-Plugin which is used to spread an amount of containers randomly in a container to afterwards be able to animate them in a special way. You can see my attempts here: http://www.manuelmaurer.at/randposplugin.php
The Problem is, that some of them are overlapping and I can't figure out why. At first I thought that the reason for this might be that $.each() is not waiting for one loop to finish before starting the next one but I also tried to solve that using recursive functions - didn't help. I hope someone could give me a little push to figure out where the problem actually is, thanks in advance! You can see the code either on the page itself or just have a look at the following important parts.
This code is used to loop over all the elements. This is not further important, but the function "NoCollision" is trying to figure out, if an element exists in that area. If yes, it returns false, if the space can be used, it returns true. If the space can not be used, some random other coordinates are chosen and it will be tried again.
var Counter2 = 0;
$(FlowContainer).children(':not(:last)').each(function(elem) {
Counter2++;
ElemNow = $(FlowContainer).children().eq(Counter2);
ElemWidth = $(ElemNow).data("animwidth")
ElemHeight = $(ElemNow).data("animheight")
var Tries = 0;
var TryNowX = ElemPrevLeft;
var TryNowY = ElemPrevTop;
while (!NoCollision(TryNowX, TryNowY, ElemWidth, ElemHeight, PositionsArray, Settings.MinSpreadX, Settings.MinSpreadY) && Tries <= Settings.MaxTries) {
if (TryNowY < 15) {
TryNowY += randomIntFromInterval(0, 10);
} else if (TryNowY > (FlowContainer.height() - ElemHeight - 15)) {
TryNowY += randomIntFromInterval(-10, 0);
} else {
TryNowY += randomIntFromInterval(-10, 10);
}
if (TryNowX < 15) {
TryNowX += randomIntFromInterval(0, 10);
} else if (TryNowX > (FlowContainer.width() - ElemWidth - 15)) {
TryNowX += randomIntFromInterval(-10, 0);
} else {
TryNowX += randomIntFromInterval(-10, 10);
}
Tries++;
}
if (Tries == Settings.MaxTries) {
console.log("Warning: Couldn't fit all elements - hiding some.")
$(ElemNow).remove();
} else {
$(ElemNow).css({ top: TryNowY, left: TryNowX });
ElemPrevLeft = TryNowX;
ElemPrevTop = TryNowY;
PositionArray = [TryNowY, TryNowX, ElemHeight, ElemWidth];
PositionsArray[Counter2] = PositionArray;
}
})
The actual check, if the space can be used, takes part in the NoCollision-Function, which you can see in the following code.
function NoCollision(X, Y, W, H, PositionsArray, SpreadX, SpreadY) {
var NoErrors = true;
//Jedes Element im PositionsArray durchgehen und Prüfen
$.each(PositionsArray, function(PositionArray) {
var ArrY = PositionsArray[PositionArray][0];
var ArrX = PositionsArray[PositionArray][1];
var ArrW = PositionsArray[PositionArray][3];
var ArrH = PositionsArray[PositionArray][2];
if ((X < (ArrX - W - SpreadX) || X > (ArrX + ArrW + SpreadX)) && (Y < (ArrY - H - SpreadY) || Y > (ArrY + ArrH + SpreadY))) {
//SHOULD BE OKAY HERE
} else {
NoErrors = false;
}
})
return NoErrors;
}
The array which I am using to save the coordinates of all the already positioned divs looks like this.
PositionsArray[
[Elem1PositionY, Elem1PositionX, Elem1Height, Elem1Width]
[Elem2PositionY, Elem2PositionX, Elem2Height, Elem2Width]
]
My thought was to do it like this. Is there something wrong with the way I'd do it or is there a mistake in my implementation?
I am absolutely thankful for every bit of help! Thanks in advance!
I finally solved the issue - my calculation of the overlapping was a bit wrong, just had to change the && (and) to || (or) - works like charm now.
Found a better solution using a plugin which was referenced on another stackoverflow-thread - works way better now.
So I've been working on re-producing the slider found here https://www.skylight.io/ ( Scroll down to find the price slider ).
So far Ive managed to create something similiar, but some numbers are hard coded, making it difficult to change and not very re-usable.
I've been researching around and I think I need to use Math.log() and Math.exp() together to achieve something like in the link above but I'm not sure.
Heres a jsfiddle of what I have so far https://jsfiddle.net/7wrvpb34/.
I feel that its the maths part of this problem that is halting me I think, so any help would be greatly appreciated.
Javascript code below:
var slider = document.getElementById("slider")
var sliderFill = document.getElementById("slider-fill")
var knob = document.getElementById("knob")
var mouseDown;
var mousePos = {x:0};
var knobPosition;
var minPrice = 20;
var price = 0;
var minRequests = 50;
var requests = 50 + ",000";
var incrementSpeed = 2;
var incrementModifier = 20;
var incrementValue = 1;
var minMillionCount = 1;
var millionCount = 1;
var previousRequestAmount = 0;
document.getElementById("price").innerHTML = price;
document.getElementById("requests").innerHTML = requests;
highlightTable(1);
document.addEventListener('mousemove', function(e) {
if(mouseDown) {
updateSlider(e);
}
})
function updateSlider(event) {
mousePos.x = event.clientX - slider.getBoundingClientRect().left;
mousePos.x -= knob.offsetWidth / 2;
console.log(mousePos.x);
if(mousePos.x < 0) {
knob.style.left = "0px";
sliderFill.style.width = "0px";
price = 0;
requests = 50 + ",000";
document.getElementById("price").innerHTML = price;
document.getElementById("requests").innerHTML = requests;
return
}
if(mousePos.x > slider.offsetWidth - 20) {
return
}
sliderFill.style.width = mousePos.x + 10 + "px";
knob.style.left = mousePos.x + "px";
//Increase requests by using X position of mouse
incrementSpeed = mousePos.x / incrementModifier;
requests = minRequests + (mousePos.x * incrementSpeed);
//Round to nearest 1
requests = Math.round(requests / incrementValue) * incrementValue;
if (requests >= 1000){
var m = requests/ 1000;
m = Math.round(m / 1) * 1;
//Problem, lower the modifier depending on requests
incrementModifier = 20 * 0.95;
document.getElementById("requests").innerHTML = m + " million";
//Adjust Prices
if(( requests >= 1000) && (requests < 10000)) {
var numOfMillions = requests / 100;
//Round to closest 10.
//10 * number of millions
var rounded = Math.round(numOfMillions / 10) * 10;
price = minPrice + rounded;
highlightTable(3);
}
//Adjust Prices
if(requests >= 10000) {
var numOfMillions = requests / 1000;
var rounded = Math.round(numOfMillions / 1) * 1;
var basePrice = minPrice * 6;
price = basePrice + rounded;
highlightTable(4);
}
} else {
incrementModifier = 20;
document.getElementById("requests").innerHTML = requests + ",000"
if(requests < 100) {
highlightTable(1);
price = 0;
} else {
highlightTable(2);
price = 20;
}
}
previousRequestAmount = requests;
document.getElementById("price").innerHTML = price;
}
knob.addEventListener('mousedown', function() {
mouseDown = true;
});
document.addEventListener('mouseup', function() {
mouseDown = false;
});
function highlightTable(rowNum) {
var table = document.getElementById("payment-table")
for(var i = 0; i < table.rows.length; ++i) {
var row = table.rows[i]
if(i == rowNum) {
row.style.background = "grey"
} else {
row.style.background = "white";
}
}
}
Thank you for your time.
If you want it to be reusable you need to create a mathematical function that assigns a result to the number of requests. I will give you a very easy example.
If you want a different result for 1,10,100,100,10000 etc
var d = Math.log10(requests);
if(d<1){
doSomething();
}else if(d<2){
doSomethingElse();
} //etc
This way if you want to change the specific values that create certain results, all you need to do is change the function.
This only works if your tiers of requests follow a math function, if they don't you need to hard code it.
However if say they don't follow a math function, but you know how you would like them to change based on a value then you can do this.
var changingValue = 4;
if(requests < 400*changingValue){
doSomthing();
}else if(requests <= 400*changingValue*changingValue){
doSomethingElse();
}else{// the requests is greater than any of the above
doTheOtherThing();
}
Edit:
For the second one you need to make sure that each condition if always larger than the other from top to bottom.
The description "increasingly increasing" matches an arbitrary number of functions. I assume you also want it to be continuous, since you already have a non-continuous solution.
TL;DR
Use an exponential function.
Generic approach
Assuming imin and imax are the minimal and maximal values of the slider (i for input) and omin and omax are the minimal and maximal values to be displayed, the simplest thing I can think of would be a multiplication by something based on the input value:
f(x)
{
return omin + (omax - omin) * g((x - imin) / (imax - imin));
}
This will pass 0 to g if x == imin and 1 if x == imax.
The return value r of g(y) should be
r == 0 for y == 0
r == 1 for y == 1
0 < r < y for 0 < y < 1
The simplest function that I can think of that fulfills this is an exponential function with exponent > 1.
An exponent of 1 would be a linear function.
An exponent of 2 would be make the middle of the slider display one fourth of the maximum price instead of half of it.
But you really need to find that exponent yourself, based on your needs.
I'm trying to detect if two given div's are too close or collide/overlap .
I have the below codepen which tries to generate 20 random div's and only append them to body if their position isn't too close to other existing div.
That's the idea but it doesn't work as expected where i get div's that get through with close/overlapping positions to existing divs. (run it multiple times if first time is perfect and you should come across it).
http://codepen.io/anon/pen/fHLzj
Can anyone see the mistake and way to make it work?
This is somewhat hard to explain and get..but here goes:
check every div against every div by running for loop.
x,y,h,w
x is top-left corner's distance from left.
y is top-left corner's distance from top.
h is div's height.
w is div's width.
Point to consider... you don't really need to check every div..consider this
there are 10 divs...
First you will check 1st against 9.
Second one against 8.
.............
Eight one against 2.
Ninth one against 1.
And don't the last one.
Also it's a good idea to assign values and check for collisions in data, before assigning them to dom. Dom should be just for rendering final result.
I'll assume you want to keep none of the two colliding divs.
Preview
http://jsfiddle.net/techsin/m4fSf/6/
as expected code is huge
var
div={},
number=10,
size=20,
m = ele('main');
mw= parseFloat(getComputedStyle(m).getPropertyValue("width"))-size,
mh= parseFloat(getComputedStyle(m).getPropertyValue("height"))-size,
f=true,
nn;
var i
for (i = 0; i < number; i++) {
div[i] = {};
var t = true, newX, newY, nn;
if (i!=0){
while (t) {
newX = rand(mw);
newY = rand(mh);
for (nn = 0; nn < i; nn++) {
if (!(((newX > div[nn].x + size+5) || (newY > div[nn].y + size+5)) ||
((newX + size+5 < div[nn].x) || (newY + size+5 < div[nn].y)))) {
break;
}
if (nn == i-1) t = false;
}}} else {
newX = rand(mw);
newY = rand(mh);
}
console.log(newX);
div[i].x = newX;
div[i].y = newY;
}
for (i = 0; i < number; i++) {
render(div[i]);
}
console.log(div);
function render(x){
var d=document.createElement('div');
d.style.position='absolute';
d.style.left=(x.x+'px');
d.style.top=(x.y+'px');
m.appendChild(d);
}
function rand(x) { return Math.random()*x;}
function ele(x){return document.getElementById(x);}
this code is from my collision site...ill try and put it in the code above, but this what's needed to avoid collisions and close gaps.
if (xpost+30>xx.left && xx.left>xpost && xx.top+30>ypost && xx.top<ypost+30) { xspeed = -speed; }
if (xpost<xx.left+30 && xx.left<xpost && xx.top+30>ypost && xx.top<ypost+30) { xspeed = speed; }
if (ypost+30>xx.top && xx.top>ypost && xx.left+30>xpost && xx.left<xpost+30) { yspeed = -speed; }
if (ypost<xx.top+30 && xx.top<ypost && xx.left+30>xpost && xx.left<xpost+30) { yspeed = speed; }
How about using one of these libraries to detect the collisions for you?
http://sourceforge.net/projects/jquerycollision/
http://gamequeryjs.com/
I changed the collision logic. It detects if an object is close to another object by comparing the distance between the objects. I wrapped the logic in a do-while loop as well, so that it will keep attempting to find a position to place the square and you'll have exactly 20 squares.
This works:
var positions = []; //stroe positions of appended divs
var divsize = 20;
var topGap = 40; // gap from top
var leftGap = 80; //gap from left
function generateRandomPositionedDiv(){
for(var c = 0; c < 20; c++){
var color = '#'+ Math.round(0xffffff * Math.random()).toString(16);
$newdiv = $('<div/>').css({
'width':divsize+'px',
'height':divsize+'px',
'background-color': color
});
var posLeft;
var posTop;
var checkObj;
var collide = false;
posLeft = Math.floor((Math.random() * ($(document).width() - divsize)));//.toFixed();
posTop = Math.floor((Math.random() * ($(document).height() - divsize)));//.toFixed();
checkObj = {x: posLeft, y: posTop};
collide = checkForCollisions(checkObj);
if(!collide) {
positions.push({x: posLeft, y: posTop});
$newdiv.css({
'position':'absolute',
'left':posLeft+'px',
'top':posTop+'px'
});
$('body').append($newdiv);
}
}
}
/*function getPositions(box) {
var $box = $(box);
var pos = $box.position();
var width = $box.width();
var height = $box.height();
return [ [ pos.left, pos.left + width + leftGap ], [ pos.top, pos.top + height + topGap ] ];
}*/
function comparePositions(obj1, obj2) {
if(Math.abs(obj1.x - obj2.x) <= (divsize + leftGap) && Math.abs(obj1.y - obj2.y) <= (divsize + topGap)) {
return true;
} else {
return false;
}
}
function checkForCollisions(posObj){
for(var i = 0; i < positions.length; i++){
var match = comparePositions(positions[i], posObj);
if (match) {
//return true if two positions are close or overlapping
return match;
}
}
}
generateRandomPositionedDiv();
I have a button and onmouseover I want it to move right 100ish pixels at 10 pixels a move then stop. The moving isn't a problem its the stopping. I can do this no problem with jquery but I need to learn how to do it from scratch in javascript. this is my script to move right so far.
function rollRight() {
imgThumb.style.left = parseInt (imgThumb.style.left) + 10 + 'px';
animate = setTimeout(rollRight,20);
}
That moves it right just fine so to stop it i tried taking the amount of loops 5x10=50px and wrote it again as
function rollRight() {
var i=0;
while (i < 5) {
imgThumb.style.left = parseInt (imgThumb.style.left) + 10 + 'px';
animate = setTimeout(rollRight,20);
i++;
};
}
Now, I think I'm missing a piece to make it return the [] values for the while function, but I'm not sure how to make it work. Once I have the move right I can apply the same principle to move it back onmouseout.
If anyone can help me fix this that would be great. If you have a better script to do the animation that is just javascript, no libraries, that would be great too.
EDIT: Because leaving it as a comment didn't work well this is my current code
function rollRight() {
var left = parseInt (imgThumb.style.left);
if(left < 50) { // or any other value
imgThumb.style.left = left + 10 + 'px';
animate = setTimeout(rollRight,20);
}
}
function revert() {
var left = parseInt (imgThumb.style.left);
if(left < 50) { // or any other value
imgThumb.style.left = left + -10 + 'px';
animate = setTimeout(rollRight,20);
}
}
In the revert I'm having a problem getting it to move back. It's probably in the if(left<50) part.
var animate = null;
function rollRight() {
if(animate) clearTimeout(animate);
var left = parseInt (imgThumb.style.left);
if(left < 50) { // or any other value
imgThumb.style.left = left + 10 + 'px';
animate = setTimeout(rollRight,20);
}
}
function revert() {
if(animate) clearTimeout(animate);
var left = parseInt (imgThumb.style.left);
if(left > 0) {
imgThumb.style.left = (left - 10) + 'px';
animate = setTimeout(revert,20);
}
}
If you want to stick with setTimeout, you were close on your first one. I moved some hard coded numbers into variables:
var totalPixels = 100;
var increment = 10;
var frameTime = 20;
var numFrames = totalPixels / increment;
var curFrame = 0;
function rollRight() {
imgThumb.style.left = parseInt (imgThumb.style.left) + increment + 'px';
if(curFrame++ < numFrames) {
animate = setTimeout(rollRight,frameTime);
}
}
You could also switch to use setInterval and then every time the interval fires, decide if you should stop the interval based on some incrementing value.
Try this
var i = 0;
var moveInterval = setInterval(function(){
if(i>=5) clearInterval(moveInterval);
rollRight();
i++;
},20);
function rollRight() {
imgThumb.style.left = parseInt (imgThumb.style.left) + 10 + 'px';
}
Once it gets to 5, it will clear the interval out, and be done.