3

I'm having a div (#option1) in the shape of a dot animating a graph using an array, Option1[], in which all the heights of the several 'dots' are included. I'm using a for loop to create space between two dots (left: '+=5px') and get the height for the next dot from the array, like so:

for (var i = 0; i < Option1.length; ++i) {
  $('#option1').animate({left: '+=5px',top: Option1[i]}, 200);
}

As you can see, the div takes 200ms to move to it's next coordinates. This all works fine, but it is just a moving dot. I actually want to make it look like the dot is drawing a line, so I tried to use a canvas and lineTo methods, like so:

var xaxis=5;
var a = document.getElementById("myCanvas1");
var atx = a.getContext("2d");
atx.beginPath();
atx.moveTo(5, 900);

for (var i = 0; i < Option1.length; ++i) {
  xaxis += 5;

  $('#option1').animate({left: '+=5px',top: Option1[i]}, 200);

  atx.lineTo(xaxis, Option1[i]);
  atx.stroke();
}

As you can see the line starts at coordinates 5,900, then moves 5px to the right and to the height of the next value of the Option1[] array. The line comes out correctly. The problem is that the total line shows instantaneously, and then the dot starts moving over the already drawn line. To make it so that a line-section only appears after the dot has past the new coordinates, I've tried the setTimeout function, setting the timeout at 200ms, just like the animate time:

$(document).ready(function() {
  var Option1 = [100, 150, 150, 130, 50, 100, 75, 125, 50, 100];

  var xaxis = 5;
  var a = document.getElementById("myCanvas1");
  var atx = a.getContext("2d");
  atx.beginPath();
  atx.moveTo(5, 400);
  atx.strokeStyle="green";

  for (var i = 0; i < Option1.length; ++i) {
    xaxis += 5;

    $('#option1').animate({
      left: '+=5px',
      top: Option1[i]
    }, 400);

    setTimeout(drawLines(), 400);

    function drawLines() {
      atx.lineTo(xaxis, Option1[i]);
      atx.stroke();
    };
  };
});
html,
body {
  width: 100%;
  height: 100%;
  background-color: black;
}

#maindiv {
  position: absolute;
  top: 5px;
  left: 5px;
  z-index: 5;
  cursor: pointer;
  Width: 500px;
  Height: 400px;
  background-color: black;
  border-radius: 1%;
  border-width: 0px;
  border-color: blue;
  border-style: solid;
  font-family: Verdana, Arial, Sans-Serif;
  color: white;
  box-shadow: 0 0 20px white;
}

canvas {
  position: absolute;
  z-index: 5;
  cursor: pointer;
  Width: 100%;
  Height: 100%;
  background-color: none;
  border-radius: 1%;
  border-width: 1px;
  border-color: blue;
  border-style: solid;
  font-family: Verdana, Arial, Sans-Serif;
  color: white;
  box-shadow: 0 0 20px white;
}

#option1 {
  position: absolute;
  top: 390px;
  left: 5px;
  z-index: 10;
  Width: 5px;
  Height: 5px;
  background-color: green;
  border-radius: 50%;
  border-width: 0px;
  border-color: blue;
  border-style: solid;
  font-family: Verdana, Arial, Sans-Serif;
  color: white;
  box-shadow: 0 0 20px white;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<html>
  <body>
    <div id="maindiv">
      <canvas id="myCanvas1" width="500" height="400"></canvas>
      <div id="option1"></div>
    </div>
  </body>
</html>

(This last hidden snippet is complete with html and css btw, so it's working). No success. The total line is there instantaneously. I've also tried to write the setTimeout in other ways using other posts on stackoverflow, but always the total line shows instantaneously.

Any help to get my line to be drawn live, would be much appreciated! Also solutions that use entirely different methods/functions are most welcome. Thanks!

Teun
  • 33
  • 6
  • I think `setInterval` is more appropriate in your case. If you replace the for loop with setInterval. Your line will not be instantaneously showed. – R3tep Apr 30 '15 at 16:18
  • The only way i see how to input the next height coordinate for the next `lineTo` without using a `forloop` is to use real numbers for the array location for each value in the array (`Option1[0]`, `Option1[1]`, `Option1[2]`, etc.) instead of using `Option1[i]`. The problem with this is that the array will change every time I run the program. Array length can change from 1 to 200. – Teun May 01 '15 at 14:31
  • Is this just an intermediary step, or will the lines+dot be the only thing being drawn onto the canvas? – Yoshi May 05 '15 at 12:33
  • The lines will be the only thing drawn onto the canvas. – Teun May 06 '15 at 19:55

1 Answers1

1

Your first issue is that you are calling your drawLines function immediately and then passing the result of that function (undefined) to setTimeout.

More on that here

Once you fix that, you will find you have an issue of having a closure in a loop. There are a number of ways to fix that, though I have chosen to create a new function and pass the needed variables in to it and then have the timeout created inside that function.

More info on closure inside a loop

With both of those issues fixed, you end up with this:

New function:

function drawLine(atx, xaxis, y, delay){
    setTimeout(function(){
        atx.lineTo(xaxis, y);
        atx.stroke();
    }, delay);
}

Called as such in place of your existing setTimeout:

drawLine(atx, xaxis, Option1[i], 400 * i);  

You'll notice I have 400 * i for the timeout delay instead of just 400 as you had. If 400 is used, they will all draw at once after 400ms.

Here is a working snippet:

$(document).ready(function() {
  var Option1 = [100, 150, 150, 130, 50, 100, 75, 125, 50, 100];

  var xaxis = 5;
  var a = document.getElementById("myCanvas1");
  var atx = a.getContext("2d");
  atx.beginPath();
  atx.moveTo(5, 400);
  atx.strokeStyle="green";

  for (var i = 0; i < Option1.length; ++i) {
    xaxis += 5;

    $('#option1').animate({
      left: '+=5px',
      top: Option1[i]
    }, 400);

    drawLine(atx, xaxis, Option1[i], 400 * i);  
  };
});

function drawLine(atx, xaxis, y, delay){
    setTimeout(function(){
        atx.lineTo(xaxis, y);
        atx.stroke();
    }, delay);
}
html,
body {
  width: 100%;
  height: 100%;
  background-color: black;
}

#maindiv {
  position: absolute;
  top: 5px;
  left: 5px;
  z-index: 5;
  cursor: pointer;
  Width: 500px;
  Height: 400px;
  background-color: black;
  border-radius: 1%;
  border-width: 0px;
  border-color: blue;
  border-style: solid;
  font-family: Verdana, Arial, Sans-Serif;
  color: white;
  box-shadow: 0 0 20px white;
}

canvas {
  position: absolute;
  z-index: 5;
  cursor: pointer;
  Width: 100%;
  Height: 100%;
  background-color: none;
  border-radius: 1%;
  border-width: 1px;
  border-color: blue;
  border-style: solid;
  font-family: Verdana, Arial, Sans-Serif;
  color: white;
  box-shadow: 0 0 20px white;
}

#option1 {
  position: absolute;
  top: 390px;
  left: 5px;
  z-index: 10;
  Width: 5px;
  Height: 5px;
  background-color: green;
  border-radius: 50%;
  border-width: 0px;
  border-color: blue;
  border-style: solid;
  font-family: Verdana, Arial, Sans-Serif;
  color: white;
  box-shadow: 0 0 20px white;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<html>
  <body>
    <div id="maindiv">
      <canvas id="myCanvas1" width="500" height="400"></canvas>
      <div id="option1"></div>
    </div>
  </body>
</html>
Community
  • 1
  • 1
James Montagne
  • 77,516
  • 14
  • 110
  • 130
  • Thanks James, that works perfectly! Exactly what I was looking for. Next problem that occurs is that after about 25 steps the lines begin to run ahead of the `div` dot animations (showing more and more lines in the 'future' and the div-dots lagging behind). As the speed difference seem to differ per device (tried on several computers) I guess a `delay` is somehow handled differently than the time of an `animate`. I'll try to call the animates outside the `forloop` as you showed me with the lines, but I don't think that will help. Anyway, that's another topic. Thanks again! – Teun May 13 '15 at 12:20
  • @Teun I'm surprised they fall noticeably out of sync. You might consider calling the next `drawLine` in the callback to `animate` instead to force them to sync. – James Montagne May 13 '15 at 13:18