<!DOCTYPE html>
<html>

  <head>
    <link rel="stylesheet" href="style.css">
    <script src="cropper.js"></script>
    <script src="script.js"></script>
  </head>

  <body>
    <h2>Cropper</h2>
    <div id="cropper">
      <img id="srcImage" src="http://placehold.it/350x350"/>
    </div>
    <button id="doCrop">Crop</button><br>
    <button id="reset">Reset</button>

  </body>

</html>
var cropCanvas, cropContext, cropping;
document.addEventListener('DOMContentLoaded', function(){

  var crop = new cropper('cropper', 3/4);
  
  document.getElementById('reset').addEventListener('click', function() {
    crop.reset();
  });
  
  document.getElementById('doCrop').addEventListener('click', function() {
    crop.getState();
  });
  
});

/* Styles go here */

#srcImage {display: none;}
#doCrop {clear: both;}
#cropper {height: 350px;}
var cropper = function(container, aspect) {
  this.container = document.getElementById(container);
  if (aspect) this.aspect = aspect;

  this.init();
  return this;
};

cropper.prototype = {
  cropperContext: null,
  cropping: null,
  state: {
    x: 0,
    y: 0,
    x2: 100,
    y2: 100,
    ref: {
      x: 0,
      y: 0,
      x2: 100,
      y2: 100
    }
  },
  action: null,
  init: function() {
    this.img = this.container.getElementsByTagName('img')[0];
    this.container.style.position = 'relative';
    this.img.style.display = 'none';
    this.imageCanvas = document.createElement('canvas');
    this.imageCanvas.width = this.img.width;
    this.imageCanvas.height = this.img.height;
    this.imageCanvas.style.position = 'absolute';
    this.imageCanvas.style.zIndex = 0;
    this.imageContext = this.imageCanvas.getContext("2d");
    this.imageContext.drawImage(this.img, 0, 0);
    this.container.appendChild(this.imageCanvas);

    this.cropperCanvas = document.createElement('canvas');
    this.cropperCanvas.width = this.img.width;
    this.cropperCanvas.height = this.img.height;
    this.cropperCanvas.style.position = 'absolute';
    this.cropperCanvas.style.zIndex = 1;
    this.cropperContext = this.cropperCanvas.getContext("2d");
    this.container.appendChild(this.cropperCanvas);
    this.cropRect(this.cropperContext, this.state.x, this.state.y, this.state.x2, this.state.y2);
    var self = this;

    this.cropperCanvas.addEventListener('mousedown', function(evt) {
      var e = self.getMousePos(self.cropperCanvas, evt);
      self.cropping = true;
      self.state.ref.x = e.x;
      self.state.ref.y = e.y;
      if (Math.abs(self.state.x - e.x) <= 15 && Math.abs(self.state.y - e.y) <= 15) {
        self.action = 'ul';
        self.cropperCanvas.style.cursor = 'nw-resize';
      } else if (Math.abs(self.state.x2 - e.x) <= 15 && Math.abs(self.state.y - e.y) <= 15) {
        self.action = 'ur';
        self.cropperCanvas.style.cursor = 'ne-resize';
      } else if (Math.abs(self.state.x2 - e.x) <= 15 && Math.abs(self.state.y2 - e.y) <= 15) {
        self.action = 'lr';
        self.cropperCanvas.style.cursor = 'se-resize';
      } else if (Math.abs(self.state.x - e.x) <= 15 && Math.abs(self.state.y2 - e.y) <= 15) {
        self.action = 'll';
        self.cropperCanvas.style.cursor = 'sw-resize';
      } else {
        self.action = 'drag';
        self.cropperCanvas.style.cursor = 'move';
      }
    });

    this.cropperCanvas.addEventListener('mouseup', function(e) {
      self.cropping = false;
      self.cropperCanvas.style.cursor = 'default';
    });

    this.cropperCanvas.addEventListener('mousemove', function(evt) {
      var e = self.getMousePos(self.cropperCanvas, evt);
      if (self.cropping) {
        if (self.action == 'ul') {
          var newX = self.state.x + (e.x - self.state.ref.x);
          var newY = self.aspect ? self.state.y2 - (self.state.x2 - newX) / self.aspect : self.state.y + (e.y - self.state.ref.y);
          self.cropRect(self.cropperContext, newX, newY, self.state.x2, self.state.y2);
          self.state.x = newX;
          self.state.y = newY;
          self.state.ref.x = e.x;
          self.state.ref.y = e.y;
        } else if (self.action == 'ur') {
          var newX = self.state.x2 + (e.x - self.state.ref.x);
          var newY = self.aspect ? self.state.y2 - (newX - self.state.x) / self.aspect : self.state.y + (e.y - self.state.ref.y);
          self.cropRect(self.cropperContext, self.state.x, newY, newX, self.state.y2);
          self.state.x2 = newX;
          self.state.y = newY;
          self.state.ref.x = e.x;
          self.state.ref.y = e.y;
        } else if (self.action == 'lr') {
          var newX = self.state.x2 + (e.x - self.state.ref.x);
          var newY = self.aspect ? self.state.y + (newX - self.state.x) / self.aspect : self.state.y2 + (e.y - self.state.ref.y);
          self.cropRect(self.cropperContext, self.state.x, self.state.y, newX, newY);
          self.state.x2 = newX;
          self.state.y2 = newY;
          self.state.ref.x = e.x;
          self.state.ref.y = e.y;
        } else if (self.action == 'll') {
          var newX = self.state.x + (e.x - self.state.ref.x);
          var newY = self.aspect ? self.state.y + (self.state.x2 - newX) / self.aspect : self.state.y2 + (e.y - self.state.ref.y);
          self.cropRect(self.cropperContext, newX, self.state.y, self.state.x2, newY);
          self.state.x = newX;
          self.state.y2 = newY;
          self.state.ref.x = e.x;
          self.state.ref.y = e.y;
        } else {
          // drag
          var newX = self.state.x + (e.x - self.state.ref.x);
          var newY = self.state.y + (e.y - self.state.ref.y);
          var newX2 = newX + (self.state.x2 - self.state.x);
          var newY2 = newY + (self.state.y2 - self.state.y);
          self.cropRect(self.cropperContext, newX, newY, newX2, newY2);
          self.state.x = newX;
          self.state.y = newY;
          self.state.ref.x = e.x;
          self.state.ref.y = e.y;
          self.state.x2 = newX2;
          self.state.y2 = newY2;
        }
      }
    });
  },
  getMousePos: function(canvas, evt) {
    var rect = canvas.getBoundingClientRect();
    return {
      x: evt.clientX - rect.left,
      y: evt.clientY - rect.top
    };
  },
  cropRect: function(ctx, x, y, x2, y2) {
    ctx.clearRect(0, 0, this.img.width, this.img.height);
    ctx.beginPath();
    ctx.lineWidth = "3";
    ctx.strokeStyle = "red";
    var w = Math.abs(x - x2);
    var h = Math.abs(y - y2);
    ctx.rect(x, y, w, h);
    ctx.stroke();
  },
  getState: function() {
    var state = {
      x: this.state.x,
      y: this.state.y,
      x2: this.state.x2,
      y2: this.state.y2,
      w: this.state.x2 - this.state.x,
      h: this.state.y2 - this.state.y
    };

    this.imageContext.clearRect(0, 0, this.img.width, this.img.height);
    this.cropperContext.clearRect(0, 0, this.img.width, this.img.height);
    this.imageContext.drawImage(this.img, state.x, state.y, state.w, state.h,
      0, 0, state.w, state.h);
    return state;
  },
  reset: function() {
    this.imageContext.clearRect(0, 0, this.img.width, this.img.height);
    this.init();
  }

};