Setting setInterval function is running and crashing the browser. - javascript

I have a function which is almost working.
It finds an element with the class of .hex and then runs a random number between 5 and 13 and then appends those .hex items to the body of the document.
Then I have a delay and the hexagons are faded in and out slowly.
What I want to do is to create this on a loop so they constantly fade in and out.
I have commented out the code where this should work to my understanding, but currently doesn't.
All I did was to wrap all my code in a new function called initHex() and then after the code I use setInterval to run the function every 12 seconds (at the moment).
I can see in the console the function runs, and then starts looping very fast, the numbers incrementing dramatically, and quite quickly it stops the browser responding.
I am wondering if the function is wrapped around too much of the code.
The idea is that hopefully the current function is still running (fading out hexagons) as new ones are fading in.
I am assuming at the end of the function I will also require something to remove the existing hex elements so they don't keep getting added to the document. I did try this.
Here is the function:
//function initHex() {
$rndNum = Math.floor(Math.random() * 8) + 5;
var e = $('.hex');
for (var i = 0; i < $rndNum ; i++) {
$docHeight = $(window).height();
$docHeight = Math.random() * $docHeight * 2;
$docWidth = $(window).width();
$docWidth = Math.random() * $docWidth;
$rndOpacity = Math.random();
$rndSpeed = Math.floor(Math.random() * 2000) + 2000;
e.each(function(){
$(this).css({
position: 'absolute',
top: $docHeight,
left: $docWidth - 195,
opacity: $rndOpacity
});
e.clone().prependTo('body').delay(e.length*800).fadeIn($rndSpeed).delay(1000).fadeOut($rndSpeed*2);
console.log($rndNum, $rndOpacity, $rndSpeed);
});
}
//}
//setInterval(initHex, 12000);
In terms of removing the hexagons once they have run I tried:
e.clone().prependTo('body').delay(e.length*800).fadeIn($rndSpeed).delay(1000).fadeOut($rndSpeed*2.5, function(){
e.remove();
});
Here is the fiddle: http://jsfiddle.net/lharby/j5bSz/
Fork it to your hearts content.
TIA

I have reworked your code. You made a few mistakes and yes you have tried to clone the whole bunch of elements with class hex each time, but the array have no method clone.
Try this:
function initHex() {
var $rndNum = Math.floor(Math.random() * 8) + 5;
for (var i = 0; i < $rndNum ; i++) {
var $docHeight = $(window).height()
, $top = Math.random() * $docHeight * 2
, $docWidth = $(window).width()
, $left = Math.random() * $docWidth - 195
, $rndOpacity = Math.random()
, $rndSpeed = Math.floor(Math.random() * 2000) + 2000;
$('<div></div>').addClass('hex').css({
position: 'absolute',
top: $top,
left: $left,
opacity: $rndOpacity
}).prependTo('body').delay(1000).fadeIn($rndSpeed).delay(1000).fadeOut($rndSpeed*2, function(){
this.remove();
});
}
}
setInterval(initHex, 12000);
One more thing, this code is still not optimized, but I have tried to save your code as much as possible to be more clear for you.
http://jsfiddle.net/andbas/DZSJT/1/

Related

Moving div elements stored in array

var move = function() {
Xpos = Math.round(Math.random() * 95);
food[num].css('left', Xpos + '%');
interval = setInterval(function() {
console.log(i);
var posTop = food[num].offset().top / $(window).height() * 100;
while(posTop < 80) {
if(posTop === 80) {
num++;
break;
}
posTop += 1;
food[num].css('top', posTop + '%');
break;
}
}, speed);
}
//Color the circle
circleColor();
move();
}
OK so this is my code. It creates a circle(a <div> element) on top of the screen and gives it a random x coordinate. Than it puts it in food[] array. The entire code starts by clicking the button, but every time I press the button again the circle that was last moving stops, function creates a new one and the new one moves. Is there a way I can make all elements in the array move without depending on each other?
http://jsfiddle.net/yqwjqx31/
I understand why this happens but I have no idea how to fix it.
First you're using a global variable num in setInterval function handler, so its value get modified while using it in new cercle create, secondly you're clearing interval of the last cercle you created before creating a new cercle. It means you're sharing the same interval between all cercles. Use instead an array of intervals just like var food =[] and use a temporary variable to prevent the index value modification inside your setInterval handler. Here's a working fiddle
//Interval
var interval =[];
var tempNum = num;
interval[num] = setInterval(function() {
var posTop = food[tempNum].offset().top / $(window).height() * 100;
while(posTop < 80) {
if(posTop === 80) {
break;
}
posTop += 1;
food[tempNum].css('top', posTop + '%');
break;
}
}, speed);
Increment your num variable
//Color the circle
circleColor();
move();
num++;
Your move function is only responsible for moving the last generated div via createCircle. If you want to move them all till collision, you need to loop through them all and move them accordingly. Or, you can animate them via CSS.
Here is the working version :
http://jsfiddle.net/yqwjqx31/3/
Notice how the setInterval callback loops through all the div and pushes them down until their height is 80.
var move = function () {
interval = setInterval(function () {
for (var i = 0; i < food.length; ++i) {
var posTop = food[i].offset().top / $(window).height() * 100;
if (posTop < 80) posTop += 1;
food[i].css('top', posTop + '%');
}
}, speed);
}

DIVs to randomly fadeIn on page

Okay, I've seen a few things that sort of * answer my question, but none of them quite do what I want to do / I'd like to understand how to do this myself from start to finish as a learning exercise. I'm a novice at all this, so bear with me!
What I'm Trying to Do:
I have a black page and I'd like 20-30 small, white div boxes to fadeIn at random positions on the page (like stars is sort of the vibe I'm going for).
Ideally, they wouldn't overlap and they would be randomly sized between 5px and 10px, but I recognize that this might be getting a little too complicated.
Here's what I have so far
I've been working off this jsfiddle and well as this one. This is what I've come up with (that doesn't work, they all fade in equally spaced in a line and don't stay confined from to the site)
Here's my jsfiddle, code below
function randomPosition() {
var h = $(window).height()-10;
var w = $(window).width()-10;
var newHeight = Math.floor(Math.random() * h);
var newWidth = Math.floor(Math.random() * w);
return [newHeight, newWidth];
}
$(document).ready(function() {
var newPosition = randomPosition();
$('.star').css( {
'margin-left':newPosition[1]+'px',
'margin-top':newPosition[0]+'px'
}).each(function(index) { $(this).delay(1500*index).fadeIn('slow');
})
});
CSS
body {
background-color: black;
}
.star {
height: 10px;
width: 10px;
background-color: white;
display: none;
}
HTML (is there a way to do this with just a for loop or something similar?)
<div class="star"> </div>
<div class="star"> </div>
<div class="star"> </div>
<div class="star"></div>
The sizing and positioning isn't too hard. The thing is to do it all in the each loop - currently you get 1 position and use it for everything. Also you will want to make them position:absolute so they don't go off the page.
I've updated your fiddle to set the random position and a size between 5 and 10px:
The overlapping is a bit harder. You need to keep track of the sizes and positions you have generated and in the same .each function compare the current generated size+position to the previous ones to check for overlapping.
http://jsfiddle.net/5ocb5aww/3/
function randomPosition() {
var h = $(window).height()-10;
var w = $(window).width()-10;
var newHeight = Math.floor(Math.random() * h);
var newWidth = Math.floor(Math.random() * w);
return [newHeight, newWidth];
}
function randomSize() {
return Math.round(Math.random() * 5) + 5;
}
$(document).ready(function() {
// stores generated star positions
var stars = [];
$('.star').each(function(index) {
var newPosition, newSize;
// check for overlap
var isOverlap = true;
while(isOverlap)
{
newPosition = randomPosition();
newSize = randomSize();
// check previous stars to see if an edge of this one overlaps
isOverlap = $.grep(stars, function(s) {
return (
(newPosition[1] >= s.x1 && newPosition[1] <= s.x2)
|| (newPosition[1]+newSize >= s.x1 && newPosition[1]+newSize <= s.x2)
)
&& (
(newPosition[0] >= s.y1 && newPosition[0] <= s.y2)
|| (newPosition[0]+newSize >= s.y1 && newPosition[0]+newSize <= s.y2)
);
}).length > 0;
}
// store to check later stars against it
stars.push({
x1: newPosition[1],
x2: newPosition[1] + newSize,
y1: newPosition[0],
y2: newPosition[0] + newSize,
size: newSize});
$(this).css({
'margin-left':newPosition[1]+'px',
'margin-top':newPosition[0]+'px',
'width':newSize + 'px',
'height':newSize + 'px'
});
$(this).delay(800*index).fadeIn('slow');
})
});
Here is my approach to your exercise ... the overlapping position would require a little bit more effort ... I'll leave you that to sort for yourself (may require restructuring the code I'm handing here)
jsFiddle Demo
JS
function starDust(wdt, hgt, tSt, tAp){
var timer = tAp * 1000;
var defInt = tSt,
starInt = setInterval(function(){
var posX = Math.floor((Math.random() * wdt) + 1),
posY = Math.floor((Math.random() * hgt) + 1),
size = Math.floor((Math.random() * 10) + 1);
$('body').append('<div class="star"></div>');
$('.star:last').css({'width':size,'height':size,'left':posX,'top':posY}).hide().fadeIn('slow');
var totalStars = $('.star').length;
if(totalStars == defInt){
clearInterval(starInt);
}
}, timer);
}
$(function(){
// Function arguments: starDust(max X position in px, max Y position in px, total number of stars, time in seconds between stars show);
starDust(600,300,25,1);
});
CSS
body{
background-color:#000;
}
.star{
position: absolute;
background-color:#fff;
min-width:5px;
min-height:5px;
}

jquery function doesn't trigger

My resizeAreas function doesn't trigger in $(document).ready function. Here's the link to the app. I have also tried $(document).load but same result.
This problem is not consistent. But most of the time the page doesn't load correctly. If the list of task have the same height and 4 equal(as height) lists on the row then they are loaded correctly else they're not. To make sure that you see how it must look you can move a task element from one list to another. I don't have the reputation to post images.
Here is most of the javascript code:
function makeDropArea() {
var widthArea = $(".taskListing").width() / 2 - 55;
var heightArea = $(".taskListing").height();
$('.dropArea').width(widthArea);
$('.dropArea').css('margin-left', 5 + 'px');
$('.generalInfo').css('margin-left', 5 + 'px');
$('.generalInfo').css('width', widthArea * 2 + 220 - 45);
$('.subTaskHeader').css('width', widthArea + 25);
}
function makeDropElement(){
var widthEl = $('.dropArea').width() + 25 ;
$('.task').width(widthEl);
}
function taskListingSize(){
var width = getWidth();
var height = getHeight() * 80 / 100;
$('.taskListing').css('width', width);
}
function resizeAreas() {
$('.taskListing').each(function(){
var highestBox = 0;
$('.dropArea', this).each(function(){
if($(this).height() > highestBox)
highestBox = $(this).height();
});
$('.dropArea',this).css('min-height', highestBox);
});
}
$(document).ready(function () {
menu();
taskListingSize();
makeDropArea();
makeDropElement();
resizeAreas(); //<-- this is the one with the problems
$(".dropArea").resize(resizeAreas());
$( window ).resize(function() {
taskListingSize();
makeDropArea();
makeDropElement();
});
});
Thanks.
Update 1:
I've done some more testing and this bug is only on chrome and firefox but not on IE. The problem appears only on the openshift platform.
Either change:
$(".dropArea").resize(resizeAreas());
To:
$(".dropArea").resize("resizeAreas");
Or:
$(".dropArea").resize(function(){
resizeAreas();
});
I think it should be
$(".dropArea").resize(resizeAreas);
Or
$(".dropArea").resize(function(){
resizeAreas();
});

jQuery animated background with many elements

I'm trying to create a background with many circles moving around and it really pushes the browser a bit too hard.
Is there any way I can do this without being too resource-intensive?
Here's the current code I have:
http://jsfiddle.net/2MGAE/2/
$( document ).ready(function() {
// Create all our glorious bubbles
for (var i = 1; i <= 150; i++) {
$('#bubbles').append('<span class="bubble' + i + '"></span>');
}
// Get random number
function getRandomInt (min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
// Function to move bubbles randomly
function moveRandom(obj) {
var positionTop = getRandomInt(-350,1000);
var positionLeft = getRandomInt(-700,1600);
var positionTopNew = positionTop + getRandomInt(-50,50);
var positionLeftNew = positionLeft + getRandomInt(-50,50);
var size = getRandomInt(30,60);
function animation() {
obj.animate({
top: positionTop + 'px',
left: positionLeft + 'px',
width: size,
height: size
}, 6000
);
obj.animate({
top: positionTopNew + 'px',
left: positionLeftNew + 'px'
}, 6000, function() {
animation();
});
}
animation();
}
// Activate bubble movement
$('#bubbles span').each(function() {
moveRandom($(this));
})
});
Or is it just too many elements animated that it will always be a resource hog?
Pretty neat! You may want to use the HTML 5 canvas element to do this. It will utilize the GPU and doesn't require 3rd party js libraries.
REF:
http://updates.html5rocks.com/2012/07/Taking-advantage-of-GPU-acceleration-in-the-2D-canvas
https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Canvas_tutorial
There are 2 things that come to mind.
You could look up how to use the <canvas> tag with really cool examples at Createjs.com
or
you could gopro and learn webgl and three.js which uses the gpu for cool fast 3d effects!

Moving Div by everyclick without JQuery

I used this jquery to move a div by every click.
$(document).ready(function(){
$('#hero').click(function(){
$(this).animate({
left: '+=50px'
},300);
})
})
I'd like to avoid jquery whenever it is possible to get deeper into pure JS.
Is there anyway to achieve the same effect without using jquery?
I know that this will be more complex, but just trying to learn.
you can use the same technique in javascript:
// get the object refrence
var hero_obj = document.getElementById('hero');
// attach the onclick event
hero_obj.onclick = function(){
this.style.left = ( parseInt(this.style.left, 10) + 50 ) + 'px'
};
However, the effect won't be as smooth as jquery
I've stumbled upon this gem on vanilla-js.com a few weeks ago:
var s = document.getElementById('thing').style;
s.opacity = 1;
(function(){(s.opacity-=.1)<0?s.display="none":setTimeout(arguments.callee,40)})();
I really like the simplicity and the size of the code. Elegant and efficient!
I've created a function that affects the left property of an element of your choice based on the code above:
/* element: DOM element such as document.getElementById('hero')
distance: distance in pixels to move to the left such as 50 or 100 */
function moveBy(element, distance){
var target = isNaN(parseInt(s.left)) ? distance : parseInt(s.left) + distance;
(function(){
s.left = isNaN(parseInt(s.left)) ? '1px' : (parseInt(s.left) + 1).toString() + 'px';
if(parseInt(s.left) <= distance) setTimeout(arguments.callee, 40);
})();
}
You can play around and see what fits to your liking in terms of speed and smoothness. Try it here on a jsfiddle.
/* So you go: */
moveBy(document.getElementById('hero'), 50);
/* Or you can bind it to an event */
document.getElementById('hero').addEventListener('click', function(event){
moveBy(this, 50);
});
What a solution like this would need if you're willing to make it better is to replace the left property by translate. As Paul Irish states on his blog, translate provides way better performance than moving elements around with TRBL (top-left-bottom-right). Some sort of easing functions could be added as well to smooth things out.
Here's a code with animation. This snippet is only for modern browsers, but it is easy to modify to work with older browsers (IEs) too. (Actually only attachment of the event needs to be fixed.)
window.onload = function () {
var timer, k, intervals, kX, kY,
counter = 0,
hero = document.getElementById('hero'),
posX = hero.offsetLeft,
posY = hero.offsetTop,
anim = function (elem, params) {
posX += kX;
posY += kY;
elem.style.left = posX + 'px';
elem.style.top = posY + 'px';
if (counter > intervals) {
clearInterval(timer);
counter = 0;
} else {
counter++;
}
return;
},
move = function (elem, params) {
if (timer) {
clearInterval(timer);
counter = 0;
}
k = Math.atan2(params.left, params.top);
kX = Math.sin(k);
kY = Math.cos(k);
intervals = Math.floor(Math.sqrt(Math.pow(params.left, 2) + Math.pow(params.top, 2)));
timer = setInterval(function () {
anim(elem, params);
return;
}, params.speed);
return;
};
document.getElementById('hero').addEventListener('click', function (e) {
move(e.currentTarget, {left: 50, top: 0, speed: 0});
return;
}, false);
return;
}
As you can see, with this code you can also move elements vertical and adjust speed. To switch direction, just add - to corresponding property. The code is using pixels only as units, but that's easy to modify if needed.
It's also easy to convert this functional code to an object. Also jQuery-like duration can be added by passing property params.duration instead of params.speed and doing some advanced calculations with that and kX, kY.
Working demo at jsFiddle
I needed to create an animation solution with easing a while back without using a framework.
The tricky part for me was coping with interrupting/restarting animations part way through when they are tied to user interactions. I found that you can run into trouble pretty quickly if your animations double-fire.
Here is on github: https://github.com/robCrawford/js-anim
There are a few supporting functions but here's the main animation:
function animate(el, prop, to, pxPerSecond, easing, callback){
/**
* Animate style property
* i.e. animate(div1, "width", 1100, 1000, "out", function(){console.log('div1 anim end')});
*
* #param el DOM element
* #param prop Property to animate
* #param to Destination property value
* #param pxPerSecond Speed of animation in pixels per second
* #param easing (optional) Easing type: "in" or "out"
* #param callback (optional) Function to call when animation is complete
*/
var frameDur = 10,
initPropVal = parseInt(getCurrCss(el, prop)),
distance = Math.abs(to-initPropVal),
easeVal = (easing==="in")?1.5:(easing==="out")?0.5:1, // >1 ease-in, <1 ease-out
elAnimData = getData(el, 'animData');
//Quit if already at 'to' val (still fire callback)
if(initPropVal===to){
if(callback)callback.call();
return;
}
//Init animData for el if first anim
if(!elAnimData){
elAnimData = {};
setData(el, {'animData':elAnimData});
}
//Get data for prop being animated or create entry
var animDataOb = elAnimData[prop];
if(!animDataOb)animDataOb = elAnimData[prop] = {};
//Don't re-initialise an existing animation i.e. same prop/to
if(animDataOb.to === to)return;
animDataOb.to = to; //Store 'to' val
//Clear any exisiting interval
if(animDataOb.intId){
clearInterval(animDataOb.intId);
animDataOb.intId = null;
}
//Create new anim
animDataOb.intId = (function(animDataOb){
var totalSteps = Math.round((distance/pxPerSecond)/(frameDur*.001)),
thisStep = 0;
return setInterval(function(){
var newVal = easeInOut(initPropVal, to, totalSteps, thisStep++, easeVal);
if(!isNaN(newVal))el.style[prop] = newVal + "px"; //Allow 0
if(thisStep > totalSteps)endAnim(animDataOb, callback);
}, frameDur);
})(animDataOb);
}
function endAnim(animDataOb, callback){
//End anim
clearInterval(animDataOb.intId);
animDataOb.intId = animDataOb.to = null;
if(callback)callback.call();
}

Categories