<div class="bird"></div>
<div class="cat"></div>
body
{ 
  margin: 0; 
  overflow: hidden;
  background-color: #98d0e1;
  background-repeat: repeat-x;
  background-position: bottom;
  background-image: url('http://cjonasw.files.wordpress.com/2014/03/angry-birds-backdrop.png');
}

.cat
{
  position: absolute;
  width: 69px;
  height: 200px;
  background-repeat: no-repeat;
  background-image: url("http://cjonasw.files.wordpress.com/2014/03/angry-birds-catapult.png");
  background-size: 100% auto;
}

.cat-sling
{
  stroke: #220f0b;
  stroke-width:10;
}

.cat:after
{
  content: "";
  position: absolute;
  z-index: 2000;
  width: 69px;
  height: 200px;
  background-repeat: no-repeat;
  background-image: url("http://cjonasw.files.wordpress.com/2014/03/angry-birds-catapult-foreground.png");
  background-size: 100% auto;
  pointer-events: none;
}

.bird
{
  z-index: 2;
  background: #ddd;
  width: 68px;
  height: 60px;
  position: absolute;
  cursor: pointer;
  background: url("http://cjonasw.files.wordpress.com/2014/03/angry_birds_red.png");
  
  /* idle */
  background-position: 0 0;
  /* blink 
  background-position: 0 -60px;*/
  /* flung
  background-position: 0 -120px;*/
  /* hit
  background-position: 0 -180px;*/
}
var // Numbers
    g                           = .15,
    wind_resistance             = .0001,
    angle                       = 0,
    rolling_additional_angle    = 0,
    floor_friction              = .1,
    sprite_pos                  = 0,
    
    // Coordinates
    cat_x,
    cat_y,
    vel_x,
    vel_y,
    bird_x,
    bird_y,

    // States
    bird_grabbed    = false,
    flung           = false,
    hit_wall        = false,
    reset_called    = false,
    hit_floor       = false,
    
    // Directional understanding
    right_fling     = false,
    flip            = false,

    // Animation
    blinking        = false,
    churping        = false,
     
    // Elements
    $_bird = $('.bird'),
    
    // Functions
    reset = function(){
      
      // Set birds position
      bird_x                    = ($(window).width()/5);
      bird_y                    = $(window).height() - 245;
      
      // Set catapults position
      cat_x                     = bird_x;
      cat_y                     = bird_y;
      
      // Set birds velocity to 0
      vel_x                     = 0;
      vel_y                     = 0;

      // Set/Reset states
      flung                     = false;
      hit_wall                  = false;
      reset_called              = false;
      hit_floor                 = false;
      
      // Set angles
      rolling_additional_angle  = 0;
      angle                     = 0;
  
      // Set the cursor css property to be pointer (shows its interactive, changes once flung)
      $('.bird').css('cursor','pointer');
      
      $('.cat').css({
        
        left : cat_x,
        top : cat_y
        
      });
    };

// Stretch the document body height
$('body').css('height', $(window).height());

// Set the game.
reset();

// Events
$_bird.mousedown(function(){
  
  if(!flung){

    bird_grabbed = true;
    $('.bird').css('cursor','grab');
  }
  
  
});

$('body').mousemove(function(e){
  
  if(bird_grabbed){
    
    var dist_y = cat_y - bird_y,
        dist_x = cat_x - bird_x;

    // Get bird to follow cursor
    bird_x = e.clientX - ($_bird.width()/2);
    bird_y = e.clientY - ($_bird.height()/2);
    
    // Floor "collision"
    if(bird_y > $('body').height() - $_bird.height() - 45)
    {
      bird_y = $('body').height() - $_bird.height() - 45;
    }
    
    // Catapult "collision"
    if(dist_x < 30 && dist_y < -45 && right_fling)
      bird_x = cat_x - 30;
    
    if(Math.abs(dist_x) < 36 && dist_y < -45 && !right_fling)
      bird_x = cat_x + 36;
    
      // Trigonometry - ATAN(adjacent/opposite)
    angle = Math.atan(dist_x/dist_y)

            // Radians to Degrees
            * (180/Math.PI);
    
    angle = 270 - angle;
    
    if(angle < 270 && angle > 90){
      angle -= 180;
    }
    
    // Work out which side its on
    if(bird_x > cat_x){
      flip = true;
    }else{
      flip = false;
    }
    
    // Positive if distance is a positive number or 0
    right_fling = dist_x >= 0;
    
  }
  
});

$('body').mouseup(function(e){
  
  // If bird was grabbed when mouse is being released perform a fling!
  if(bird_grabbed){ 
    
    vel_x = (cat_x - bird_x)/10;
    vel_y = (cat_y - bird_y)/10;
    flung = true;
  }
  
  bird_grabbed = false;
  
  // Make visually non interactable
  $('.bird').css('cursor','default');
  
});

// "Gameloop"
setInterval(function(){
  
  // If bird has been flung
  if(!bird_grabbed && flung){
    
    // Apply the birds y velocity to the birds x position
    bird_x += vel_x;
    
    // Left screen edge "collision"
    if(bird_x < 0)
    {
      bird_x = 0;
      hit_wall = true;
    }
    
    // Right screen edge "collision"      
    if(bird_x > $('body').width() - $_bird.width())
    {
      bird_x = $('body').width() - $_bird.width();
      hit_wall = true;
    }
    
    // Apply the birds y velocity to the birds y position
    bird_y += vel_y;
    
    // Floor "collision"
    if(bird_y >= $('body').height() - $_bird.height() - 45)
    {
      hit_floor = true;

      bird_y = $('body').height() - $_bird.height() - 45;
      
      // If hasnt hit a wall yet apply floor friction to slow down bird
      if(!hit_wall)
        vel_x -= floor_friction * (!right_fling ? -1 : 1);
      
      // If still moving
      if(((vel_x > 0 && right_fling) || (vel_x < 0 && !right_fling)) && !hit_wall)
      {
        // Add/Minus the rolling additional angle, to create rolling effect
        if(right_fling)
          rolling_additional_angle += (vel_x/(floor_friction*10));
        
        if(!right_fling)
          rolling_additional_angle -= ((vel_x*-1)/(floor_friction*10));
      }

      // Bounce maybe in t' future.
    }
    else
    {
      // Apply gravity
      vel_y += g;
    }  
    
    // Apply wind resisitance if hasnt hit a wall yet
    if(!hit_wall)
      vel_x -= wind_resistance * (!right_fling ? -1 : 1);
    
    // Stops any Bird "reversing" happening (movement)
    if(right_fling){
      
      if(vel_x < 0){
        
        vel_x = 0;
      }
      
    }else{
    
      if(vel_x > 0){
        
        vel_x = 0;
      }
    }
        
    // Trigonometry - ATAN(adjacent/opposite)
    angle = Math.atan(vel_x/vel_y)

            // Radians to Degrees
            * (180/Math.PI);

    angle = 270 - angle;
    
    // Have no idea why changing the 275 to 270 fixes it for left fling but not right...
    if(angle < (right_fling ? 275 : 270) && angle > 90){
      angle -= 180;
    }
    
    // Apply the rolling effect
    angle += rolling_additional_angle;
    
    // if the bird has stopped moving...
    if((vel_x == 0 || (hit_wall && bird_y == $('body').height() - $_bird.height() - 45)) && !reset_called){
      
      setTimeout(function(){
        
        // Reset the "Game"
        reset();

      }, 1000);
      
      // Allows "SetTimeout" to work within gameloop, means it only gets calleds onces which is what we wants, isnt it hobbitis.
      reset_called = true;
    }
    
    // Change sprite position to be the hurt bird if hit wall or floor.
    if(hit_floor || hit_wall){
      sprite_pos = -180;
    }else{
      sprite_pos = -120;
    }

  }else{
    
    // Animation

    // If bird is not churping or blinking
    if(!churping && !blinking){
      
      // Create random variable to chose animation from
      var anim_rand = Math.random();

      // 0.5% chance of blinking
      if(anim_rand > .995){

        blinking = true;

        // Only gets called once
        setTimeout(function(){

          blinking = false;

        }, 100);
      }

      // 0.1% chance, roughly, of churping
      if(anim_rand < .001){

        churping = true;

        setTimeout(function(){

          churping = false;

        }, 250);

      }
    }
    
    // Alter sprite positions accordingly
    sprite_pos = blinking ? -60 : (churping ? -242 : 0);
  }
  
  // Apply chances to the bird div
  $_bird.css({

    left                    : bird_x,
    top                     : bird_y,
                              
                              // Spritesheet is a bit pants, need to space them a bit more really..
    height                  : (sprite_pos == -180) ? 61 : 60,

    'transform'             : 'rotate('+angle+'deg)' + (flip ? ' scaleX(-1)' : ''),
    '-ms-transform'         : 'rotate('+angle+'deg)',
    '-webkit-transform'     : 'rotate('+angle+'deg)' + (flip ? ' scaleX(-1)' : ''),
    'background-position'   : "0 " + sprite_pos + "px"
  });

}, 12);

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. //cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js