0

I would like to capture the peak Y position of a bounce. So every time the ball bounces I would like to capture the highest Y axis value that it achieves on that bounce.

So I know that the first value is the starting Y position of the ball. However I'm unsure on how to capture the subsequent values.

*** Please run the example in full screen

'use strict';

// Todo
// - Make the ball spin
// - Make the ball squish
// - Add speed lines
// - Clear only the ball not the whole canvas


(function () {

  const canvas = document.getElementsByClassName('canvas')[0],
        c = canvas.getContext('2d');


  // -----------------------------------
  // Resize the canvas to be full screen
  // -----------------------------------

  window.addEventListener('resize', resizeCanvas, false);

  function resizeCanvas() {
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;

    // ---------
    // Variables
    // ---------

    var circleRadius = 40,
        circleHeight = circleRadius * 2,
        x = (canvas.width/2) - circleRadius, // inital x position of the ball
        y = (canvas.height/2) - circleRadius, // inital y position of the ball
        fallHeight = y,
        vx = 0, // velocity
        vy = 0, // velocity
        groundHeight = circleHeight,
        bouncePoints = [],
        gravity = 0.8,
        dampening = 0.5,
        pullStrength = 0.04,
        segments = 4,
        bezieCircleFormula = (4/3)*Math.tan(Math.PI/(2*segments)), // http://stackoverflow.com/a/27863181/2040509
        pointOffset = {
          positive: bezieCircleFormula*circleRadius,
          negative: circleRadius-(bezieCircleFormula*circleRadius)
        },
        // Each side has 3 points, bezier 1, circle point, bezier 2
        // These are listed below in clockwise order.
        // So top has: left bezier, circle point, right bezier
        // Right has: top bezier, circle point, bottom bezier
        circlePoints = {
          top: [
            [x+pointOffset.negative, y],
            [x+circleRadius, y],
            [x+pointOffset.positive+circleRadius, y]
          ],
          right: [
            [x+circleHeight, y+pointOffset.negative],
            [x+circleHeight, y+circleRadius],
            [x+circleHeight, y+pointOffset.positive+circleRadius]
          ],
          bottom: [
            [x+pointOffset.positive+circleRadius, y+circleHeight],
            [x+circleRadius, y+circleHeight],
            [x+pointOffset.negative, y+circleHeight]
          ],
          left: [
            [x, y+pointOffset.positive+circleRadius],
            [x, y+circleRadius],
            [x, y+pointOffset.negative]
          ]
        };



    // --------------------
    // Ball squish function
    // --------------------
    // For `side` you can pass `top`, `right`, `bottom`, `left`
    // For `amount` use an interger

    function squish (side, squishAmount) {
      for (let i = 0; i < circlePoints[side].length; i++) {
        if (side === 'top') {
          circlePoints[side][i][1] += squishAmount;
        } else if (side === 'right') {
          circlePoints[side][i][0] -= squishAmount;
        } else if (side === 'bottom') {
          circlePoints[side][i][1] -= squishAmount;
        } else if (side === 'left') {
          circlePoints[side][i][0] += squishAmount;
        }
      }
    }

    var drop = 1;

    console.log('drop ' + drop + ': ' + y);


    // ------------------
    // Animation Function
    // ------------------

    function render () {

      // Clear the canvas
      c.clearRect(0, 0, canvas.width, canvas.height);



      // -----------------
      // Draw the elements
      // -----------------

      // Ground
      c.beginPath();
      c.fillStyle = '#9cccc8';
      c.fillRect(0, canvas.height - groundHeight, canvas.width, groundHeight);
      c.closePath();

      // Shadow
      let distanceFromGround = parseFloat(((y - canvas.height/2) + circleHeight) / (canvas.height/2 - groundHeight/2)).toFixed(4),
          shadowWidth = circleRadius * (1-distanceFromGround+1),
          shadowHeight = circleRadius/6 * (1-distanceFromGround+1),
          shadowX = (x + circleRadius) - shadowWidth/2,
          shadowY = canvas.height - groundHeight/2,
          shadowOpacity = 0.15 * distanceFromGround; // The first value here represents the opacity that will be used when the ball is touching the ground

      c.beginPath();
      c.fillStyle = 'rgba(0,0,0, ' + shadowOpacity + ')';
      c.moveTo(shadowX, shadowY);
      c.bezierCurveTo(shadowX, shadowY - shadowHeight, shadowX + shadowWidth, shadowY - shadowHeight, shadowX + shadowWidth, shadowY);
      c.bezierCurveTo(shadowX + shadowWidth, shadowY + shadowHeight, shadowX, shadowY + shadowHeight, shadowX, shadowY);
      c.fill();
      c.closePath();

      // Bezier circle
      c.beginPath();
      c.fillStyle = '#cf2264';
      c.moveTo(circlePoints.left[1][0], circlePoints.left[1][1]);
      c.bezierCurveTo(circlePoints.left[2][0], circlePoints.left[2][1], circlePoints.top[0][0], circlePoints.top[0][1], circlePoints.top[1][0], circlePoints.top[1][1]);
      c.bezierCurveTo(circlePoints.top[2][0], circlePoints.top[2][1], circlePoints.right[0][0], circlePoints.right[0][1], circlePoints.right[1][0], circlePoints.right[1][1]);
      c.bezierCurveTo(circlePoints.right[2][0], circlePoints.right[2][1], circlePoints.bottom[0][0], circlePoints.bottom[0][1], circlePoints.bottom[1][0], circlePoints.bottom[1][1]);
      c.bezierCurveTo(circlePoints.bottom[2][0], circlePoints.bottom[2][1], circlePoints.left[0][0], circlePoints.left[0][1], circlePoints.left[1][0], circlePoints.left[1][1]);
      c.stroke();
      c.closePath();



      // -------------------------------
      // Recalculate circle co-ordinates
      // -------------------------------

      circlePoints = {
        top: [
          [x+pointOffset.negative, y],
          [x+circleRadius, y],
          [x+pointOffset.positive+circleRadius, y]
        ],
        right: [
          [x+circleHeight, y+pointOffset.negative],
          [x+circleHeight, y+circleRadius],
          [x+circleHeight, y+pointOffset.positive+circleRadius]
        ],
        bottom: [
          [x+pointOffset.positive+circleRadius, y+circleHeight],
          [x+circleRadius, y+circleHeight],
          [x+pointOffset.negative, y+circleHeight]
        ],
        left: [
          [x, y+pointOffset.positive+circleRadius],
          [x, y+circleRadius],
          [x, y+pointOffset.negative]
        ]
      };



      // -----------------
      // Animation Gravity
      // -----------------


      // Increment gravity
      vy += gravity;

      // Increment velocity
      y += vy;
      x += vx;



      // ----------
      // Boundaries
      // ----------

      // Bottom boundary
      if (y + circleHeight > canvas.height - groundHeight/2) {
        y = canvas.height - groundHeight/2 - circleHeight;
        vy *= -1;

        // Dampening
        vy *= dampening;
        vx *= dampening;

        // If the Y velocity is less than the value below, stop the ball
        if (vy > -2.4) {
          dampening = 0;
        }

        fallHeight = fallHeight*dampening;

        if (drop < 5) {
          drop++;
          console.log('drop ' + drop + ': ' + y);
        }
      }

      // Right boundary
      if (x + circleHeight > canvas.width) {
        x = canvas.width - circleHeight;
        vx *= -1;

        // Dampening
        vy *= dampening;
        vx *= dampening;
      }

      // Left boundary
      if (x + circleHeight < 0 + circleHeight) {
        x = 0;
        vx *= -1;

        // Dampening
        vy *= dampening;
        vx *= dampening;
      }

      // Top boundary
      if (y < 0) {
        y = 0;
        vy *= -1;

        // Dampening
        vy *= dampening;
        vx *= dampening;
      }

      requestAnimationFrame(render);
    }



    // -----------
    // Click event
    // -----------

    canvas.addEventListener('mousedown', function (e) {
      let dx = e.pageX - x,
          dy = e.pageY - y;

      if (dampening === 0) {
        dampening = 0.5;
      }

      vx += dx * pullStrength;
      vy += dy * pullStrength;

    });

    render();

  }
  resizeCanvas();

})();
body{
  margin: 0;
}

canvas {
  background: #ddf6f5;
  display: block;
}
<canvas class="canvas"></canvas>
MarioD
  • 1,703
  • 1
  • 14
  • 24
  • 1
    awesome piece of JS, but it's absolutely not clear what is your question – smnbbrv Apr 01 '16 at 16:45
  • Sorry about that, I updated the question with a little more clarity: So every time the ball bounces I would like to capture the highest Y axis value that it achieves on that bounce. – MarioD Apr 01 '16 at 16:52
  • this isn't your code, is it? :) – smnbbrv Apr 01 '16 at 16:53
  • Wow, if you click in the window, it moves the ball. If you click as the ball hits the floor, it bounces higher. like dribbling. Cool code. – Jonathan M Apr 01 '16 at 16:54
  • @JonathanM it definitely is, but looks like the guy wants to use it without understanding of how it works ;) – smnbbrv Apr 01 '16 at 16:55
  • Wrote it all from scratch, how come? – MarioD Apr 01 '16 at 16:55
  • That's a little judgemental. I know what to do, I'm just unsure of how to go about it. I can log the Y value of each frame. Then I need to check whether the values are increasing. Once they are no longer increasing I know what my peak value is. – MarioD Apr 01 '16 at 16:59

1 Answers1

2

Pseudo-code:

// begin with an overly large seed for the minimum Y value
var minY=1000000;

// whenever the circle changes position, save the minimum of minY & currentY
minY=Math.min(minY,currentY);

If you want the top of the circle's circumference then subtract circleRadius

markE
  • 102,905
  • 11
  • 164
  • 176
  • 2
    +1 Just a trivial point when doing min and max. A better seed to use in Javascript is `var min = Infinity;` and for maximums `var max = -Infinity;` though when you have clear limits it it makes no difference and is a coding style choice. – Blindman67 Apr 01 '16 at 19:25
  • @Blindman67. You make a good style point because "Infinity" makes it clear that the value is intended as an extreme. :-) – markE Apr 01 '16 at 20:03