<template>
  <span>fps: {{fps}}, fps (min): {{fpsMin}}, fps (max): {{fpsMax}}</span>
  <div id="ball" :style="{ 'background-image': backgroundImage, top, left, height, width}"></div>
</template>

<style scoped lang="scss">
#ball
{ position:          absolute; 
  background-repeat: no-repeat;
  background-size:   cover;
}
</style>

<script>
const
  v_g = 981, // gravity
  
  // In order to avoid a scrollbar to appear if the ball
  // is placed too near the right or bottom border,
  // the stage is chosen to be one pixel smaller than the
  // window on each side.
  v_stage =
  { left:   1,
    right:  document.documentElement.clientWidth-1,
    top:    1,
    bottom: document.documentElement.clientHeight-1
  },
  
  v_time_format =
   new Intl.DateTimeFormat
       ( 'de-DE', 
         { hour: 'numeric', minute: 'numeric', second: 'numeric',
         }
       )
  ;

export default 
{ name: 'Ball',

  mounted()
  { let 
      l_last_timestamp = performance.now(),
      l_nr_of_frames   = 0;

    const
      f_step = 
      p_timestamp =>
      { const c_delta = (p_timestamp - l_last_timestamp)/1000;

        this.fps    = (Math.round(100/c_delta)/100).toFixed(2);
        if (++l_nr_of_frames > 10) // wait until the timer has settled
        { this.fpsMin = (this.fps < this.fpsMin) ?  this.fps : this.fpsMin;
          this.fpsMax = (this.fps > this.fpsMax) ?  this.fps : this.fpsMax;
        }

        this.update(c_delta);
        
        l_last_timestamp = p_timestamp;
        window.requestAnimationFrame(f_step);
      };
    
    window.requestAnimationFrame(f_step);
  },

  data()
  { return {
      d:        150, // diameter
      x:          1,
      y:          1,
      vx:       300,
      vy:         0,
      ax:         0,
      ay:         0,
      elasticity: 0.7, // super elastic ball (value 1.0)
                       // => no speed loss when it collides
                       // with a wall
      stopped:    false,

      fps:        0,
      fpsMin:    60,
      fpsMax:     0,

      'backgroundImage': `url(${require('/img/owl-150.png')})`, 
      get left()   { return this.x + 'px' },
      get top()    { return this.y + 'px' },
      get width()  { return this.d + 'px' },
      get height() { return this.d + 'px' },
    }
  },

  methods:
  { update(p_delta_s = 1/60)
    { if (this.stopped)
      { return; }
      
      // stop a slow movement totally
      // (which is more realistic due to friction)
      if (this.y >= v_stage.bottom-this.d-2 && Math.abs(this.vy) < 50)
      { this.stopped = true;
        this.y = v_stage.bottom-this.d;
        return;
      }
      
      this. x += this.vx * p_delta_s;
      this. y += this.vy * p_delta_s;
      
      this.vx += this.ax       * p_delta_s;
      this.vy += (this.ay+v_g) * p_delta_s;
      
      if (this.x <= v_stage.left)
      { this. x = v_stage.left;
        this.vx = this.elasticity*Math.abs(this.vx);
        this.ax = Math.abs(this.ax);
      }
      if (this.x >= v_stage.right-this.d)
      { this. x = v_stage.right-this.d;
        this.vx = -this.elasticity*Math.abs(this.vx);
        this.ax = -Math.abs(this.ax);
      }
      
      if (this.y <= v_stage.top)
      { this. y = v_stage.top;
        this.vy = this.elasticity*Math.abs(this.vy);
        this.ay = Math.abs(this.ay);
      }
      if ( this.y >= v_stage.bottom-this.d)
      { this. y = v_stage.bottom-this.d;
        this.vy = -this.elasticity*Math.abs(this.vy);
        this.ay = -Math.abs(this.ay);
      }
    }  
  }
}
</script>

