97
jQuery('#carregar').click(function() {
  var canvas    = document.getElementById('canvas');
  var image   = document.getElementById('image');
  var element = canvas.getContext("2d");
  element.clearRect(0, 0, canvas.width, canvas.height);
  element.drawImage(image, 0, 0, 300, 300);
});

jsfiddle.net/braziel/nWyDE/

I have a problem to rotate an image 90 ° to the right or to the left.

I use an image on the canvas, the same screen will have several canvas equal to that of the example, but I left it as close as possible to the project.

I ask, how do I rotate the image 90 ° to the left or right when I click "Rotate Left" and "Rotate Right"?

I tried several codes on the internet but none worked.

Mosh Feu
  • 28,354
  • 16
  • 88
  • 135
Max William Vitorino
  • 1,200
  • 2
  • 11
  • 19

11 Answers11

185

You can use canvas’ context.translate & context.rotate to rotate your image

enter image description here

Here’s a function to draw an image that is rotated by the specified degrees:

function drawRotated(degrees){
    context.clearRect(0,0,canvas.width,canvas.height);

    // save the unrotated context of the canvas so we can restore it later
    // the alternative is to untranslate & unrotate after drawing
    context.save();

    // move to the center of the canvas
    context.translate(canvas.width/2,canvas.height/2);

    // rotate the canvas to the specified degrees
    context.rotate(degrees*Math.PI/180);

    // draw the image
    // since the context is rotated, the image will be rotated also
    context.drawImage(image,-image.width/2,-image.width/2);

    // we’re done with the rotating so restore the unrotated context
    context.restore();
}

Here is code and a Fiddle: http://jsfiddle.net/m1erickson/6ZsCz/

<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>

<style>
    body{ background-color: ivory; }
    canvas{border:1px solid red;}
</style>
    
<script>
$(function(){

    var canvas=document.getElementById("canvas");
    var ctx=canvas.getContext("2d");

    var angleInDegrees=0;

    var image=document.createElement("img");
    image.onload=function(){
        ctx.drawImage(image,canvas.width/2-image.width/2,canvas.height/2-image.width/2);
    }
    image.src="houseicon.png";

    $("#clockwise").click(function(){ 
        angleInDegrees+=30;
        drawRotated(angleInDegrees);
    });

    $("#counterclockwise").click(function(){ 
        angleInDegrees-=30;
        drawRotated(angleInDegrees);
    });

    function drawRotated(degrees){
        ctx.clearRect(0,0,canvas.width,canvas.height);
        ctx.save();
        ctx.translate(canvas.width/2,canvas.height/2);
        ctx.rotate(degrees*Math.PI/180);
        ctx.drawImage(image,-image.width/2,-image.width/2);
        ctx.restore();
    }

       
}); // end $(function(){});
</script>

</head>

<body>
    <canvas id="canvas" width=300 height=300></canvas><br>
    <button id="clockwise">Rotate right</button>
    <button id="counterclockwise">Rotate left</button>
</body>
</html>
Lee Taylor
  • 7,761
  • 16
  • 33
  • 49
markE
  • 102,905
  • 11
  • 164
  • 176
  • so what about putImageData( ) ? It doesn't seem to work – TheRealChx101 Apr 11 '14 at 06:25
  • Well, putImageData *would work*, but it's a much more expensive operation to do the same thing--so putImageData is not called for here! ;-) – markE Apr 11 '14 at 15:01
  • Appears you have a typo: ctx.drawImage(image,-image.width/2,-image.width/2); should be ctx.drawImage(image,-image.width/2,-image.height/2); – Steve Farthing Jul 02 '14 at 13:54
  • @SteveFarthing No typo...when rotated 90 degrees, the width becomes the height and the height becomes the width. ;-) – markE Jul 02 '14 at 14:40
  • 25
    This code works perfectly for square images. For rectangle images the image is cropped incorrectly (see http://jsfiddle.net/6ZsCz/803/ ). If you change ctx.drawImage(image,-image.width/2,-image.width/2); to be ctx.drawImage(image,-image.width/2,-image.height/2) then it works for all size images. – Steve Farthing Jul 02 '14 at 16:10
  • @SteveFarthing. Cropping is not "incorrect", it's just a design option--like overflow:hidden. :-) If your own design requires displaying the full rectangular image you could (1) increase the size of the canvas (2) decrease the size of your image when rotated at 90 or 270. Cheers! – markE Jul 02 '14 at 16:24
  • 11
    ok. Heres an example where the canvas fits to the image and parts of the image are not lost: http://jsfiddle.net/6ZsCz/804/ . – Steve Farthing Jul 02 '14 at 16:27
  • 1
    @SteveFarthing can you put this as an answer? It works flawlessly and I'm positive that anyone who looks for this functionality needs your 'corrected' solution – nicholaswmin Mar 29 '15 at 15:21
  • 2
    @markE : for the 3rd time this month you helped me out :-D – Dr. Gianluigi Zane Zanettini Jan 30 '16 at 12:05
  • Please check you fiddle, it doesn't work: `Uncaught DOMException: Failed to execute 'drawImage' on 'CanvasRenderingContext2D': The HTMLImageElement provided is in the 'broken' state.` – deathangel908 Jul 09 '17 at 20:44
  • Great job as always Mark! Updated JSFiddle with working image: https://jsfiddle.net/6ZsCz/3455/ – Ohad Schneider Nov 05 '17 at 11:49
65

Quickest 2D context image rotation method

A general purpose image rotation, position, and scale.

// no need to use save and restore between calls as it sets the transform rather 
// than multiply it like ctx.rotate ctx.translate ctx.scale and ctx.transform
// Also combining the scale and origin into the one call makes it quicker
// x,y position of image center
// scale scale of image
// rotation in radians.
function drawImage(image, x, y, scale, rotation){
    ctx.setTransform(scale, 0, 0, scale, x, y); // sets scale and origin
    ctx.rotate(rotation);
    ctx.drawImage(image, -image.width / 2, -image.height / 2);
} 

If you wish to control the rotation point use the next function

// same as above but cx and cy are the location of the point of rotation
// in image pixel coordinates
function drawImageCenter(image, x, y, cx, cy, scale, rotation){
    ctx.setTransform(scale, 0, 0, scale, x, y); // sets scale and origin
    ctx.rotate(rotation);
    ctx.drawImage(image, -cx, -cy);
} 

To reset the 2D context transform

ctx.setTransform(1,0,0,1,0,0); // which is much quicker than save and restore

Thus to rotate image to the left (anti clockwise) 90 deg

drawImage(image, canvas.width / 2, canvas.height / 2, 1, - Math.PI / 2);

Thus to rotate image to the right (clockwise) 90 deg

drawImage(image, canvas.width / 2, canvas.height / 2, 1, Math.PI / 2);

Example draw 500 images translated rotated scaled

var image = new Image;
image.src = "https://i.stack.imgur.com/C7qq2.png?s=328&g=1";
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
canvas.style.position = "absolute";
canvas.style.top = "0px";
canvas.style.left = "0px";
document.body.appendChild(canvas);
var w,h;
function resize(){ w = canvas.width = innerWidth; h = canvas.height = innerHeight;}
resize();
window.addEventListener("resize",resize);
function rand(min,max){return Math.random() * (max ?(max-min) : min) + (max ? min : 0) }
function DO(count,callback){ while (count--) { callback(count) } }
const sprites = [];
DO(500,()=>{
    sprites.push({
       x : rand(w), y : rand(h),
       xr : 0, yr : 0, // actual position of sprite
       r : rand(Math.PI * 2),
       scale : rand(0.1,0.25),
       dx : rand(-2,2), dy : rand(-2,2),
       dr : rand(-0.2,0.2),
    });
});
function drawImage(image, spr){
    ctx.setTransform(spr.scale, 0, 0, spr.scale, spr.xr, spr.yr); // sets scales and origin
    ctx.rotate(spr.r);
    ctx.drawImage(image, -image.width / 2, -image.height / 2);
}
function update(){
    var ihM,iwM;
    ctx.setTransform(1,0,0,1,0,0);
    ctx.clearRect(0,0,w,h);
    if(image.complete){
      var iw = image.width;
      var ih = image.height;
      for(var i = 0; i < sprites.length; i ++){
          var spr = sprites[i];
          spr.x += spr.dx;
          spr.y += spr.dy;
          spr.r += spr.dr;
          iwM = iw * spr.scale * 2 + w;
          ihM = ih * spr.scale * 2 + h;
          spr.xr = ((spr.x % iwM) + iwM) % iwM - iw * spr.scale;
          spr.yr = ((spr.y % ihM) + ihM) % ihM - ih * spr.scale;
          drawImage(image,spr);
      }
    }    
    requestAnimationFrame(update);
}
requestAnimationFrame(update);
Blindman67
  • 51,134
  • 11
  • 73
  • 136
  • 1
    Thank you - I'm happy - How fast is my computer is !?! – T4NK3R Aug 22 '18 at 07:26
  • @Blindman67 Thanks for the great solution! Would you please kindly explain what's being done in the following lines? I suck at the math part :/ - iwM = iw * spr.scale * 2 + w; ihM = ih * spr.scale * 2 + h; spr.xr = ((spr.x % iwM) + iwM) % iwM - iw * spr.scale; spr.yr = ((spr.y % ihM) + ihM) % ihM - ih * spr.scale; – zumek Jan 12 '19 at 02:45
  • 1
    @zumek `spr.xr` and `yr` represent the rendered coordinate of the sprite. The first two lines get the width/height of image (canvas) with margins that allow sprite to move off the canvas (on all side). Next two get the position offsetting by margin size up and left. The % cycles position (like asteroids move off left appear on right) The margins ensure that you don't see the sprite disappear and then reappear on opposite side. – Blindman67 Jan 12 '19 at 03:33
  • This is underrated. –  May 29 '20 at 01:24
  • is there any way to rotate the `drawImage` element without rotating the `canvas` itself? – oldboy Dec 16 '20 at 05:30
  • in this [answer](https://stackoverflow.com/a/46811056/7543162), the `ctx` is being rotated instead of the `drawImage` element, right? is there any way to rotate the `drawImage` element directly without having to rotate the `ctx` and then unrotate it, etc? – oldboy Dec 16 '20 at 20:54
  • so the way in the answer that i linked is slower than `setTransform`, `rotate`, `drawImage`? ok, but is there any way to rotate the "pen" instead of rotating the "canvas"? – oldboy Dec 16 '20 at 23:35
  • yes, i know rotation ultimately takes place via a transform, but the transform is applied to the canvas/context. what am i not understanding? youre confusing me – oldboy Dec 16 '20 at 23:48
  • i now see whence the confusion was arising. i wasnt asking whether or not we can rotate the function `drawImage` (i know that would make zero sense), but rather the **element** or "output/application/etc" (perhaps a poor choice of words) of `drawImage`. consider my Q in the context of the following metaphor whereby the canvas/context is a piece of a paper on a desk and `drawImage` is the pen/pencil. right now, we are rotating the piece of paper, then drawing on it, then unrotating the paper. is it possible to instead rotate the pen or hand (similar to how CSS transforms are applied to eles)? – oldboy Dec 17 '20 at 01:02
  • ok. the way canvas goes about it is so much more confusing than vanilla CSS transforms. thanks for your help. – oldboy Dec 17 '20 at 01:56
  • @Blindman67 this is a nice solution, is there a non-square solution, e.g., scale.x and scale.y are different? Suppose I had an image originally of size 1800x1800 but I want to place it as 180x120 and I want to rotate it? – mshaffer Dec 25 '20 at 16:52
  • @mshaffer Yes you can. Just set the axis scales with `ctx.setTransform(scaleX, 0, 0, scaleY, x, y) ` where `scaleX` and `scaleY` replace `scale` in the `drawImage` functions. – Blindman67 Dec 25 '20 at 17:59
30

The other solution works great for square images. Here is a solution that will work for an image of any dimension. The canvas will always fit the image rather than the other solution which may cause portions of the image to be cropped off.

var canvas;

var angleInDegrees=0;

var image=document.createElement("img");
image.onload=function(){

    drawRotated(0);
}
image.src="http://greekgear.files.wordpress.com/2011/07/bob-barker.jpg";

$("#clockwise").click(function(){ 
    angleInDegrees+=90 % 360;
    drawRotated(angleInDegrees);
});

$("#counterclockwise").click(function(){ 
    if(angleInDegrees == 0)
        angleInDegrees = 270;
    else
        angleInDegrees-=90 % 360;
    drawRotated(angleInDegrees);
});

function drawRotated(degrees){
    if(canvas) document.body.removeChild(canvas);

    canvas = document.createElement("canvas");
    var ctx=canvas.getContext("2d");
    canvas.style.width="20%";

    if(degrees == 90 || degrees == 270) {
        canvas.width = image.height;
        canvas.height = image.width;
    } else {
        canvas.width = image.width;
        canvas.height = image.height;
    }

    ctx.clearRect(0,0,canvas.width,canvas.height);
    if(degrees == 90 || degrees == 270) {
        ctx.translate(image.height/2,image.width/2);
    } else {
        ctx.translate(image.width/2,image.height/2);
   }
    ctx.rotate(degrees*Math.PI/180);
    ctx.drawImage(image,-image.width/2,-image.height/2);

    document.body.appendChild(canvas);
}

http://jsfiddle.net/6ZsCz/1588/

Steve Farthing
  • 792
  • 6
  • 10
14

This is the simplest code to draw a rotated and scaled image:

function drawImage(ctx, image, x, y, w, h, degrees){
  ctx.save();
  ctx.translate(x+w/2, y+h/2);
  ctx.rotate(degrees*Math.PI/180.0);
  ctx.translate(-x-w/2, -y-h/2);
  ctx.drawImage(image, x, y, w, h);
  ctx.restore();
}
Mohammad Usman
  • 37,952
  • 20
  • 92
  • 95
CpnCrunch
  • 4,831
  • 1
  • 33
  • 31
  • 1
    is there any way to rotate the `drawImage` element without rotating the `canvas` itself? is `save restore` really the only way? – oldboy Dec 16 '20 at 05:59
  • 2
    @oldboy canvas isnt rotated here. Rotation is applied to context which works inside canvas – aleha_84 Dec 16 '20 at 10:22
  • @aleha so then, is there any way to rotate the `drawImage` element directly without rotating the `ctx` itself? – oldboy Dec 16 '20 at 20:49
  • 2
    @oldboy I think you misunderstood the concept. ctxs.drawImage only draws. – aleha_84 Dec 16 '20 at 21:04
  • @aleha yes, i know that `drawImage` only draws. it draws an image onto the canvas or context. i am wondering if it is possible to directly rotate the image itself instead of rotating the canvas or context...? – oldboy Dec 16 '20 at 23:32
6

As @markE mention in his answer

the alternative is to untranslate & unrotate after drawing

It is much faster than context save and restore.

Here is an example

// translate and rotate
this.context.translate(x,y);
this.context.rotate(radians);
this.context.translate(-x,-y);

this.context.drawImage(...);    

// untranslate and unrotate
this.context.translate(x, y);
this.context.rotate(-radians);
this.context.translate(-x,-y);
aleha_84
  • 8,309
  • 2
  • 38
  • 46
6

This is full degree image rotation code. I recommend you to check the below example app in the jsfiddle.

https://jsfiddle.net/casamia743/xqh48gno/

enter image description here

The process flow of this example app is

  1. load Image, calculate boundaryRad
  2. create temporary canvas
  3. move canvas context origin to joint position of the projected rect
  4. rotate canvas context with input degree amount
  5. use canvas.toDataURL method to make image blob
  6. using image blob, create new Image element and render

function init() {
  ...
  image.onload = function() {
     app.boundaryRad = Math.atan(image.width / image.height);
  }
  ...
}



/**
 * NOTE : When source rect is rotated at some rad or degrees, 
 * it's original width and height is no longer usable in the rendered page.
 * So, calculate projected rect size, that each edge are sum of the 
 * width projection and height projection of the original rect.
 */
function calcProjectedRectSizeOfRotatedRect(size, rad) {
  const { width, height } = size;

  const rectProjectedWidth = Math.abs(width * Math.cos(rad)) + Math.abs(height * Math.sin(rad));
  const rectProjectedHeight = Math.abs(width * Math.sin(rad)) + Math.abs(height * Math.cos(rad));

  return { width: rectProjectedWidth, height: rectProjectedHeight };
}

/**
 * @callback rotatedImageCallback
 * @param {DOMString} dataURL - return value of canvas.toDataURL()
 */

/**
 * @param {HTMLImageElement} image 
 * @param {object} angle
 * @property {number} angle.degree 
 * @property {number} angle.rad
 * @param {rotatedImageCallback} cb
 * 
 */
function getRotatedImage(image, angle, cb) {
  const canvas = document.createElement('canvas');
  const { degree, rad: _rad } = angle;

  const rad = _rad || degree * Math.PI / 180 || 0;
  debug('rad', rad);

  const { width, height } = calcProjectedRectSizeOfRotatedRect(
    { width: image.width, height: image.height }, rad
  );
  debug('image size', image.width, image.height);
  debug('projected size', width, height);

  canvas.width = Math.ceil(width);
  canvas.height = Math.ceil(height);

  const ctx = canvas.getContext('2d');
  ctx.save();

  const sin_Height = image.height * Math.abs(Math.sin(rad))
  const cos_Height = image.height * Math.abs(Math.cos(rad))
  const cos_Width = image.width * Math.abs(Math.cos(rad))
  const sin_Width = image.width * Math.abs(Math.sin(rad))

  debug('sin_Height, cos_Width', sin_Height, cos_Width);
  debug('cos_Height, sin_Width', cos_Height, sin_Width);

  let xOrigin, yOrigin;

  if (rad < app.boundaryRad) {
    debug('case1');
    xOrigin = Math.min(sin_Height, cos_Width);
    yOrigin = 0;
  } else if (rad < Math.PI / 2) {
    debug('case2');
    xOrigin = Math.max(sin_Height, cos_Width);
    yOrigin = 0;
  } else if (rad < Math.PI / 2 + app.boundaryRad) {
    debug('case3');
    xOrigin = width;
    yOrigin = Math.min(cos_Height, sin_Width);
  } else if (rad < Math.PI) {
    debug('case4');
    xOrigin = width;
    yOrigin = Math.max(cos_Height, sin_Width);
  } else if (rad < Math.PI + app.boundaryRad) {
    debug('case5');
    xOrigin = Math.max(sin_Height, cos_Width);
    yOrigin = height;
  } else if (rad < Math.PI / 2 * 3) {
    debug('case6');
    xOrigin = Math.min(sin_Height, cos_Width);
    yOrigin = height;
  } else if (rad < Math.PI / 2 * 3 + app.boundaryRad) {
    debug('case7');
    xOrigin = 0;
    yOrigin = Math.max(cos_Height, sin_Width);
  } else if (rad < Math.PI * 2) {
    debug('case8');
    xOrigin = 0;
    yOrigin = Math.min(cos_Height, sin_Width);
  }

  debug('xOrigin, yOrigin', xOrigin, yOrigin)

  ctx.translate(xOrigin, yOrigin)
  ctx.rotate(rad);
  ctx.drawImage(image, 0, 0);
  if (DEBUG) drawMarker(ctx, 'red');

  ctx.restore();

  const dataURL = canvas.toDataURL('image/jpg');

  cb(dataURL);
}

function render() {
    getRotatedImage(app.image, {degree: app.degree}, renderResultImage)
}
casamia
  • 619
  • 1
  • 10
  • 19
3

I built an image uploader that resizes the image in the background, and also allows the user to rotate the image 90 degrees left or right. This isn't something that is solved with just a few lines of code, so you will want to checkout the jsfiddle https://jsfiddle.net/alienbush/z02jatnr/6/. I also included the complete code below.

The main drawing function looks like this:

let drawOptimizedImage = function (canvas, image, maxSize, rotationDirection) {

    let degrees = updateRotationDegrees(rotationDirection)
    let newSize = determineSize(image.width, image.height, maxSize.width, maxSize.height, degrees)

    canvas.width = newSize.width
    canvas.height = newSize.height

    let ctx = canvas.getContext('2d')
    ctx.save()
    ctx.clearRect(0, 0, canvas.width, canvas.height)

    if (degrees === 0) {
        ctx.drawImage(image, 0, 0, newSize.width, newSize.height)
    } 
    else {
        ctx.translate(canvas.width / 2, canvas.height / 2)
        ctx.rotate(degrees * Math.PI / 180)

        if (Math.abs(degrees) === 180) {
            ctx.drawImage(image, -newSize.width / 2, -newSize.height / 2, newSize.width, newSize.height)
        }
        else { // 90 or 270 degrees (values for width and height are swapped for these rotation positions)
            ctx.drawImage(image, -newSize.height / 2, -newSize.width / 2, newSize.height, newSize.width)
        }
    }

    ctx.restore()
}

Here's the complete code:

let imgPreview = document.getElementById('imgPreview')
let canvas = document.getElementById('canvas')
let statusMessage = document.getElementById('statusMessage')
let img = new Image
let maxSize = {
  width: 800,
  height: 600
}
let rotationDegrees = 0

// canvas.toBlob Polyfill from https://gist.github.com/salzhrani/02a6e807f24785a4d34b
if (!HTMLCanvasElement.prototype.toBlob) {
  Object.defineProperty(HTMLCanvasElement.prototype, 'toBlob', {
    value: function(callback, type, quality) {
      var bin = atob(this.toDataURL(type, quality).split(',')[1]),
        len = bin.length,
        len32 = len >> 2,
        a8 = new Uint8Array(len),
        a32 = new Uint32Array(a8.buffer, 0, len32);

      for (var i = 0, j = 0; i < len32; i++) {
        a32[i] = bin.charCodeAt(j++) |
          bin.charCodeAt(j++) << 8 |
          bin.charCodeAt(j++) << 16 |
          bin.charCodeAt(j++) << 24;
      }

      var tailLength = len & 3;

      while (tailLength--) {
        a8[j] = bin.charCodeAt(j++);
      }

      callback(new Blob([a8], {
        'type': type || 'image/png'
      }));
    }
  });
}

document.getElementById('fileInput').addEventListener('change', function(e) {
  if (!e.target.files.length) {
    return
  }
  let file = e.target.files[0]
  if (isValidMIME(file, ['image/bmp', 'image/jpeg', 'image/png'])) {
    img.src = window.URL.createObjectURL(file)
  } else {
    alert('Invalid file type. The image file must be one of the following:  .jpg  .jpeg  .png  .bmp')
  }
})

let isValidMIME = function(file, MIMEtypes) {
  for (let i = 0; i < MIMEtypes.length; i++) {
    if (MIMEtypes[i] === file.type) {
      return true
    }
  }
  return false
}

img.addEventListener('load', function() {
  rotationDegrees = 0
  removeStatusMessage()
  drawOptimizedImage(canvas, img, maxSize)
  updateImgPreview(canvas, imgPreview)
})

let removeStatusMessage = function() {
  statusMessage.textContent = ''
  statusMessage.style.display = 'none'
}

let drawOptimizedImage = function(canvas, image, maxSize, rotationDirection) {
  let degrees = updateRotationDegrees(rotationDirection)
  let newSize = determineSize(image.width, image.height, maxSize.width, maxSize.height, degrees)

  canvas.width = newSize.width
  canvas.height = newSize.height

  let ctx = canvas.getContext('2d')
  ctx.save()
  ctx.clearRect(0, 0, canvas.width, canvas.height)

  if (degrees === 0) {
    ctx.drawImage(image, 0, 0, newSize.width, newSize.height)
  } else {
    ctx.translate(canvas.width / 2, canvas.height / 2)
    ctx.rotate(degrees * Math.PI / 180)

    if (Math.abs(degrees) === 180) {
      ctx.drawImage(image, -newSize.width / 2, -newSize.height / 2, newSize.width, newSize.height)
    } else { // 90 or 270 degrees (values for width and height are swapped for these rotation positions)
      ctx.drawImage(image, -newSize.height / 2, -newSize.width / 2, newSize.height, newSize.width)
    }
  }

  ctx.restore()
}

let updateRotationDegrees = function(rotationDirection) {
  if (rotationDirection === 'clockwise') {
    rotationDegrees += 90
  } else if (rotationDirection === 'anticlockwise') {
    rotationDegrees -= 90
  }
  if (Math.abs(rotationDegrees) === 360) {
    rotationDegrees = 0
  }
  return rotationDegrees
}

let determineSize = function(width, height, maxW, maxH, degrees) {
  let w, h;
  degrees = Math.abs(degrees)
  if (degrees === 90 || degrees === 270) { // values for width and height are swapped for these rotation positions
    w = height
    h = width
  } else {
    w = width
    h = height
  }
  if (w > h) {
    if (w > maxW) {
      h = h * maxW / w
      w = maxW
    }
  } else {
    if (h > maxH) {
      w = w * maxH / h
      h = maxH
    }
  }
  return {
    width: w,
    height: h
  }
}

let updateImgPreview = function(canvas, div) {
  if (canvas.width < div.clientWidth && canvas.height < div.clientHeight) {
    div.style.backgroundSize = 'auto'
  } else {
    div.style.backgroundSize = 'contain'
  }
  div.style.backgroundImage = 'url(' + canvas.toDataURL() + ')'
}

document.getElementById('clockwiseBtn').addEventListener('click', function() {
  removeStatusMessage()
  drawOptimizedImage(canvas, img, maxSize, 'clockwise')
  updateImgPreview(canvas, imgPreview)
})

document.getElementById('anticlockwiseBtn').addEventListener('click', function() {
  removeStatusMessage()
  drawOptimizedImage(canvas, img, maxSize, 'anticlockwise')
  updateImgPreview(canvas, imgPreview)
})

document.getElementById('uploadBtn').addEventListener('click', function() {
  let fileInput = document.getElementById('fileInput')
  if (!fileInput.files.length) {
    alert('Please choose a file first');
    return;
  }

  let formData = new FormData()
  formData.append('fileName', 'yourCustomFileNameHere')

  canvas.toBlob(function(blob) {
    formData.append('image', blob)
    let url = 'theURLtoSendTheFileTo'
    sendForm(url, formData, doAfterUploadSuccess)
  }, 'image/jpeg', 1.0)
})

let sendForm = function(url, formData, callback) {

  // Simulating upload. Use the commented code below for a real upload.
  statusMessage.style.display = 'block'
  statusMessage.textContent = 'Uploading, please wait...'
  setTimeout(callback, 2000)

  // let xhr = new XMLHttpRequest()
  // addUploadListeners(xhr)
  // xhr.open('POST', url, true)
  // xhr.onload = function () {
  //     if (xhr.status == 200) {
  //         if (callback) { callback(xhr) }
  //     }
  //     else if (xhr.status === 0) {
  //         alert('No response from server. Check network connection.')
  //     }
  //     else {
  //         alert('There was a problem uploading:  ' + xhr.statusText)
  //     }
  // }
  // statusMessage.style.display = 'block'
  // xhr.send(formData)
}

let addUploadListeners = function(xhr) {
  xhr.addEventListener('loadstart', function(e) {
    statusMessage.textContent = 'Uploading, please wait...'
  })
  xhr.addEventListener('abort', function(e) {
    statusMessage.textContent = 'Aborted upload'
  })
  xhr.addEventListener('error', function(e) {
    statusMessage.textContent = 'Error during upload'
  })
}

let doAfterUploadSuccess = function(xhr) {
  statusMessage.textContent = 'Success!'
}
body {
  background-color: lightgray;
  min-height: 100vh;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
  font-size: 11pt;
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  margin: 0;
}

button {
  outline: none !important;
  border-style: none;
  background-color: rgb(115, 115, 250);
  color: white;
  padding: 10px 15px 12px 15px;
  cursor: pointer;
  border-radius: 2px;
  font: inherit;
}

.canvas {
  /* You can use display: none; to hide the canvas. The image will upload the same, whether or not the canvas is diplayed. */
  display: block;
  margin: 0px auto 40px auto;
}

.card {
  background: white;
  border-radius: 2px;
  box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12);
}

.img-form {
  padding: 15px 15px 0px 15px;
  width: 320px;
  margin: 0 auto;
}

.img-uploader-panel {
  padding: 10px;
  width: 340px;
  margin: 0px auto 20px auto;
}

.img-uploader-header {
  margin-top: 5px;
  text-align: center;
}

.img-preview {
  display: flex;
  background-repeat: no-repeat;
  background-position-x: center;
  background-position-y: center;
  background-size: contain;
  border: 1px solid rgb(160, 160, 160);
  width: 320px;
  height: 320px;
  margin: 0 auto;
}

.status-message {
  display: none;
  align-self: flex-end;
  margin: 0;
  line-height: 50px;
  background-color: rgba(0, 0, 0, 0.5);
  color: white;
  width: 100%;
  text-align: center;
}

.uploader-btn-panel {
  display: flex;
  padding-top: 10px;
  justify-content: center;
  align-items: center;
}

.rotate-btn {
  font-size: 32px;
  padding: 5px 12px 9px 13px;
  margin-right: 10px;
  background-color: transparent;
  color: rgba(83, 83, 249, 1);
}

.clockwise-btn {
  transform: rotate(90deg);
}

.anticlockwise-btn {
  transform: rotate(270deg);
}

.upload-btn {
  margin-left: 20px;
}
<form action="post" id="imgForm" class="img-form">
  <label for="fileInput">Add an image:</label><br>
  <input type="file" name="fileInput" id="fileInput" accept=".jpg, .jpeg, .png, .bmp">
</form><br>

<div class="img-uploader-panel card">
  <p class="img-uploader-header">Image Preview</p>
  <div id="imgPreview" class="img-preview">
    <p id="statusMessage" class="status-message"></p>
  </div>
  <div class="uploader-btn-panel">
    <button id="anticlockwiseBtn" class="anticlockwise-btn rotate-btn">&#8634;</button>
    <button id="clockwiseBtn" class="clockwise-btn rotate-btn">&#8635;</button>
    <button id="uploadBtn" class="upload-btn">Upload</button>
  </div>
</div>

<canvas id="canvas" class="canvas"></canvas>
McGrew
  • 812
  • 1
  • 9
  • 16
1

Here is Something I did

var ImgRotator = {
    angle:parseInt(45),
    image:{},
    src:"",
    canvasID:"",
    intervalMS:parseInt(500),
    jump:parseInt(5),
    start_action:function(canvasID, imgSrc, interval, jumgAngle){
        ImgRotator.jump = jumgAngle;
        ImgRotator.intervalMS = interval;
        ImgRotator.canvasID = canvasID;
        ImgRotator.src = imgSrc ;
        var image = new Image();
        var canvas = document.getElementById(ImgRotator.canvasID);
        image.onload = function() {
            ImgRotator.image = image;
            canvas.height = canvas.width = Math.sqrt( image.width* image.width+image.height*image.height);
            window.setInterval(ImgRotator.keepRotating,ImgRotator.intervalMS);
            //theApp.keepRotating();
        };
        image.src = ImgRotator.src;   
    },
    keepRotating:function(){
        ImgRotator.angle+=ImgRotator.jump;
        var canvas = document.getElementById(ImgRotator.canvasID);
        var ctx = canvas.getContext("2d");
        ctx.save();
        ctx.clearRect(0,0,canvas.width,canvas.height);
        ctx.translate(canvas.width/2,canvas.height/2);
        ctx.rotate(ImgRotator.angle*Math.PI/180); 
        ctx.drawImage(ImgRotator.image, -ImgRotator.image.width/2,-ImgRotator.image.height/2);
        ctx.restore();
    }
}

usage

ImgRotator.start_action("canva",
            "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAkGBxIQEhUSEhMVFhUVFRUVFRUVFRcVFRUQFRUWFhUVFRUYHSggGBolGxUVITEhJSkrLi4uFx8zODMsNygtLisBCgoKDg0OFhAQFy0lIB8rLS4tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tKy0tLS0tLS0rLS0tLS0rLf/AABEIAL0BCgMBIgACEQEDEQH/xAAcAAACAgMBAQAAAAAAAAAAAAABAgADBAYHBQj/xAA9EAACAQIDBgMGAwYGAwEAAAABAgADEQQSIQUTMUFRYQYicQcygZGh8BRCsSNSwdHh8RUzYnKCkmOywkP/xAAZAQEBAQEBAQAAAAAAAAAAAAAAAQMCBAX/xAAkEQEAAwACAwACAQUAAAAAAAAAAQIRAyESMUETIlEEFDJhgf/aAAwDAQACEQMRAD8A34SWl+5PSEUD0nbFQBJYzI3J6QNRtARFtG1kCxgveQLl+7xwIDGCwJaNTe0gWNu4Ad7xI7LzM5n428dFWNHDEXGjN0ETK1jXQa+2aVEXeoqjuZ5dXxzgCbfiE+f8ZwTHYt6pzVHJPc/p0mEexnPk0/HD6OpeJsG/u16R/wCa/wAJ6NDGKT5XU+hE+YlqsvpPb2P4ir0iN3VYW/KTmT/qdJPJfxxL6JeveV2ml+EvHNPEMKVcCnUOim/kc9AeTdj9ZvDJ2M6idZWrMdSSCPaSUKJDDaW0qd4FIjTKWlY8YzpeDWHDL6lK0otAEgjW+7RdYEMBEdVj7mBQYDMjdQbqMNUrUIiljMncwbgRhqsVTF3x7RgkRqX3eVyb8Qe0Bqkxd196RkpwJaS0bJCqQpI4jBYbSBN5aT8TGaneeV4lxBw2FrVv3KbMNfzW8v1tKNE9oHjqoKrYXDEBV8tSpxOfmi8hbn305a8wq1uPfX+8Ck2JOvMkm5JPEk8zrMao3OZt4jBZ+silOY+plAHWOAveFZaUVPuNr0bh8xEakRraxHEfygogE6T01oNa9viNR8RxAkWIXbOs4AJ/2noeU6D4Q8bNRtQxRJXglU3Nh/q56df7zn2Fp5Qbfm1X4e8h73tb4x6eL11+z2k3J6dZ5RkvoelUVlDKQQdQQbgjsYSZy7wZ4lNGyMb0/wAy34dXQdRzWdSpMGAZSCrAEEcCDqCJpE689q+MoJA5WWZIpSVwn4gw78wBIMn3eUFqpMUfesYU/SMEkUloLfd5YV9IMvpArJgzmW5e8G77/SBUXMAc9Zaaff6RTTgQ1D1i7w9Y279ZN1AQVRAagijDiK1AS4mrN6JN4JQaHrGWiOp+UYmrt4IN6IRQEIw69IXQ3ohWoJPw69IdwvT9JARVE597ZdpFMJTorwrVfN/tp2a3/bL8pv7YcTmvtrw+XD4dv/Mw+dNj/wDP0iXVfblFMk3EY4Qkd4uG1abPs/ZpYAk3BmVr+L0Up5NW/AP0jHDsJ0zDbLXLa395h7Q2CBqJj+Z6P7eI+ueZPSets+pl0yn4HQ/SW7S2WVmBh67IdCb/AEPwmtbaytTxZzVxcixGuo5g9R3lVRM3Djz7jr6QYmuSQWHcMJFNrHly/lCDh8U1Mg66cDzB/j8Z1r2abeFVWog6KM6jXyi9mUdACRYd5yrKHvYj0PP4cv0lmx9o1MFWWtSNiOKn3XXmp6gxHUpaPKH0ZvBA1QTz9iY+li6KVqfBhwPFW5qe4MzXoibPLPRjUEArCIKAh3AjE04qjtDve8rWgIdyIw029HWHeCV7gSbj7tGLq3PBvBKmww6xfw/eMNXmqIu9EqNHvBue5jDV++g3spNDuYm57mDWTaI4lmkGUTpyqymMolgQQ5RAUGMBABGEgkIEkIgK4nOfbUAcHT6rXU/DI6n/ANp0sqJyX2t7TD3wqj3AtS5/MSb2HwA+c5tMRDulZmenMNn0yzC03/ZVI5BNQ8N0blj0t9ZsdPDbweauaZHAAaD1POebl7nHv4f1rrccFh9JnvgQRac+bHYqh/l4lKoHIsL6dj/ObB4d8TVKzhKqZTa9wND1mU0xrF/LpbtTYOYaTne3dmNRbWdg2rid3TL2vZSbde05Z4gxtfEXLhaacr6Ej46mdcftzy+u3jU6wZcp9fvvLqdTQA/YnnJYaA3mRSc8Pv4T0fXm9wuLW1B/pLPxObjofoZjst+EqZTzk9r6dH9lO3DTrNhifLU1UHlUA5eo/SddJnz54OF8Vhct8++1I/dAFv0Pzn0KiaD0mlPTDl96QCQy3dd5N13mjFUITHKWgkUpvBGMggVtDaW7u/ODdd4FRglu67yCkOsCuIZkbsdYu7HWBVeS8GSQLK5NeQQZZLQpowMQCPaAYQvaKIwkC1WsCToACT6CfN+K2wcVi6rtqKrNl7DXL8LX+c7b7RNpfh8BXcGzMmRT/qfyj9TPnNXKkMOIII9ROLxsY24pydbR4aTKzDqR8tZn7W8P1C4qAFkvcqL8O5HATC2WRmV191hf0PMToWwqmYC88lrTFtfQpSLVxpH+EbyoxWmqKzKci2BAGhC1b5gDrcc/UCerhdk1MO1Jyb+axtcA3Omh5248p0gU1I5TXNovvK4TSym/x+zJbkmYWvHFZ6ertWjnpKo0zTn21/D1Xz51FS6kLbNZOjf6j6zpeL91e2sNJA9ric0t4u708vb5/wAbs2ojEsLa3va2sqD9RrO77Z2BTqrYqPXnOM+LtnHC1QvI3t6TanJ5TkvPfi8a7DFc+v0MrLn7MmHqg8Len8jzjv3H10mrFuHsooCrjwTxpK7nuTZB8fNO5Cck9jtFd9WqcwgUd7sM3ysP+wnXBNK+mHJ7QSSLIROmYSWhgMAQ2ktJAEFj1jXgJgAwQwQAYIbSfCFPlhCxRUh33aVwYp92jKlpXv8AtDvoCt96QgwXgJtIprwrBJeBzP2640rRoUh+dyx9FH8yPpOMg6GdM9uWKG8oUrebKzs1+A0ULbpoTOZqPL8ZzPttX02jZOGelTpZrWc1gLG5VkyEq3Q+e/zm3bEx+XQ8pqeFxQOFYk+ak9Gv3K64ev8A+yt8BNo2CVYg6a/rPFyPfwT8e4ds5wVT5zW6uPr4Z1LqCma5bnYmTxThK9B1rUG8hsHXmLaXXr6T0tlbPxOICmm9Gpmy/wD6G6h1LDMMpsfLwitNjW02r9nHsptx66Dc0w9v3myj0vYzIqPURc1gG45Qbj0vFXA4unT81NAFzXtUXTLe7chbSeQMRiqtYUlAyC+8csDa2lhbifpJNJhYms+pepQ8Qh1N+I0I5g95yzx9jd7X0/KLfM6/oJ0fatGnRR352v3NhYTkG06l6hY662/WXhj9tZf1Fv1xjUReZ1O4tp8YhwLA6cbXHcSyjjmX+Inpl5Ie/wCHPEZwVZHAJTTMo45SLN69bdQOk7vsvaFLE01q0XDowuGH1B5g9QdRPmxmBHDT9J1r2O7siqAzXy0yQx/MCwJsNDby68bEA8JaS45a9a6MqwMJeABDlBmrzsW0mWWVAAYlhAW0IhIkgWZIoSOrCS4lQrpFVJZcSZxAR1ibuWlx1g3g6iFYoimWCI4hEEIEW0IlRYLw2gEaxkUicOf9oj1lA1P00+calw9b8+8w62KVQyVSFFiLt7pHW/C+vC9/1kVx7GJTx+JxNXE3CqlR8wBO7RMyiwGrEHKbDob2mgW/pNqbENTq4mkhLZlNPMdboTcmx5HSa1jKeW4+X8TOG0Q9R9h4j8Ga4VsrspFidaRGhaxsBccCNcwN9ADkeHdpmnZWPa8vw/j+pToiglBQmQIwL3VgtreXLwuL2148Z41CqtUlhYEknLwtc8BPPlpifKG9JiJjJdOZ9/Ste9+HrMbZ1MoRmp5sp0ZTkqD4ix+U1bYm2WoNZrlf0nSdl4ulVUMpU39Jls1e6l+lKDeLlFM8Sb1GZrE8TZiTfUz1MDhhRU39SZlUsTTA1yiab4w8WKqtTonMx0JHBfjzMk2myzbp5Xi/bOdjSQ3Y30HLrNF2nhzTsje8fNoQdNLaj4x0xZVy5bU6EnXvwse0xcbjC51N+9gOw0no46eLw8t/KWXTxBKgdBp8zMcXbX0goi5HK5ue3WenVqIlMBV1J4np37zrXMMJahUcLifQXhbY9KmlLEUVy7ylTzAaArkBvbqTY3nGj4fqOMLQykVMQbi/EBrWJHQKS2vefQeDo7tFpjgihR/tUAD6Cd0hjyz6XgQEx4pmrABJIPhJeRUvJDFgQ/esBEYQMZUIYBGMUGBGlccwW7yKgitGEVmlchCsF4yyiwGNEEMisTG4oUQWIJB6Ak3J6DU3J5dZom3fF+HRiprAEXuqi7XHJ2AuvTKuvVhPS9oOJYUamUkWpsFtxzsQlx042v0Y/Dk+PbDU8LSWlRvVZQa1V2JysSwC01BsoOUm5B0t6ziZaVqpxe11avUqKmZXBGUkgHzaXIF7WA4WJ68Z5GPxBqMS3HnbQDoABwAGlpXvCeGnf+EQJfQf1khpLJw2zWrDyMuYfkPlutgQQ50BN+Bty11tMTE4R6TZXUqw5H9QRoR3Gk9nD4qp5QrN5TmUEhggHmsqHQi4BtbX4zZ6FOltCj/lKHQM1RFfKQgUHeUVIJ4AmwNhkYEMbTmbTBEa0Kjj3Xj5h34/Oe5szaiH8+7PQmw+B4TH2p4ddCTS/aLc6L5qijuo98D95fUhZ4QktStmleS1W9VUqH87EdySJj1cNZSzfCeR4d2pu23dQndnhzyHqO3abJ4gw+XDs4a4y3B63GlvnPNNJraIeuvJW9ZmPjSkXOSepMysBhFVi1XgvBR+Zv5TCwlUowI5fpMyq5Os9U9PHHa/FFeKjuf6/GXVCuTMdTawHThr+p+Ex6VTKSTqLAMD+6bfLXW8zsPhwV6obG/Q9D0M5dN22N4hWvicNjPzUlCVqYBJ3ZD03dOts6vYa2VtNBfrtGqrgMpBUi4KkEEHgQRxE+bEc0XDUyQe2t7dufxnVPBOPr0MgqkNRrHyOLgZzfh0PVfUjgZpSWPJV0SKT2jCAzVgUH1hvJYQWE5UZLiQW7wGBNJM0U3glQSYtoTIBABMXN92jNK7QMi6xfLAEMEBiBAVEgMhMBbSPpIX7GAC+p+UDnPtGxFV6i4ZAtPyb1qr31DFiUSwNyMl9egnL8ZSVEqhjd81EqTzUrUzgcuJUf8AGfRO1dmpXAzDzLcqw95SbXt14DTnPnDxQ6jE1UQqyo7KGX3WseI1nEx21pPTAvfQafepJm07F8PowVmrBW4hR7yupI8/lJXXXQaaes04mAacJzaJn1LuOm+1PDFMNZq6JULEBgxNPOAT+0AUFODcNOGmtpg1cHUoWZiDmuLJb3OBKtoGU2sVueGoHLwMJtitTBGYsrAB1Yk5lBBtfjbQfIdBbbtm7Qw9eiVs2YL5l8iqpUHJl4LcnLrprfqRM5i0O/1l6FFFpUA28yMWXd5mKhWsmWxFtSPNYX0NzwNrKOzqWOdRWoJUqVb5GVjTqM66ENUUKWPlOrqdLcLiYtFc1EU2/aAqCnRxxBXNqrgWA5AKBz1GxsccO2V/dptZi4I1KkAWC5szB1Fzwvw5znv4vXTxPEHhynRPkzJqQcxLKLAcRq3G99bjpbhmVcO52UQxBKkgFWDApnDCxHYkfCe9Va7/ALNXPvA52Kgs1xSGUaFSEGpzaaWBsJifh1CvRCMquzHkEDg5Sqea/JTYCw6LcAS1pmI/0044yc/npzqkBMylY6coMTgGpswPBTa/6SzZwGoPMG3qNf0vNZnY1nEZOFxVIqQeXD1HET2/DlN6rinTpiqWBG7JClhYkgMSLcCb3EwnqBltx5H+BE97wnSrUa9N8OuY1Gakgv8AnVUYknkLOddeEQW6eXi9nNvfwyZt4ai0hmWzB3IC5l5e8P6gz6Co7Jp7rdFAU00I6AAHsdBPB8LeCVwzCvWYVK1y5PECo3Fsx1Y9OA7X1m4X7zSsY897aSlSyiwvppqST8zqZGQ9I+aAuZ2zVlYLRiTJcyKSNeGSALQZT9iWh4DUgUkQ27S3eQb6BURJY9JaavaDedoDBoCB0i2bpJr0nTk2Ud4Mg7/OC56SFz0gNkEV8oFzf4an5CDP2k3nb6wObe1rxmMPR/C4cuK1YedirIadC5By5gDdiCARwAbnacQnteMNtfj8ZWxGuVmtTvyor5aenK4GYjqxnjTOW8RkBJeCECFES7DYg0zmAuOangRx+B6HlKpBA6PgMaxwp3dNWyDNSdLgM1grKwC+Virg+tM8LyYmjvaa1qZ98Lo4ujF1YumYC4uRa2pUst7XJi+zfago4atcKzI5KhrEHMqnVTxUBDpY8+ulfhii1RqmHcEWZmCqL5FYBi6jkBc63OjacRPP6mWv8POwWJNN8tQt5WXiLe8xvlsp5uTwNtewnsY/FpQotUBzZQjEDKf2hy5WvrdrlRfXRgepmY1Jt4KWQKzqAA7f5tUqcgsfzG9r3setyZRTwmFqYerh2R6bABWdgWy1bqzW18qBgmnG3UXtN2dlcz01aspr0zWFje2YDgj8LW48QSL8iJ5NG4P1+k2jwzUXC4iphKlNmSqoZM3vrwYMw0BBVTy4EW4mZ23PCG7DFb3FzrxGtyD98pZv4zku4r5xse2p0DYk9v6H6Tsfs52E7CjXZbU6NNlpEizVKlRnZ6gH7oVwoPM5jwsTzbZuCDjy2zWcpmsAtVBfdsebadNVNxwNut+zHagqYdqebNlOZb3uoOjUyCdMrgi3IEep7457xly/4tx3Rg3ZlmeDNPQ8hd1AaRjwQqrdnpJkPSXXhvIKMh6SZOxl94LwKcp6QFZfeSBRlhWleWn71kgUMtoLzI+MkCoVTFbEWlCmAidOV/4iKa15RCogWieN42xZo7PxdRTZhQqAHozKVB+BM9i01b2o11TZeJubZwiL3ZqiWHyv8LySse3zsosIhj1DEE4boBLAJFEJgI0KiKY3KBsHhTalKhmFQjVswDDytZfcLcgeHxOs2vZ7gVKtXMxoFFcWHkJppdiQVNmCtcheNgb2FxzIzbPD+3kSmlJ3yW817MAXQtkBZdR+Vrgjhl4TK9PsNK2+S3B67qEZcjqrozUzbNxdDa44Zip0HIWNpgbUApK+Wo27K57LnNVQtNc1J8qksp3d7NawF+FyMXxHtGjQp2DLUzBcgRlykgHJVsBYroGtYAXAFrT0/C2MTE0gMo94uyhsxR7DMaa6W1N8pvo1tRpMp2I3HcZPWuc7V2gtWoKtJd2dT5bgk5iQeJA0NrDSwnWfA+2f8RplXy5+lwMzD3wAT6N6N2M5Z4qwP4fF1qZsCragZvKxAuDn1Jvz4G9xM3wLtoYTEqWW4YgXAGZW1CkHjlu2ouPpNb0i1XFLzWzquJ8JbzCVGKFaqPWuF99qauSuW35xbMp14kcGM13wZtQYPE7+qyilVy0KwX3EdtKOIzFicr5Fv0Di5Np0nZG0lqMHFwtZQ1jxWqBZlI5G4PymieNtgpQrMzIxoVVawBJGYks9Mrzyn9og1IBcKPKJxWc/4to3d+usZR0lopDpNF9mfiM1qbYOs96+F8oa+tbCiwpVh10yg8eRPGbrvDPVE68kxi8IIhoiIKhhNQwhSg+zFKD7MaAShco6n5whO5+cb75Q3gTddzAaZ6mOKkm+7SCvdnqZMh6mOa0O9gVFG6yZW6mWGtJvYGGtpD6xgsDJOkJbvABLAtpFhEE5J7ctsqTQwam5UmvU/wBJKlKQ9bGofQjrOvz5T2xtN8XXqYip71Vy57A+6o7AWA9JzLSkMRpFEEcDWctT2isY0rMCAQyARSYEMkEMCWl+GxT0zmRiOo5Hsw5jUygRlgZu19pHEuKjCzZQG1JzEX1uxJOlhr0HHjMMHiDz/WLIYzB07wB4napkov7ykKG1ANQ/5TOeAzW3ZJ55OF503aBFak1OsBoAGtya+hF+YNiJ87+GbHE00N7VWFI2ZltnIAN1IJsbG1xw5TvGLrMcEtW5zMVzE6k2JXUi3S88948Z6bUnfbnGLp19m4tMSigHDFi6gkGth2I3lMDXyhCzqToFZea2nccLiFqotRGDI6q6MOBRgCp+RE0jxfhRUoYZzoXLUmtzptSckHqLZx/zvxAnr+zlj/h9JSb7tq9Jb8clKvUppf8A4qB8Jtxz8Y8sfWzARTGimasEkhEkigIZLwwFtAYwMhlRWYYxWS0ikMEa0lpR/9k=",
            500,15
            );

HTML

<canvas id="canva" width="350" height="350" style="border:solid thin black;"></canvas>
Dr.Sai
  • 303
  • 4
  • 5
1

@Steve Farthing's answer is absolutely right.

But if you rotate more than 4 times then the image will get cropped from both the side. For that you need to do like this.

$("#clockwise").click(function(){ 
    angleInDegrees+=90 % 360;
    drawRotated(angleInDegrees);
    if(angleInDegrees == 360){  // add this lines
        angleInDegrees = 0
    }
});

Then you will get the desired result. Thanks. Hope this will help someone :)

Vivek Saha
  • 91
  • 1
  • 9
1

To rotate and fill (stretch to fit), try this fiddle which uses the corner of the canvas for a start point instead of the center and can rotate 90, 180 and 270 degrees.

Note: the fiddle is stretching a 200 * 300 image to fit a 600 * 650 canvas

const canvas = document.getElementById('canvas');
const image = document.getElementById('source');

const Rotation = {
  None: 0,
  Right: 90,
  Flip: 180,
  Left: 270
};

const noRotation = () => Rotate(Rotation.None);
const rotateRight = () => Rotate(Rotation.Right);
const rotateLeft = () => Rotate(Rotation.Left);
const flip = () => Rotate(Rotation.Flip);
const Rotate = (orientation) => rotateImage(canvas, image, orientation);

function rotateImage(canvas, image, rotation){
    const rotate =  correctRotation(rotation);
  const context = canvas.getContext('2d');
  
  let offsetX = 0;
  let offsetY = 0;

    let displacementX = canvas.width;
    let displacementY = canvas.height;

  if (rotate === Rotation.Left || rotate === Rotation.Right){
    displacementX = canvas.height;
    displacementY = canvas.width;
  }

  switch(rotate) {
    case Rotation.Flip:
      offsetX = canvas.width;
      offsetY = canvas.height;
      break;

  case Rotation.Left:
      offsetY = canvas.height;
      break;

  case Rotation.Right:
        offsetX = canvas.width;
      break;
  }
  
  context.setTransform(1, 0, 0, 1, offsetX, offsetY);
  context.rotate(rotate * Math.PI / 180);
  context.drawImage(image, 0,0, displacementX, displacementY )
}

function correctRotation(rotation) {
    return !rotation ? Rotation.None    
    : rotation < Rotation.Flip ? Rotation.Right
    : rotation < Rotation.Left ? Rotation.Flip 
    : Rotation.Left;
}

image.addEventListener('load', e => noRotation() );

https://jsfiddle.net/0unyh7ck/8/

Antony Booth
  • 413
  • 4
  • 5
0

Why not do it for the entire page. At page load detect all images and continuously rotate all of them.

 var RotationCollection = {
    rotators: [],
    start_action: function (showBorders, isoverlap) {
        try {
            var canvasTemplate = '<canvas id="_ID_" width="350" height="350"  ></canvas>';

            var ja = 5;
            $.each($("img"), function (index, val) {
                var newID = "can_" + index;
                var can = canvasTemplate.replace("_ID_", newID);

                if (showBorders == true) $(can).insertAfter($(val)).css({ "border": "solid thin black", "box-shadow": "5px 5px 10px 2px black", "border-raduis": "15px" });
                else $(can).insertAfter($(val));
                $(val).remove();

                var curRot = new RotationClass(newID, $(val).attr('src'), ja  * ((0 == index % 2) ? -1 : 1), isoverlap);
                RotationCollection.rotators[index] = curRot;
                ja += 5;
                //return false;
            });
            window.setInterval(function () {
                $.each(RotationCollection.rotators, function (index, value) {
                    value.drawRotatedImage();
                })
            }, 500);
        }
        catch (err) {
            console.log(err.message);
        }
    }
};
function RotationClass(canvasID, imgSrc, jumgAngle, overlap) {
    var self = this;
    self.overlap = overlap;
    self.angle = parseInt(45);
    self.image = {};
    self.src = imgSrc;
    self.canvasID = canvasID;
    self.jump = parseInt(jumgAngle);
    self.start_action = function () {
        var image = new Image();
        var canvas = document.getElementById(self.canvasID);
        image.onload = function () {
            self.image = image;
            canvas.height = canvas.width = Math.sqrt(image.width * image.width + image.height * image.height);
            self.drawRotatedImage(self);
        };
        image.src = self.src;
    }
    self.start_action();
    this.drawRotatedImage = function () {
        var self = this;
        self.angle += self.jump;
        var canvas = document.getElementById(self.canvasID);
        var ctx = canvas.getContext("2d");
        ctx.save();
        if (self.overlap) ctx.clearRect(0, 0, canvas.width, canvas.height);
        ctx.translate(canvas.width / 2, canvas.height / 2);
        ctx.rotate(self.angle * Math.PI / 180);
        ctx.drawImage(self.image, -self.image.width / 2, -self.image.height / 2);
        ctx.restore();
    }
}
var theApp = {
    start_Action: function () {
        RotationCollection.start_action(true, true);
    }
};
$(document).ready(theApp.start_Action);

Please check out for theApp.start_Action where all action begins The HTML can be as follows:

 <p>
    Deepika Padukone.<br />
    <img alt="deepika" src="data:image/jpeg;base64,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" />
</p>

<p>
    Priyanka Chopra.<br />
    <img alt="Priyanka" src="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAkGBxMTEhUTExMWFhUXGB0YGBgXGBoaGBodGhgYHRodHR0aHSggIB8lGxgXITEhJSkrLi4uHR8zODMtNygtLisBCgoKDg0OGxAQGy0lICYvLy0vLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLf/AABEIAMIBAwMBIgACEQEDEQH/xAAcAAACAgMBAQAAAAAAAAAAAAAEBQMGAAIHAQj/xAA/EAABAgMGAwYEBAQFBQEAAAABAhEAAyEEBRIxQVEGYXETIjKBkaGxwdHwFCNC4VJicrIHJDOi8RZTc4KSY//EABkBAAMBAQEAAAAAAAAAAAAAAAIDBAEABf/EACkRAAICAgIABgMAAgMAAAAAAAABAhEDIRIxBCIyQVFhEzNxQvAUkbH/2gAMAwEAAhEDEQA/AEshcxYRLJOYru0X+y2BUqWlWIxU+GJSLSHeoOnKLfaLRQSyax5WTjkk4Q1RRbirZspRId6xFZFqCqiC7JJqI3takh92jI3jXHtnduyZChh5mv37QFNW8ucOR9mPygqSaDlEEkf6nMdaNE+K+cSqS8rAJyAJMtRd3JAbOn7D1hPejrkTpYoCEkPVgV18wXiwqwlAJDgGmmbAe4FIqnEFsXLSUobPAS2ZDqUW5vSPQJRFd9mBVXUj+36gxb7BRKdAE1JYDT2DGKVZJq0TACp3CSXi62BizlyA7HwZ0pr5+kXxriiWV8mTWTD2mJjQFiUn+J6Pm9MoZT7wlyxiWph0qTpp7RrLs+FOIqdSnJJyrl5RQuJ7wXNXhSruANTrXzZozk+jVCwy++M5isSZACU6qNT0Ayf1iqKsk2arFMKiTWp03Ow+MMhKSnAD4czzwgk+7Qkt1+90/wA/wFAPV4yX2OikuiGfZyHEtLjdnEBTJahmjKGF2TRNOEmnMqb2+MT2i6QHbEOhf+5MDwRvJiJUtJ0Y+kTSLVNlEMoqSM0GoZ3ZtonnWFv1Hr+xEDzJZAfxJ3GYgXE1SOr3LxnZJ0tKlK7IihQQVHKrFIy2NPKGU7iixAYhOSWozKfyDRw5KikhaD9D1EdF4as8i0ycaUAEFlDNj0aDT5dsTKHHobWjjmUA0pExZ6N7nTyhXe182m1JCAgyZZDEAkqVu5YUypD+w3OgKwhIGsMZ1gS4yAHLlBOKirFqTboqty3CEAEirem8XK7JACRG8qzgDKusESUEA0hcb5Ww30azWEA4HOJ6E0HnB83aNBKDOPSDkYiDsgCS0AWzIEGvhPQ5e/xhrMBIIgOZZgQ1P30hadSTNatCq7LOtiVLxAEgBWQ1oc4OmyCDgOZzIyDc4jsxdKk4cw9fTbnBiZeNKVEsUS6hqcz7ND5LehaZXb5yCQfEWbkPsQn/AAuJYQzB68tX5losFvlBTEaZbmFmApWFAOAdPvlAhCe0yO8WAZ6R7Ei0uSTm5+MZGnC/ga9E2WeFLJwEMRpF2PEsmfa09mXAHlFfu+6Av9Ln2hxdfDUtCgsqCS8TT8NybkuxqypKmXOatQAIhetZmLSnV4LTPQQAVinON7NIRjCwXaseX/xs2Of0P5xaPZamDRLY5bkjcKHxiKSATQUJygy7pYC+j/OF4l50Uz9LBDKBAl5EjEeoNPrFLvX81M2rqC8Rb9LEgjL+EEReVo7z8gPYftFPtsgJVPJ1xkbeFQPpX0j1CMrNpThnpBL91LvVqqDejRerklBSQ+RbL1+cUG2S0icgjJSRUjZRy8mjod2hpaSM2D51pp6RZH0onl6gm8mbEVMlKSS2tI5d2talgwCcqjd+efnF34stLWcpyxEAaMKkv5fKOepCnAZ30pQCp+I9Y5dhxWjy8rQUpY9H5HP5e8VMoJLbReLfYx2TkdBUtT2EILJdyllwPb7MBN7GKNntyKQkMty+z+7Q5nzw3d+/91fSCJHBMwyxMS6TAs+yTkDDMALZFsoxTXQXBiq0zzuX5wvVaa0LH2MHTljIpECTJSTkBGMxb7Api2U4puIe8I33+Gnhf6FUWPnCeYj+Jg3OsDuQYzs5qtHfbmnYjiNX16/GGE2XXn9/SOVcH3ucLG1djhozA4qvmskegGUXmValEj/NIXlTCkmtc0Ug2/JTE8akWE7RvLEL5Fp3L82IbrB1nU5OlNt4yL2Y0blO8Zhggim8RLMMkCiBQ0EQrUByrG05UZLrVqwGrCApymmMDufX948m2oCWorUEpLpA1Jagjy9BUFqtC2/UpSJazmQWo/VvaGvqxa7o2E9OGgJOQHoSfaBZaKsoMkAAb1fWNZZwpKquTrXPSNrRP7gA8W+v/MYaBWiyjEe61dI8hlMk1qwP7RkLsMY3fZESpbOHb7EVK+LKFzCfxCuQCg0Mp93TiCrG6H5uBHsi4ZbYkh1b6wx976BXRHw1dygS61KGjxbbNITLL7wsuKxFKq0ENrTYFmuKkeX4jJjlNxi6ZTC+Nsml0NIY3bVZJ+6GAZYpB92AuT09zEeH1oryelgVpmFJUtTM1Byz+kUu8pZ/EzEKLk1Z/wBJdg/OhLc4uF4Wfv5Op/ck/L0iv21AFsmqcZI1GiB8wRHqkRT75wpXJAL4QrF/9MP7TF5uqf3EtTOp6mKReEjHObZAGX/6TG+EP5VtwSwNW9t/usUL0oCrkD8V2nGsJFQBTr9hoUypQQCogPkH+9/ukEoOIqmKFNPp7QrtFsxVJydXoO7718oyxyiL+I70buA115xb+B7meWmYsuc205Ry2eTMtKRuQPv3jtV2XgizyAEoVMwhlBAJw9TvCsm2kHj6bH8xJwsBCC8rtxO4jVfH9lFFdog/zIPyguwXzKtAJlqxAZ8oCSDi/g5/f1yZsGilzcSCRtoY6TxXfKZRw4CpRem3WOdWqaZisRBJL+EaD4s2fKDx2BlaBJk4nP2LxoJp1jZQeNFo1hmhLsZcPXl2E9K64XGJtvm2flHabFfKJw7SWtKkmjkszCvnlHCJaA0X/wDw5vJFZSkpxCqXAfmMoyT0Y4+50hBepIL7ZQZZ1VhfLUDt8IJsueccnsBjDFECyaxIDGsxnhjBREA7CMMtukeKVG4mU5wKpmi+3p7vSBLRLC0JJD4aPtB05NSPtoHCUpBCsj7NDMbtNASVOxfg5dNh9YkFnqCT4QT6RKhSXIGYdusbWkthA1Dqf1jORtC+a7xkQLSoknKpjIHibY5u20YlBGaVe3IwfabImSlkhnyit3PaQiWqa/2MvN4s0386QleuZ+cFOKlRidCD8UpKn5w5st7iYliWgO2yQUs2kL7ChqRFnwY8lyitoOE5R0y+SLuVhBoXHpBMizKSfDltrWFUm8VhKU4sgK9IaIvcMCU5s5579I2OHCvoa8k2Jb2vRMtczCCVEgJIZqip+XWKbbpn+ZWQQCpq0oavXentyia2cSJM4vLJKiplO4YElJqxyIPnC9akLeYp6UqXajuHhrj9gJ/R5a0ATMQU4KWJOZIKjX1MBT14yED7+9ojKji7oUXFCrJvKkGSUBCSc1Kp1P0gr1Q+MaIbcoBIlp2b4RWbdN/1Dv3R5UMN7WakatU7fZis3xOpgFG9uXXfmYzs16AbnL2qWd1fIx0+97vtUxKOzmYJNMeHxMTXLlHKrKcJRMH6FOemsfQnCs0LlJ5iByakmdi3Fo5ieGMKlfmPTuYSpyXDFYVyxOBSoZmc9B4KuTskkqAdYegao8hFlXd0od4pT6QVYik1GUc99m6itHHeJbrM21LQCRT51gK23KsOpKQCQ1GAyYkBqEjbc7mLPxavBbAsefSDbQElLwvm0tDeEZPZya8bBgRUVEKpYoCaPFq4tIwqbaEKLO8sf1fWG49oRmXGVIhwFuXtG9itypUxK0mqSD6adDlGtoGBWFYoddoGMsgsctDoRyMFxF8jt9xXkidLSsAimW3KHlgOZjl3+HtuYLQokagedX+/jHQrHasJ3BgY6YuSHiV/YiKduY0mWhhRn3P03iNKwKkOfusOk0AkeKc9IkQdNIHnLP3rGxW/38ITdMOj1YAMBXiNdvv0gifN2FW2iK3HElx1hsHTAkrQP2KcIwjYHzeC5aBi7RRBAHdTur7eBkpPZFQFRGiirswda+T0EFJVIFO0KJ4UpRUdS8ZA8y2kEhk56qb2jI44Cue8ZZR2a6OSz0dy+cXzh5WFODNJqI5yuXjQhTM2sXzhOaJcsPVvaEyypRTkO4W9DSdYcRbnAyLOlLp1EObWclphKFErWTtEkpTUgko0SpHcTvDWRKBSzAkJdjlTls7QntaVJWg/oLD2pDiWWxHaWT0y+fwiitmexyy+SVzCt3ClEgjOoFB6N5QEqfUJNHYqA1OgESznUWagVowrUn3LQDInjtlrIqksAcqBhTkxjmw8S2M5ysBClZkOE+tTvyEDLtKsIqyi+f6RqT9ekAT7aVTO8SSwpuSSw9vYQJMmu+JTOe90FfjHIeE2m2MgiWXJc4tSeWzaPFaWlzDW1WsKSAkYUHJIzIGqjtAaUVcjVgOiX+LQxCZOwK7QFd3fLbp8Y6v/AIfXl+UEE1RQ9ND97GOPsxcaF4tHDN+dnNCzkaKHLX3r5mMyK4nYZVI7DaLxKiEg5xNaLPaEhJlTBhALoKXd+ecJ5awoYpZGIihNR6RBeE22F0m0yxsMCkj0BPvCI7LFDk6RXb4slpmTcU04Rk3KCJ9sZISDVoUXyJ3iXOQScsIUTTq0eWZJly8S1FSjv7RzQco8GJOI1uG3LfM/CB5dEHkoe8D2m19pN5Bx1JguTXEnnTqmKIKkQTfKVgd9rCkAjNPdPy9j7RDdiSEknLY5ffOJlS8QUDzPp+0aJtAdQGVAPJv39YJsBLY74ZnJRPBNBhIIz1Fd2oOkdKu0OcVco45YrSUTgoafX4R1a4LQUy0EvhKRU5fdYBd2dNaLAX2aIyvnGKmpp8NIDmzVJWAKgh2b5iOkm+haoPTvGmQPw3jFTXAcHpoI9s6nro+kA0EgYk4jmQ2T155aRIkigO/7RgAJV1+/eIp4Z9xWBcmmbSaJraAgJzNXYZkNX5Rk6qAUpLGtaeUe2chYQVGuQEIeI7xWpRRKWUpQKkFnL/s0V5JxjUvkTCEpWvgXWi5gpRJKnJ2jIVKts1JICz5msZCvzx+Bv4ZfJardYpaZeHF3Rt9Ya3AEmX3Kgfecc4m3njkBLkq19axceC5yghI0hE8fDFUe/kJPlLZeUgqlMMxCxKCnEFZmCvxISCXaA5dtTMmNtWAU5Tjxj7e5jSTCbwDlIzZQPo8GWUkCaoVIQwfzLQtlzguYGLgqI9Cf2hpMZKJpdmQrq7U9jDYvZz6OV2yi1g/xqLDLNwOnXaFHYFZDUNelO8XbSGM/vKerlzoWbfbQx7JA7NVDiAI/+nB9mEZMbiK2mYvEpiyzV9RQ1HqmPU2J2Fa1flmfYwd+DZcxVB4E1yqHOXNMFSpZK5Y0KKmniyFRyHvB1oMCl2cDTX2yPz9IBkjEH3mD3B+Qgi9bYErIGzHl9gGFhtmFHqfMsPYD3jUzJJLQsmnvHn8o0SopPsfv1jV9Y3Upx9/esMEFu4R4sMkhE11S9CKlP1jpihZbTKBKgoGoIUx9o4JZVMsRZpEpSfCoh9i0JnFJlOOTZdrbd9kluoVPNTxz/ia+8ZKJdE6n5CN7XLUrNSj5wnttlYR0UrtnZJOiGyFiOX1htdloSCxoSaelfaFUtNYZ2axJmCimOhFfIpLGGMStDa8LOiik0BzGxI26wmstiAKsSu6l1E8moOrwUbFMSlioKHWvligGWe/hOR9/3jjfcHTPrQa/Yi13DxP2YSiYk4QGxs9PIRU7xs5lrIzGYO43jWzKUDTKNpANnWjxNZgkKE5JB0DlXpp5wq/61JL/AIcqTzXhI9i2kUidJBDs2xHTXyiWxJLVIAyBennyg1BJ7Ao6nd1+S56QQ6Vayz4/bMcxDIzaBsuWcctlzWqqrHMO450q3PMReLss6u6oT5hSQ4BUFBjnVn9zAZI0rRqHElRBy10+ERWqayxTQjprEs+0ANV2zMB2j8wknL9MTvSDQ0u6WAAToCYpstbpUdHD05/8RcsX5Km/g+UVdVmKZD5ZPTVxB50+eNfT/wDDML8s3/BBMAJJYRkaTiMRZ84yF0NsK4auBE6RjKmOnKLvw5dRlAA+HIRQLinmVNwEkIBblnHS7DeKVMkEGJfF5Jxlx9mZBKrA+ILEsIXhNCIW8LWlKVhB8RoItF8oUtGEFoot03fNTbpRNUhT+oI+Jh2Ga48G/wCASVuy43RJKbRMJDIClEDbP5B/OH8oCZKVo6T7p/eFciUrHaCaPMUANGZgesMLn/MQcwGf+0/WHw7oGXRzS8pCUzVgAkuU1GzBwGy1gRUxsTV5bkQxvdZE5RyCi7Pk5ELbatIBWvupPh3LbD7zgqsZjaoiVLBlPmolSgNyFzAkeqkR6uxKTJxKVkCCQaklhhS9H3V6PCObfBBxMRLIIHQ96h/m3gW2X8tTBArknZIOgGka1bDUlFA9uQlBxTM9ED2fb4/GFS1Fan02GTQZMsLuSSpTseatQOQoH1gufZRLGHMgOrYHaC6A2xVMSQHOvwiOy+LlGWqbiJzYRHZ1MaHOjnSNrQLasZ2K7iZgGY/4+sXSRdpKcjEfAHCVpm/mzAUSzRBI7ytyBtTM56PHVbLc6JYoku2Zz9svaC/C5/RqzxgvllEsXCK1AKmFKAa1qw3La7JFTyiLiDhZCZLS0lyXUotiIDMKUAzOEatmzxfVSWI2GgrEd5IGGvdHNP7w+OCKVCJZ5Sezk1zcHLmLGIhCMnYlXUJGbZ+VWFY2vGwyZRCCcNGAzfmpQoTXQADLrfrPaEuQxPsPMAuRyJA6xWOK7B24JKVAiqSU5gbAULZx34Ulo78rbKdbrNLzlqrtv0bPpCdRY6CsTWiyrQpTg0zph6094gnOflv0idquxt2FTLQSK5fPfqwjQMEgiPJYBQ25Psx+Z9IGQohxnWMOLbcliTMss1aiHDNyOEp+YiFGGzKmS5yXlLJS7VSoMoKHqKajpCqwz+zQe8o4iHGSS1Q/NxnB183mJqFoOEutCgrIg4QCAOifjFEZLiKa2SypXYzQEqCkLGJJ5EkMef1i7XcpWFKUFkgeEBm6ekcqstrKO6S4DtyrVo6ZwrN7SWGUDV65jev/ADCJv2D9rG0xmOQHz5HUwPLmCgCnbMs0HW6zKCQXDPCuzysSgAwLs+e0SzTUqGRaoc2qbhsqi9cPyhULaF2U4hkoA/H5QdxHMwWYh3cge8JbQGskulVKKj5Uh2X9qXxEDH+tv7Ei5gc0jIHXOqa+xj2EjTa+qyhMRyeJ+C7RMM0EEkJ+cF2RKbTOmSpbBByPxizWThhNjkKWFOamvSC/NCSprbV0A4tdEFvv1ZWxoPjDS55uKbK1dQ+sc/vO8e0IbTaLFwhbP8xJS71+RiB4acZJVsNv2OjqAIUefwpG11FKEL2fCPIZCALvmqXNmJJoksBBd3yMQQo0/MmKbcuwy0avpHow9QqXRz29JnaTxJlSytYd0gOQdXcsBzOUDX9w6UJ7S1qBUBiTKSXSlI/jOr5BPWpi2XBLTZRaJyu8ozFMTm5JLPyDCN1ykTwlSqkqxrUcqZAdNOdc2izHjVbFubXRya/kLXiUpGEFqaCgJDabtzhTddmImKcP2YUR5ME+6njpHGS5RlgACiifVx8VRXbssAKjibCavuEio9SmFTVNjo7SF1jlhDFeaR2h3qO6PhTdfKF1qmFWI6uH9VGMXbMSlvmpWI+YdvcDyg+wgJWlag6ThNORI9sT+RgIhSYy4Q4TM7vdkuYgviAGFT/yKUGd2zLHUbXfh7gyzIWVKSlYSTgCk0TWhPNtPN4MRfYwAy3Zh6bwbJtBCcVTiGJxhLg123cU2i6MVRHKTscS5qU00GzUjVagpyhbtmksPrFZnX4hjWm5HvmKQum3wuzKWqapaQligIlkhYOqSZgDM4IxOC7galQI7tq04q4Ru6gG9VPEk6xLEvxBD5Elkn6ephNZb1RaEKnfhglQDjEp3DbIThf+U4qwstl5HsCxXMnGoSwAA5gAADkI3SRyTbG06ZKsgTNnJVOUT4UTAUPzxKqeQERHjBU8ES8KUk+EsFsN6tFBuywz7Va+yC1doe+VHJKRQkgUo4bo2sdBv+4ZcqyGWgB0oJFKuBm+5OvOJp+IS6KYeHb7El92Cz2iqiZU0hsbKCC+TjvDzHvFIt3D0xHibuu+E4shQ0yc7wFMvW0yywWoNmHfq/tU+RidF7T1IGM9okFmmd4peoKVE4xlk7dYycosGKaF6lYSoOzU8wS/0jVcxJqRhVu7g9doON2hYKwokmpBr8CYhlWX+LLXlChlBNiUlaChTE7GhP8ASpvYxqLrExTIOgABNXrBabqHdKD1d2pmfT0zNKjLTZF+JIBA/Wkln/lfNhUn7LIJgSFloudaCAQakgdQ3yIh/wAI21clYTQpVTmD11+/IuzXkmZJSiaHUkpUlWtDnzcEuMqRLe92pA7eRUACYpDucJ8RD54TnuC+8MniTWgFJ+5YbVaCWSVD1iKyhTirMaFucQSyJ0sLBq2dT+8TWRbFL6HoDHntecen5Q3iuYPw7MxxD1aF99TB2Elhkn4tBXFankp3C2rrRqQJfMt5KDqBl5CHZf2v+AQ/Wv6JpckkPGRkmahhizjISGW/hXhyTLSlT11hTx3aVomCXLmEoIqmBrr4kSggLVRqQovu+kLmlSatHn+ChleV/k/7DlNVoLsFlAS5EEcJFrfJc/qIHmktFZtF+rUGTSPLrta0Tpc0KIKFAuMxv7PHpQg1bYq70dguO1LVaZ6y1KAAuPGrPyAh1e9tVJlJw+Iu3IkM7eZhNw5YBLSsuVYykuTnBV+AkIJoQCVEs2f7ekDBmzPbvu8KlHtD3U4lHmSfjn7REqzKUUoHdT8Br97tBtztOSgg/lBPaLL0UR4Uvk1PjCO/L/ClrEpylNO7mX7ofZyaO2u8eknSJ9tiriiWhaFSkDEpRd9hn5MkgeZ5RU5V5IRMKQXABc5jxAqY6/tDK128H8sqHeDrIeicyHPL7o8UiZbBMnFSQQKhKRRk6eesS5PNIqj5YmTJCpdoVRwCSNimvyIiwSQjsnSXl1I3APiHlU+UK7QlXZBSwAfCk8qFvvpAt1FSSUB6pKjs+T/vGWdVMufDl8B8EzJA8QIctrhVRQIzS7mhDKrFhs94pVMRLkLSuWo91nJD0IILKBCmcKAI1581s0iWuckLBUFFmTR82L6B/nsxv1zWNEi09wEkAJUOS0KKRU/poc/0htoswybiTZFTIuFLQkzyicEGW6lBaZyFBOInA6MyCKEe0NL5PaqSiWkjAThAVVsgAQahgGO2+cYtUuzpPZpSUJA0Ylyauz1JzgQpTPJ8SFZgOFI5+FOMde8OYhgH2AXuJtjkqmoASr9acIAU4qFdzm9CDzakNbm4MBlBwU4u8WOT9YSWiQSUypqsAK0jDiKkLSFgqwh1aPWjDpHY7JLASkAaRH4lvSKsFK2Iri4clWRJwB1K8Sz4i2TnYOaQDfqu4t9WHz+kWi3LASekUK/bXVupiKRXj3s5/wAe3fKQiXMQ4U+FQ3YEg+Rp5xXbqmHwhQrorI9CY6BeqJapMxC0hRKCxP6TuObxRbNdykEFQoS3I7g84fiblGhOaPGV/ISZxlOFJPqacxViIhFqIJI1zBjSfaACzHDqCXIfUGPCRhGI1SGB/iSapMGKs8VbyOaYbSLcog6kip0CXBbkHAhGJJLtR4a3QCE4TUE5bnIdekNxvYEiz3VKlzpbAPNS6kAZr1Kc81JJZ6BTPE8nBZ5qTMH5SqBWgKgaHkQ/UFL5wrsMjCrPuKLYhRUtX6SAMw9DV2roCHFvtUudLnSJ7CakANTvFix8yUGmwioQzSyBKEsB3QWYZt+lXMFLV/eDrNNRjSUgxVrltJSlKVOQfC+YcmnrD+yOFgg70H3SPOkqyIo/xJuLJriSl81/fxgm2JSqWCosGqBrXKEvEK8VplI0Af1L/KGNtmUArSOlvLI5KoRFiykEsmnR4yNFpJJzjIEIrku5Jy0GaACgc6nygKz2MnvMWjdF8T0IMpK+4dGD+sXHhWWgyQpbUD+kIy55YVyl1Zqin0VWXZGzDdYIllKdY94tvwKXglig1itonqUoZkvQb8ooxyc4qTVAdH0BwdNKrJJJ2brhJT8o34tl4h2YFCls6OxVl5wfc1h7KzolaykJfq3e9zGt4S3WhWOhSlKiM0sHNOYgEvNSNb0KpFxJXZpEoKYJCi6j3TokEZFy5ja9buk2aVJk0UpasSzk4loUXIG6lJp9Ie35JSpCBokUG5+dfWKTYOH503FNWo4ErCUvonEx9j7R6KRPZReI54/EWoISEp7VaQ2WHtFBP+1MK0oKBiABJGoizcUXQlNqmy0HupmYCToopcA+YI5NCqVLZOJQcy1YSjp4fcHOJnHspT6JbTZlTOzRUkgAPo+avpsImtlhEpLJzWGJ2QPqW94Fu28FYyVZqfqB+rzagfcxtfF5YxhHiUWfYAED6/8AtCqsZaSsk4Usgn2hKRQBSEA7Akup+QB9RFrkW/tJqsIwhnWdT2SC4HPvrc/zPFT4UJlyFqBIKpgA3ZIqegLe8XnhewJUk4ncuQrcTUqlk+oSrzEXYlUSSb2e2BWJBWsOlh3Fd0LRMB7RD6MApaTo2YqTEiwy1LPZrSqWwUhQJRMSpgAWcJVUVYguGHMW/wA4iLJVJSJU3EKUAUlt6YG8ztA86b2KQkEJoyU6Hk24hgFWQ3/ZT20rDhBxOSPBiSXcDQ64WoXjsthBElBXRWAFQ2LBxHOeBrl/ETBapqSEJU4eoWoOKP8ApB9WA3EXm87xGT0EQ+ImrK8UGLr9tba5xQr6nkqYaw+va3Y320inXhbUhRKiABTqYjW2Waijy129EpJUrJiK6wos1qC5CEs610bXPTqdeu0a3mVqaYB3cuY5xDZ7N2IE0rwqeic1nmdgM66xfhxuCIc2Tmxfekrsp60AhWAsTvv7vGkmX2iu6hRbQaa6+cOL1sw7OWogYpmJaq6qdn9acg+pgS6LGvGBUOWYOMQ89I2UPMApaDuH5KDOQiYO6VB3/SkVV54XrzEWK23ZLIV2Q7NBybxF83OYBP6QwbekeWG7kTO0UgDDLFVGjtrl/EwbPvcmgRVqIQVmiXLPyOfq3rD4xUULcrZFIsa5auzUXQtgk7E0Hk7eTwPfNss62E9Ku3GAEpzOBwAf6khPtBc61FUsL0KW9DT1cjyMTcUXRJVMVaFzUpUUJUAC2JSmXvq6qaONo1r4Mv5FViIElvGxIQrVQdwDsoUPTFs8Pbmn96hJxAmuYOoI3eFlksfi7IulQdhmCFUI2ILEddiYb3RaEL74ZKmaYhm7wpiA0B+MKnC2mby0xfbV47ZVqJHr9mD7bN5/fWEchZNoWvIPR9WpBVuVjOF2o+2oMS+7Y72QLMtlS+OMhUJSl95zWMgaCFE8ViSTal4SkKIGwMRz6mIZBrB0n2CSS5W8NeDbt7a8LPL0CwtXRHePwbzgERff8HbC8+0TyKIQEA81Fz7JHrBy0jDrVjl4yoEtiDe7/GALTYvzMKjT+U0LVFW9oMSvDLJdtj97Z+UKpd4iYO93VVU5Zs6V3Yj3ie0v6FTY6sskdmFrzFOpGvqYRcQXivs1SpeoLDdgT8cMNpYVMCEOQlKTiJrk5c8zSF9kuwJmGfMUXclKXolISpKE83KiTuQ+0ejHomfZRrTw7aJ9oKi/fWpKzoJgxLr5EF/rAy+GgDMxKJYg0LYgpGIdGGu5TvFsv++ZisSJYAAUlRUPFioHHkG/5ivruq22ia+FRcFKzl4FVB5gqCgNUlLZQTQSYivS5Ey0y5shYUkuFJNFJLOKczWK5+DmDvlJYfGnvlF7vPh1UhcuXMWkLXLKkF+6paF4sL/0EB94gum8kfhuymyyVJUlSVMyDmznIBi+bVMLeKLYX5HRXbKCvspKHGNalN1CGHR0kdTF3uu1hI7NSuznylN2a+6Slsg9FBmIIzFCBnCWbZUJItCAhWEgBIWpBpkQQoKNAMkkGsHzrcbV+bacCSkfloUhVUh85iEBRr5coZFcdAS8wVb7ZKWEKUodslxQsVBRqGJc94AsHq7Z184c4RVbV9pagUyEqxBGRWdRuEehdwGEMeG7oTMBUtJD1dJIps4UyQ2j1i5zJ6ZaAAGAGzekI8RNrSHYYpmWyclCQlIASAwAoABFPva8Mw8T31e2bGKr2c20OUEJlgspaiwfYc2qTkB6RDGLm6RbahG2Q3pfGEKSgY1sTQ5N9IQ2K61qONYJUqrmoFH8of2bhySEkpmTFLW7ucII2YVq9AS4cPVwH/C0yVIGCaQRooioDk13YkNtUZR6GLAodkWXO5FGny1J7qmAqDiJoQa5feUK7VgQyQaE1KRpqzny0i83zaLPOmEABKcRD5gHJCujMCNiWyDJpnDMuY/iQoUYHLyLiMy5FDs7Hjc9o8stvRMwpA7wFMQKgkAbANzKieQaFclSjOVjUSTQnIsdho+UFnh60Sn7KYCDmCGLdf8AiNrHY04mmdqmYo5kAAuf0lOLzKqmNhljP3MlilDtDQ3kmVIVKSwKiC2gAcknk5B8hEXFU1H4eTIlkYi2IlqfqW7bKwHyO0RX3cSkhEuUkqWovMwOoj+EFyToTs1Yy4rtRiSLS+ITZxXyCZSFV541ENuYc76E67GdnuIGWElZSFolWdIOkxRVPJP9CWfqRCafwNMmiXM7YYTkTpLThYk5USYktV/FRlywFGYFFRGQddnSmYScnxA+R5wqve9p1qnqlf6aPCEKoAEk4gG0OvIcqY+PucrNJcpdnmSlyl9pLIP9JB7pCuSklvOGJnCYoqS6Z4LHTtUZd7aYls8izbEa8O25VlmETE4K4VYw8vSihpm+LJi7tDa8EYp3aCR2ShRgWRMDVDgs9O6eQZ8oytGtiVEwOysRIBqlnoa/KCZlkMwDAsAbtUwDaJgExTZDXdydNMqiBxbFIONJ7x9B5RJJpSpoak2tFnkz7OhIQQQRRmMZFam29aziLOdhGQX5F8AcH8ltl8I2GYmSrDOHbh0kKPd7hVXQZerRVbz4GtUpSlISJiAvCliMZBUySU86RbuIr4/D2KWLPaZXaSwhKgkpUVBmoC7MWL8oJmcSWcEWgT5IQpKElOEmee8pwe84CcT5H9UFRnIqH/QttdsMvJ/HTplnHTv8PuHFWawqTMH5q1dp3C6SFd1IyrRMA3HaZRWuXLnImqmLXNGA4sKVYQHbKrdfKL5colSZEqSFg9lLSmlXwjTfKOkjlK/cX37LUiWCRQaUYlmah0BJblFas9hCUWhW2IjyLp+nlFpvmYC4xIKceJkiuWZJNC5Zmiv3+vspK6ZqxciHp5Ev5RHlirH45Ohpd8xUuzoQ7qXU8k5+VSk9IFvBSxLfcsPOvsC8acI2gTXlq/1ESytT7K/cmGV4S+2IQnwoJJ8qD1Dx6UHpE0uwaz3OiXKTNWXdGPqpaAD6Uit3he00zzJkrCFTEuok07svuE7EkCvOLRPsykIAmnGAAEoGRwpGfm49DpFVtN7pTNVLwpQVELmKbEpYKg4JDYe6MIqQO6KkwwwWCd2TTZsztUkkJVMTiHeJBKErCgRR3ATnnrHkviS0TpU8zZk2RLShPZoAAxAmr90BValAU5DsM4qt9Xz+In4lulAJGGrDvHxAsXycHybKLLxHa2sslCJqjJUC6WxyyQQWC1DtJahXuF9Ms4GwqMuyypnKThlpSktiAUrDiyKgW7oarEUi73vZpCbJhCkO4YoXRzSpxEHzaKpwhY2lKtMoJmKl1KCVJWM3ok4hQFlpJGYKczAhtMufaioJXJxsXCELKaV7rB0nN2eNOL7w5ZZ0lACkIIIcLSQf9oCT559YUcS3hhWRk2ZCSkE9CT6xuFSkSRMl2lOIBwU4k4g38Ib2HlFJvC2TbSs1BYOpTBISNSo7DcmJs+/KijBp8mbWqcqdiZTJT4mqsuQAEgP3iSANyRmaQHdkhc6eUJl4MCVESwcTFIS5J1U6kB+fKD7JfaZIMqzgKUxeeRULIwhaBoUhRAPPLdnwlOEleIVKUgc2FRn0T7wzFiUEBlyOTsHVZ1Ily1AHvnCCQaEVrs6VIPQHUQbed1LLLCaLQF0qcQotOxdn8+cb2u2EoWlOR7yX0wgsPRq8o1F/YrOhGSS4J1TiIwt/7D4Q8RbAJN0IBGGYGUMjmk5Ftw7ODk/KNZU7CopOYp6f8EeULpdrMwsotV3yBoXroW15RFb7UApSlJxKT4ncHme6aEK3p0hGbHzjodiycJbLXKUCIgtdmxEEHCxfzGUV6z3yUM4ZJycg/SGsi9kqjzZQcWeipqSA0WufIE5KJpS4fEUlS1FRqQcn5mgYZkQmmWuZKV2VnAWVkJxlJoXSTTKgofjQxbDOQrNjGtikS1TcOXcUB1Lgf3xXhzttRZLmwJJyRWEXjaPw+E95cwrSVqbEGKDhDBu8JZc6udY9svaWhZxAdsipQkspkUOEZOcq0JI5xFeEkJ7RBoykqTVhiSVOOmFSh5QbbLuCp6OyWRjRMWheSgZasJD+XpFmyNji85KOzQiaD3QwmpT4kNmxr3cik1SN2oJZZs2yqknGmdZFnBi8QY1HMFJq2Y6Bogk26Z2Z/EAll9lPw+IMDgmpH8SQCkjUBOgj1XdlzZWIJVgCwR4FsWCgC9ci2oI1aN7M+hLf6wmevSrN8OsKZi3ETXhae0XizBAO7UygMmoiDL6mUw6DADGRJKRQVjIJR0A2Q3pnCuZHsZABl7/wjURPmMSKysuq47DZfF5fOMjIPJ6UKj+x/wC/AotBe0zAchhppmYScRKJSly/dH95jyMiNlKJOBT+bbuUpIH++LdY6Cb/AEj+xUZGR6WH0IlyeoivNZEuaoE4hKWQXqPFkdI4lJmq7K294/6CNT/3URkZDGZDo84FSDOlOHcl3q9DnDiYHlF64bVhHJOKYGGwYANsBGRkdHo2XY5ljBKSU909thcULMKU05QTw8gFyQCRMlgE5gFRBA6gNGRkMAAL1DptD1YqI5ZZRXr2OG75bUxTyFNTEAhw+7GoeMjIm/yf8H/4o3tcsJmywkADs7MWAYd7GVepqd4Ouk/5icNO6G/9RHkZDUKD5Z7g6H+wQFZ0g2VdPtoyMhgINwqXnl6vJQovqezSXPN6vHl6JAtFA3dOX/ijyMjPZHe5Ba0gyEOAe8v4q+g9IV2ZRCjU6RkZEviOijB2OrMo7wdcp/zMvqPiIyMiXF60V5f1sQX94j/5Jn90T2NR7WxV/RO90Kf1jIyPSXZ5z6G/EI79q/rs/uUv8T6mF0oP2b/9qZ7FbejBugjIyC9/9+QPYqMjNQ6fCNj4oyMjz59sqj0MbP4RGRkZD10JZ//Z" />
</p>

Some options to overlap rotations, borders are also added

Dr.Sai
  • 303
  • 4
  • 5