New to Javascript, and this one is troubling me a little. I am basically trying to make a whack-a-mole type game. Once I add the images, I want to remove them one by one. I think I have the unique IDs added, but but when I add the image removal, everything stops working. What am I doing wrong? I also thought to find other items besides the .remove, but I'm not sure what that part of the code is called (method, function, etc.) My code is below.
function addMole(){
var xPos = randomPosX();
var yPos = randomPosY();
var count=0;
$("#gamespace").append('<img src="img/roach.png" style="left: '+xPos+'px; top: '+yPos+'px;" id="img'+count+'"/>');
var r=Math.floor(Math.random()*2000);
t=setTimeout("addMole();", r);
$("#gamespace").remove("img'+count+'"").removeAttribute(img'+count+'");
count++;
}
Everything stops working because this line is not valid JavaScript:
$("#gamespace").remove("img'+count+'"").removeAttribute(img'+count+'");
check your quotes: while '...' and "..." are interchangeable, single " and ' are not.
Another problem is, after fixing the above, that your code logic doesn't pan out: you are adding an image then immediately removing it. I don't quite know what you are trying to do so I can't rewrite it for you; the best I can do is tell you to define your flow more in detail.
Finally, when you are creating elements programmatically, it is generally nicer and less failure-prone to keep element references rather than mess with IDs.
var $gamespace = $('#gamespace');
var $img = $('<img>').css({
left: left,
top: top
}).appendTo($gamespace);
// ...
$img.remove();
use
$("#gamespace").find("#img'"+count+"'").remove();
instead of
$("#gamespace").remove("img'+count+'"").removeAttribute(img'+count+'");
or.. while image ID is unique you can use directly
$("#img'"+count+"'").remove();
You had some invalid quotations:
function addMole(){
var xPos = randomPosX();
var yPos = randomPosY();
var count=0;
$("#gamespace").append('<img src="img/roach.png" style="left: '+xPos+'px; top:' + yPos + 'px;" id="img'+count+'"/>');
var r=Math.floor(Math.random()*2000);
t = setTimeout(addMole, r);//is t defined already?
$("#gamespace").remove("#img" + count).removeAttribute("img" + count);//why you need removeAttribute
count++;
}
move the count declaration outside of your function... it's reset to 0 each time it runs.
Also, would add 300 to your random number for remove, so it will show at elast 1/3 second... beyond this, you can reference the function directly in the setTimeout(addMole,r); ... you're creating an extra wrapper that isn't needed.
var count = -1; //first is 1
function addMole(){
count++;
var rnd;
var xPos = randomPosX();
var yPos = randomPosY();
var img = $('<img src="img/roach.png" />')
.css({left:xPos,top:yPos})
.addClass('mole')
.attr('id','img'+count);
img.click(addPoints);
$('#gamespace').append(img);
//remove image at random interval
rnd = 300 + Math.floor(Math.random() * 2000);
setTimeout(removeImageShim(img),rnd);
//add next mole at random interval
rnd = Math.floor(Math.random() * 2000);
setTimeout(addMole, rnd);
}
function addPoints() {
//TODO: addPoints
$(this).remove();
}
function removeImageShim(img) {
return function(){
img.remove();
}
}
Related
I'm currently using the following javascript as shown below.
It's working well when I place just text within the div .image_scroll_3 but as soon as I insert images the scroll glitches and won't move past the top of the image.
Any advice would be much appreciated
JS
<script>
(function($, undefined) {
$.fn.loopScroll = function(p_options) {
var options = $.extend({
direction: "upwards",
speed: 60
}, p_options);
return this.each(function() {
var obj = $(this).find(".image_scroll_2");
var text_height = obj.find(".image_scroll_3").height();
var start_y, end_y;
if (options.direction == "downwards") {
start_y = -text_height;
end_y = 0;
} else if (options.direction == "upwards") {
start_y = 0;
end_y = -text_height;
}
var animate = function() {
// setup animation of specified block "obj"
// calculate distance of animation
var distance = Math.abs(end_y - parseInt(obj.css("top")));
//alert("animate " + obj.css("top") + "-> " + end_y + " " + distance);
//duration will be distance / speed
obj.animate(
{ top: end_y }, //scroll upwards
1500 * distance / options.speed,
"linear",
function() {
// scroll to start position
obj.css("top", start_y);
animate();
}
);
};
obj.find(".image_scroll_3").clone().appendTo(obj);
$(this).on("mouseout", function() {
obj.stop();
}).on("mouseout", function() {
animate(); // resume animation
});
obj.css("top", start_y);
animate(); // start animation
});
};
}(jQuery));
$("#example4").loopScroll({ speed: 700 });
</script>
I think the problem is that your text_height is calculated before the images are actually loaded inside your .image_scroll_3 elements. So you'll need to wait for the images to load.
Put your loopScroll call inside a $(window).load like so:
$(window).load(function(){
$('#example4').loopScroll({speed:700});
});
That massive glitch should now be gone as the fix above should have helped mitigate it.
However, there is still some unwanted jank / stutter (don't want to use the word glitch again, lets keep it reserved for the initial problem) in movement of all images if you notice and I am guessing that is probably because we are animating the whole thing too fast. Passing in speed like 100 or 200 resolves that but this is not really a solution because, ideally, you should be able to put in any speed value and it should just produce smooth animations out of it.
I am working on exactly the same thing but before that, I want to know if the above fix for the glitch helps you and we are finally done with it? Let me know.
Update:
Here is my version that I spoke of earlier, for your perusal.
Because all you are trying to do is loop images in a very linear fashion, I, for one, do not see the need to rely on animate() function of jQuery. There is requestAnimationFrame API that I have leveraged instead. In fact, in my demonstration below I have completely abandoned jQuery in favour of vanilla JavaScript only because I kept finding alternatives to pretty much everything we needed to do in this demo. But of course, this is also a very subjective matter; a taste thing; so if you want to go with jQuery, then by all means.
Another fundamental change I brought is rather than updating top values, I have resorted to updating translateY values.
Take a look at this jsFiddle and let me know if it fits your needs.
JavaScript code of which is as belows:
// [http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/]
window.requestAnimFrame=(function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||function(callback){window.setTimeout(callback,1000/60);};})();
var main=null;
var imageScroll2=null;
var imageScroll3=null;
var totalHeight=null;
var initY=null;
var destY=null;
var currY=null;
var increment=null;
var direction=null;
var UP=null;
var DOWN=null;
var isPlaying=null;
function init(){
main=document.getElementById('example4');
imageScroll2=main.getElementsByClassName('image_scroll_2')[0];
imageScroll3=main.getElementsByClassName('image_scroll_3')[0];
totalHeight=imageScroll3.clientHeight;
UP='upwards';
DOWN='downwards';
isPlaying=true;
direction=UP;
increment=10;
if(direction===DOWN){
initY= -totalHeight;
destY=0;
}else{
initY=0;
destY= -totalHeight;
}
currY=initY;
imageScroll2.appendChild(imageScroll3.cloneNode(true));
if(imageScroll2.addEventListener){
imageScroll2.addEventListener('mouseover',function(){isPlaying=false;},false);
imageScroll2.addEventListener('mouseout',function(){isPlaying=true;},false);
}else{
imageScroll2.attachEvent('onmouseover',function(){isPlaying=false;});
imageScroll2.attachEvent('onmouseout',function(){isPlaying=true;});
}
requestAnimFrame(render);
}
function render(){
if(isPlaying){
imageScroll2.style.transform='translate(0px,'+currY+'px)';
if(direction===DOWN){
currY+=increment;
if(currY>=destY){currY=initY;}
}else{
currY-=increment;
if(currY<=destY){currY=initY;}
}
}
requestAnimFrame(render);
}
//
init();
I'm a beginner and currently I can move an image from left to right and I can turn it back to its initial point when moving finishes. What I want to do is also controlling image's speed. In order to do this I tried the codes below:
<script type="text/javascript">
var userWidth = window.screen.width;
function moveRight(speed) {
var pp = document.getElementById("myimage");
var lft = parseInt(pp.style.left);
var tim = setTimeout("moveRight()", speed);
lft = lft + 50;
pp.style.left = lft + "px"
if (lft > (userWidth) + 80) {
document.getElementById("myimage").style.left = 100 + "px";
clearTimeout(tim);
}
}
</script>
And html:
<form>
<input type="button" value="Speed 1" onclick="moveRight(50)" />
<input type="button" value="Speed 2" onclick="moveRight(25)" />
<input type="button" value="Speed 3" onclick="moveRight(10)" />
</form>
My problem: there is no difference when I click any of three buttons. Image is always moving with the same speed and looks like buttons have no control on the speed.
The mistake you did in your orignal code is that when you call the function again you do not pass it the value of speed which means that the only thing that happens is that the first animation is delayed.
Try replacing the call with this line and then your code should work.
var tim = setTimeout("moveRight("+speed+")", speed);
With this you can still do this without using Jquery
Your above code doesn't have any speed logic. The only thing you're doing is delaying the time before the animation start :
var tim = setTimeout("moveRight()", speed);
Making a recursive call to a function waiting for an attribute (speed), is also a nogo.
I setted up a quick fiddle to demonstrate jquery animation speed :
http://jsfiddle.net/yeQtB/
To achieve speed in animation, two possibilities :
• Cycle logic :
You admit that rather than seconds, you can work with cycles, then just create a for loop, that will iterate until animation is over. edit : this is just here for the sake of the explanation
• Time logic
You have a distance and a timeframe (in sec), divide the distance by the timeframe, it gives you step, divide the distance by the step, it will give you the number of steps, then it is just a matter of creating a settimeout firing every second in a loop. edit : if you achieved this and is eager to go for something a bit smoother, i'd advise to have a look at the requestAnimationFrame HTML5 Api :
http://paulirish.com/2011/requestanimationframe-for-smart-animating/
You have to pass the variable speed to moveRight in the setTimeout :
var userWidth = window.screen.width,
tim,
pp = document.getElementById("myimage"); // You should cache your variables
function moveRight(speed) {
var lft = parseInt(pp.style.left) || 0;
tim = setTimeout("moveRight(" + speed + ")"); // Here you have to pass speed as a parameter
lft = lft + speed; // Here I guess it is speed rather than 50
pp.style.left = lft + "px"
if (lft > (userWidth) + 80) {
pp.style.left = 100 + "px";
clearTimeout(tim);
}
}
I am doing some research at the moment into creating a new maths game for primary school children where divs from 0-9 appear at random inside a container.
A question is given at the beginning. Something like, multiples of 20. The user will then have to click on the correct ones, and they will then be counted at the end and a score will be given.
I have just changed the speed in which the divs appear so that they appear for longer and more than one at a time to make the game easier for younger children.
I used "fadeIn" like so..
$('#' + id).animate({
top: newY,
left: newX
}, 'slow', function() {}).fadeIn(2000);
}
My problem is that now when I shoot the correct or incorrect number the animation is very glitchy and I cannot figure out why.
Fiddle: http://jsfiddle.net/cFKHq/6/ (See version 5 to see what it was like before)
Inside startplay(), control the concurrency when calling scramble() , I do it with a global var named window.cont, so I replaced your following call:
play = setInterval(scramble, 1800);
for this one:
play = setInterval(function() {
if (window.cont){
window.cont = false;
scramble();
}
}, 1000);
The var window.cont needs to be set globally at the start of your code, like so:
var miss = 0;
var hit = 0;
var target = $("#target");
window.cont = true;
So with window.cont you now can control that animations are executed one after another, without overlapping, like so:
$('#'+id).css({
top: newY,
left: newX
}).fadeIn(2000, function() {
setTimeout(function() {
$('#' + id).slideUp('fast');
window.cont = true;
}, 1500);
});
See working demo
I'm having slight troubles with my code. What I'm trying to do is make these element's css property 'left' update according to the difference of it's current left value, and the amount the page resizes. This way, when the page resizes and the background moves over, the elements will move too. Take a look at the code below and I'll describe the issue:
$(window).resize(function() {
var docWidth = $(window).width();
if (docWidth < 1000) {
var difference = 1000-docWidth;
$('#headNav a,#icons div').each(function() {
var left = $(this).position().left;
var newLeft = left - difference;
$(this).css({ 'left' : newLeft });
});
}
});
So the issue that I'm getting is the elements are being given left values of wild numbers, while the value of the variable 'newLeft' is the reasonable, desired value. The each function I think is collecting the sums of these values and running them for each element x amount of times that the elements found exist (so if there's 5 elements it runs 5 times, I mean.) What I want is this code to execute uniquely for each element, but just once each, not each element 10 times! (that's how many elements are in the html).
So my question is, how can this be achieved? I hope I explained myself well enough, this was tough to iterate. Any help is extremely appreciated. Thank you!
Here's a fun trick: Include += in your .css() call:
$(this).css({left: "+=" + difference});
jQuery does the math for you to get the new value.
Try this:
$(window).resize(function() {
var docWidth = $(window).width();
if (docWidth < 1000) {
var difference = 1000-docWidth;
$('#headNav a,#icons div').each(function(iconInst) {
var left = $("#" + iconInst).position().left;
var newLeft = left - difference;
$("#" + iconInst).css({ 'left' : newLeft });
});
}
});
I need to move a div from the right to the left of the screen, but using both classic JS and jQuery makes it jerky:
My divs:
<div class="lisp" id="lisp0" style="top:100px;">)</div>
<div class="lisp2" id="lisp1" style="top:300px;">)</div>
Classic javascript method:
function move()
{
pos = parseInt($("#lisp1").css("right"));
$("#lisp1").css("right", pos+10+"px");
}
var interval = setInterval("move()",10);
jQuery method:
$("#lisp0").animate({"left": "-=2200px"}, 10000);
I made a webpage to show you how jerky it is. The first move is with jQuery (the smoothest one), the second one with classic JS. With several divs (and classic JS), it starts to be really annoying.
I tried to modify jQuery.fx.interval, but it doesn't really increase performances.
So my question is: what is the best way to make these divs move smoothly ?
You asked me for an example to improve the speed, I'm not an expert but here is what I would do:
Don't use setInterval with string functions, they have to run through eval to run, so use this instead. In fact I wouldn't use setInterval at all for the main loop (see point #3).
setInterval(doSomething, 100)
Store an object you will be using multiple times (especially in a function that loops constantly). Even this example is not ideal:
var lisp = $('#lisp1');
function move()
{
var pos = parseInt( lisp.css("right"), 10 ); // always use a radix
lisp.css("right", pos + 10 + "px");
}
For functions that loop constantly, be as short and concise as possible and eliminate extra function calls. From your second link, I compressed this code:
function move(){
$(".lisp").each(function(){
pos = parseInt($(this).css("right"));
if (pos > width)
$(this).remove();
else
$(this).css("right", pos+speed+"px")
});
$(".bonus").each(function(){
pos = parseInt($(this).css("right"));
if (pos > width)
$(this).remove();
else
$(this).css("right", pos+speed+"px")
});
$(".special").each(function(){
pos = parseInt($(this).css("right"));
if (pos > width)
$(this).remove();
else
$(this).css("right", pos+speed+"px")
});
}
into this more concise version:
function move(){
$(".lisp, .bonus, .special").each(function(){
var pos = parseInt(this.style.right || 0, 10);
if (pos > width) {
$(this).remove();
} else {
this.style.right = pos + speed + "px";
}
});
if (!over) { setTimeout(move, 10); } // use this instead of the setInterval()
}
It's still not ideal, because your code keeps adding more and more objects. It should be limited because at one point I have over 200 objects on the screen and the page came to a crawl. This is also why I would use the setTimeout in the last line instead of the setInterval you use because the script may not have cycled through all of the elements before you want it to start again.
I'm sure there are more points someone else could add to optimize my or your code even more :)