1

I'm new to Javascript and canvas. I'm working on a simple canvas race game that randomly sets the speed two rectangles (Red & Blue). I have it set so they both stop at a certain point. When one car wins, it prints out "Red/Blue Won!" on the canvas.

I have the racing part down, but I'm stuck on figuring out how to run the conditional statement, where to put it, and have it print out when 1 rectangle has reached the end of the race/ certain point first.

 if (square1 gets passed finish line first or stops moving)  {
    // Draw to canvas 
    context.font = "20pt sans-serif";
    context.fillText("Red is The Winner!", 5, 25, 300);
    context.fillStyle = "red";
  }
else if (square2 gets passed finish line first or stops moving) {

    context.font = "20pt sans-serif";
    context.fillText("Blue is The Winner!", 5, 280, 300);
    context.fillStyle = "blue";
}

I'm not sure if its the way I have it animated using time. Any help would be much appreciated.

Here is the animation code

  var animateRed = function(prop, val, duration) {
    // The calculations required for the step function
    var start = new Date().getTime();
    var end = start + duration;
    var current = square1[prop];
    var distance = val - current;

    var step = function() {
      // Get our current progres
      var timestamp = new Date().getTime();
      var progress = Math.min((duration - (end - timestamp)) / duration, 1);


      // Update the square's property
      square1[prop] = current + (distance * progress);
      // If the animation hasn't finished, repeat the step.
      if (progress < 1) requestAnimationFrame(step);
    };

    // Start the animation
    return step();
  };

  var animateBlue = function(prop, val, duration) {
    // The calculations required for the step function
    var start = new Date().getTime();
    var end = start + duration;
    var current = square2[prop];
    var distance = val - current;

    var step = function() {
      // Get our current progres
      var timestamp = new Date().getTime();
      var progress = Math.min((duration - (end - timestamp)) / duration, 1);


      // Update the square's property
      square2[prop] = current + (distance * progress);
      // If the animation hasn't finished, repeat the step.
      if (progress < 1) requestAnimationFrame(step);
    };

    // Start the animation
    return step();
  };


  $("#go").on('click', function() {
    //Math.floor(Math.random() * 1000) + 100;  
    var speedRed = Math.floor(Math.random() * 1000) + 500;
    var speedBlue = Math.floor(Math.random() * 1000) + 500;
    animateRed('x', 450, speedRed);
    animateBlue('x', 450, speedBlue);
  });

************** Updated Fiddle ******************** accidentally posted wrong one

Here is my fiddle of it: Fiddle

Gorgon_Union
  • 563
  • 2
  • 8
  • 24

1 Answers1

1

You can use Promise.race()

var animateRed = function(prop, val, duration) {
        // The calculations required for the step function
        return new Promise(function(resolve) {
          var start = new Date().getTime();
          var end = start + duration;
          var current = square1[prop];
          var distance = val - current;
          // var speedRed = Math.floor(Math.random() * (90 - 20) + 20);
          //Math.floor(Math.random() * 2);


          var step = function() {
            // Get our current progres
            var timestamp = new Date().getTime();
            var progress = Math.min((duration - (end - timestamp)) / duration, 1);
            // Update the square's property
            square1[prop] = current + (distance * progress);
            // If the animation hasn't finished, repeat the step.
            if (progress < 1) {
              requestAnimationFrame(step)
            } else {
              resolve("red")
            }
          };

          // Start the animation
          step();
        })
      };

      var animateBlue = function(prop, val, duration) {
        // The calculations required for the step function
        return new Promise(function(resolve) {
          var start = new Date().getTime();
          var end = start + duration;
          var current = square2[prop];
          var distance = val - current;
          // var speedBlue = Math.floor(Math.random() * (90 - 20) + 20);

          var step = function() {
            // Get our current progres
            var timestamp = new Date().getTime();
            var progress = Math.min((duration - (end - timestamp)) / duration, 1);
            // Update the square's property
            square2[prop] = current + (distance * progress);
            // If the animation hasn't finished, repeat the step.
            if (progress < 1) {
              requestAnimationFrame(step)
            } else {
              resolve("blue")
            }
          };

          // Start the animation
          step();
        })
      };

      Promise.race([animateRed('x', 450, speedRed),
          animateBlue('x', 450, speedBlue)
        ])
        .then(function(winner) {
          alert(winner + " wins")
        });

var canvas = document.getElementById('canvas');
var goBtn = document.getElementById('go');
goBtn.addEventListener('click', render, false);

if (canvas.getContext) {
  // Grab our context
  var context = canvas.getContext('2d');

  // Make sure we have a valid defintion of requestAnimationFrame
  var requestAnimationFrame =
    window.requestAnimationFrame ||
    window.webkitRequestAnimationFrame ||
    window.mozRequestAnimationFrame ||
    window.msRequestAnimationFrame ||
    window.oRequestAnimationFrame ||
    function(callback) {
      return setTimeout(callback, 16);
    };

  // Let's define our square
  var square1 = {
    'x': 0,
    'y': 50,
    'width': 50,
    'height': 50,
    'fill': '#FF0000'
  };

  // Let's define our square
  var square2 = {
    'x': 0,
    'y': 120,
    'width': 50,
    'height': 50,
    'fill': '#4169E1'
  };

  var render = function() {
    // Clear the canvas
    context.clearRect(0, 0, canvas.width, canvas.height);

    // Draw the square
    context.beginPath();
    context.rect(square1.x, square1.y, square1.width, square1.height);
    context.fillStyle = square1.fill;
    context.fill();

    // Draw the square
    context.beginPath();
    context.rect(square2.x, square2.y, square2.width, square2.height);
    context.fillStyle = square2.fill;
    context.fill();

    // Finish Line
    context.beginPath();
    context.strokeStyle = 'black';
    context.moveTo(canvas.width - 110, 0);
    context.lineTo(canvas.width - 110, 290);
    context.globalCompositeOperation = "destination-over";
    context.lineWidth = 10;
    context.stroke();

    /*
        context.font = "20pt sans-serif";
        context.fillText("Red is The Winner!", 5, 25, 300);
        context.fillStyle = '#FF0000';


        context.font = "20pt sans-serif";
        context.fillText("Blue is The Winner!", 5, 280, 300);
        context.fillStyle = "red";
    */
    // Redraw
    requestAnimationFrame(render);
  };

  // Start the redrawing process
  render();

  var animateRed = function(prop, val, duration) {
    // The calculations required for the step function
    return new Promise(function(resolve) {
      var start = new Date().getTime();
      var end = start + duration;
      var current = square1[prop];
      var distance = val - current;
      // var speedRed = Math.floor(Math.random() * (90 - 20) + 20);
      //Math.floor(Math.random() * 2);


      var step = function() {
        // Get our current progres
        var timestamp = new Date().getTime();
        var progress = Math.min((duration - (end - timestamp)) / duration, 1);
        // Update the square's property
        square1[prop] = current + (distance * progress);
        // If the animation hasn't finished, repeat the step.
        if (progress < 1) {
          requestAnimationFrame(step)
        } else {
          resolve("red")
        }
      };

      // Start the animation
      step();
    })
  };

  var animateBlue = function(prop, val, duration) {
    // The calculations required for the step function
    return new Promise(function(resolve) {
      var start = new Date().getTime();
      var end = start + duration;
      var current = square2[prop];
      var distance = val - current;
      // var speedBlue = Math.floor(Math.random() * (90 - 20) + 20);

      var step = function() {
        // Get our current progres
        var timestamp = new Date().getTime();
        var progress = Math.min((duration - (end - timestamp)) / duration, 1);
        // Update the square's property
        square2[prop] = current + (distance * progress);
        // If the animation hasn't finished, repeat the step.
        if (progress < 1) {
          requestAnimationFrame(step)
        } else {
          resolve("blue")
        }
      };

      // Start the animation
      step();
    })
  };

  if (animateRed() < animateBlue()) {
    context.font = "20pt sans-serif";
    context.fillText("Red is The Winner!", 5, 25, 300);
    context.fillStyle = '#FF0000';
  }

  //Math.floor(Math.random() * 1000) + 100;  
  var speedRed = Math.floor(Math.random() * 1000) + 500;
  var speedBlue = Math.floor(Math.random() * 1000) + 500;
  Promise.race([animateRed('x', 450, speedRed),
      animateBlue('x', 450, speedBlue)
    ])
    .then(function(winner) {
      alert(winner + " wins")
    });

};
canvas {
  border: 1px solid black;
}
<canvas width='500' height='300' id='canvas'>Your browser does not support canvas - go get Chrome!</canvas>

<button class="goBtn" id="go">Go</button>

jsfiddle https://jsfiddle.net/py49rzbu/

guest271314
  • 1
  • 15
  • 104
  • 177
  • 1
    See [`Promise.race()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/race) – guest271314 May 18 '16 at 04:14
  • Sorry, I accidentally posted the wrong fiddle. I updated it with the correct one. Anyway, Is there a way to use promise.race() to get the winner to draw out "Red/Blue Wins" inside the canvas? I need to have it so instead of an alert it draws ```context.font = "20pt sans-serif"; context.fillText("Red is The Winner!", 5, 25, 300);``` inside the canvas. – Gorgon_Union May 18 '16 at 04:18
  • 1
    @Gorgon_Union You can create a variable `complete` initially ste to `false`; when `Promise.race()` returns a value, set to `true`. Within `render` use `if` at `if (complete === false) { context.clearRect(0, 0, canvas.width, canvas.height); }` and `if (complete === false) { requestAnimationFrame(render); }`; also to adjust `fillStyle` color, place before `.fillText` , see http://jsfiddle.net/gr2fhhxs/4/ – guest271314 May 18 '16 at 04:37
  • Thats what I was looking for. I didn't think about setting a boolean value to allow the font draw to render. Also, I've seen this before but not sure if I understand whats going on ```context.fillStyle = winner === "Red" ? "red" : "blue";``` is this saying ```winner(red or blue) === Red / red or blue```? I tried googling the syntax but don't get anything. The ```Promise``` methods are new to me, but will definitely be of benefit for future projects. Thanks for your help. – Gorgon_Union May 18 '16 at 04:59
  • See [How to use the ?: (ternary) operator in JavaScript](http://stackoverflow.com/questions/6259982/how-to-use-the-ternary-operator-in-javascript) , https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_Operator – guest271314 May 18 '16 at 05:16
  • Not sure if you might have ever experienced something like this, but would the ```Promise.race```, with how the animation is set, cause pictures to blur when it's resolved like [this](http://i.imgur.com/U9NI7B9.png) ? This seems to be a new problem now that I'm using it. I'm not sure if its just how it resolves the promise so it creates another image of the animation frame perhaps. – Gorgon_Union May 18 '16 at 06:34
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/112229/discussion-between-gorgon-union-and-guest271314). – Gorgon_Union May 18 '16 at 06:37