Reputation: 1163
I am trying to understand what is causing the speed movement of the UFO image to increase once the function has been re-triggered.
var timer = null;
var el = null;
var stop = true;
var restart = false;
var stopforward = true;
var secondadd = false;
function moveit() {
if (parseInt(el.style.left) > screen.width - 100) {
stopforward = false;
}
if (stop) {
if (stopforward) {
el.style.left = parseInt(el.style.left) + 6 + "px";
el.style.top = 100 + (80 * Math.sin(parseInt(el.style.left) / 50)) + "px";
}
}
timer = setTimeout(moveit, 25);
if (stopforward == false) {
if (stop) {
if (parseInt(el.style.left) == 0) {
stopforward = true;
}
el.style.left = parseInt(el.style.left) - 6 + "px";
el.style.top = 100 + (80 * Math.sin(parseInt(el.style.left) / 50)) + "px"
}
}
}
function start() {
el = document.getElementById("img1");
el.style.left = "0px";
moveit();
startstop();
}
function secondstart() {
if (restart == true) {
stop = true;
moveit();
restart = false;
}
}
function startstop() {
document.getElementById("start").removeEventListener("click", start, false);
document.getElementById("start").addEventListener("click", secondstart, false);
}
document.getElementById("start").addEventListener("click", start, false);
document.getElementById("stop").onclick = function() {
stop = false;
restart = true;
}
#range {
position: absolute;
top: 0;
left: 0;
background: url(https://blenderartists.org/uploads/default/original/4X/7/e/2/7e2d7bea4ac21388c4a96e1371f375c4ce00094b.jpg);
cursor: crosshair;
height: 300px;
width: 100%;
}
#img1 {
position: absolute;
border: none;
left: 0;
top: 100px;
padding: 10px;
width: 100px;
}
#start,
#stop {
position: relative;
top: 300px;
}
<div id="range">
<div id="score"></div>
<img id="img1" src="https://www.pngkit.com/png/full/779-7799339_unidentified-flying-object-saucer-cartoon-ufo-png.png">
</div>
<button id="start">START</button>
<button id="stop">STOP</button>
Sorry if it looks a bit too convoluted. There really is no other way to actually show you what happens.
When you first click start
, the ufo moves at a certain speed. When you click stop
, and then start
again, the speed doubles. This happens for two reasons. One is because when you click start
the second time, you re-trigger the moveit
function. The second is because the setTimeout
method that re-triggers the moveit
function is never disabled even when stop
is clicked, because it is outside the if
condition. This results in the function being triggered twice. I can see why that would lead to one iteration being doubled in speed, but why does it remain double?
Note, please don't give me any advice on how to "fix it". I know how to fix it. This isn't my question. This is purely a curiosity based question. I just want to understand why does the speed increase. What makes it increase.
Note2, you should view it on full screen.
Upvotes: 0
Views: 115
Reputation: 351328
please don't give me any advice on how to "fix it". I know how to fix it. This isn't my question.
OK.
This is purely a curiosity based question. I just want to understand why does the speed increase. What makes it increase.
So we see that when moveIt
is called it will always reschedule itself to be run again, using setTimeout
. This means that once you call it, you have started an infinite sequence of calls, each separated by 25ms.
When the Start button is clicked, moveIt
gets called. So far so good. It's doing what it should do. The Stop button works fine too. moveIt
keeps continuing its repeated calls, but the stop
variable now prevents it from making any changes.
The user clicks the Start button again. Now things go wrong: moveIt
gets called again. So now we have two self-propagating sequences of moveIt
calls. You can see how that doubles the speed of the changes.
In short, make sure you only call moveIt
once, and let setTimeout
do the rest. Don't call moveIt
explicitly again.
You write in comments:
I thought about that, but when I tested that scenario, I ended up dismissing it.
Then I think you did not test it correctly. So now I will provide the fix:
In the function secondstart
remove the call to moveIt
:
function secondstart() {
if (restart == true) {
stop = true;
// moveit(); // <------- remove!
restart = false;
}
}
Below the version with that correction so you can run it -- I didn't change anything else:
var timer = null;
var el = null;
var stop = true;
var restart = false;
var stopforward = true;
var secondadd = false;
function moveit() {
if (parseInt(el.style.left) > screen.width - 100) {
stopforward = false;
}
if (stop) {
if (stopforward) {
el.style.left = parseInt(el.style.left) + 6 + "px";
el.style.top = 100 + (80 * Math.sin(parseInt(el.style.left) / 50)) + "px";
}
}
timer = setTimeout(moveit, 25);
if (stopforward == false) {
if (stop) {
if (parseInt(el.style.left) == 0) {
stopforward = true;
}
el.style.left = parseInt(el.style.left) - 6 + "px";
el.style.top = 100 + (80 * Math.sin(parseInt(el.style.left) / 50)) + "px"
}
}
}
function start() {
el = document.getElementById("img1");
el.style.left = "0px";
moveit();
startstop();
}
function secondstart() {
if (restart == true) {
stop = true;
// moveit(); // <---- removed!
restart = false;
}
}
function startstop() {
document.getElementById("start").removeEventListener("click", start, false);
document.getElementById("start").addEventListener("click", secondstart, false);
}
document.getElementById("start").addEventListener("click", start, false);
document.getElementById("stop").onclick = function() {
stop = false;
restart = true;
}
#range {
position: absolute;
top: 0;
left: 0;
background: url(https://blenderartists.org/uploads/default/original/4X/7/e/2/7e2d7bea4ac21388c4a96e1371f375c4ce00094b.jpg);
cursor: crosshair;
height: 300px;
width: 100%;
}
#img1 {
position: absolute;
border: none;
left: 0;
top: 100px;
padding: 10px;
width: 100px;
}
#start,
#stop {
position: relative;
top: 300px;
}
<div id="range">
<div id="score"></div>
<img id="img1" src="https://www.pngkit.com/png/full/779-7799339_unidentified-flying-object-saucer-cartoon-ufo-png.png">
</div>
<button id="start">START</button>
<button id="stop">STOP</button>
Upvotes: 2
Reputation: 2304
The moveIt()
method calls itself recursively. Once this process is started, it is never ending, as from within itself, the stop
condition is trapped and the moveIt
function is then set to behave accordingly (not moving the UFO, every 25ms).
However, in this instance, the moveIt
function will keep being called. When you press start again, you invoke secondstart()
, which will call moveIt()
again, starting another timeout, and setting the stop
variable so that the UFO moves.
The previously active timeout will now respond to this change in condition too, as setTimeout
acts asynchronously. So now there will be two asyncrhonous instances of moveIt()
, and if you repeat this operation there will be three, four and so on... All performing the same actions, which being CSS-based, will make your UFO move coherently, as its movement are calculated out of its instantaneous position.
I am still going to spill my two cents on what could be the quickest fix, because its fun... and might help others! So you probably guessed that already, but we need to find a way to get rid of that timeout, that you have wisely identified through the global variable timer
.
You could then use the clearTimeout()
method to perform clearTimeout(timer)
within the secondstart()
function. This way you clear the earlier timeout before starting a new one by invoking the moveIt()
function again. Else... simply remove the second moveIt()
call in the secondstart()
function, as it is being executed already, and varying the stop
variable will be enough to trigger a restart... with no adverse consequences!
Obviously, this will keep the moveIt()
function being called continously even when the UFO is stopped, which maybe isn't super efficient.
var timer = null;
var el = null;
var stop = true;
var restart = false;
var stopforward = true;
var secondadd = false;
function moveit() {
if (parseInt(el.style.left) > screen.width - 100) {
stopforward = false;
}
if (stop) {
if (stopforward) {
el.style.left = parseInt(el.style.left) + 6 + "px";
el.style.top = 100 + (80 * Math.sin(parseInt(el.style.left) / 50)) + "px";
}
}
timer = setTimeout(moveit, 25);
if (stopforward == false) {
if (stop) {
if (parseInt(el.style.left) == 0) {
stopforward = true;
}
el.style.left = parseInt(el.style.left) - 6 + "px";
el.style.top = 100 + (80 * Math.sin(parseInt(el.style.left) / 50)) + "px"
}
}
}
function start() {
el = document.getElementById("img1");
el.style.left = "0px";
moveit();
startstop();
}
function secondstart() {
if (restart == true) {
clearTimeout(timer);
stop = true;
moveit();
restart = false;
}
}
function startstop() {
document.getElementById("start").removeEventListener("click", start, false);
document.getElementById("start").addEventListener("click", secondstart, false);
}
document.getElementById("start").addEventListener("click", start, false);
document.getElementById("stop").onclick = function() {
stop = false;
restart = true;
}
#range {
position: absolute;
top: 0;
left: 0;
background: url(https://blenderartists.org/uploads/default/original/4X/7/e/2/7e2d7bea4ac21388c4a96e1371f375c4ce00094b.jpg);
cursor: crosshair;
height: 300px;
width: 100%;
}
#img1 {
position: absolute;
border: none;
left: 0;
top: 100px;
padding: 10px;
width: 100px;
}
#start,
#stop {
position: relative;
top: 300px;
}
<div id="range">
<div id="score"></div>
<img id="img1" src="https://www.pngkit.com/png/full/779-7799339_unidentified-flying-object-saucer-cartoon-ufo-png.png">
</div>
<button id="start">START</button>
<button id="stop">STOP</button>
Upvotes: 1