Rotate image

To rotate image using handler only.

by Twisty

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.1/jquery.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.10.3/jquery-ui.js"></script>
<script src="https://cdn.jsdelivr.net/jquery.ui.rotatable/1.0.1/jquery.ui.rotatable.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.10.3/css/base/jquery-ui.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/jquery.ui.rotatable/1.0.1/jquery.ui.rotatable.css">
<script src="//demo.prestalife.net/modules/customtextdesign/js/resizable-rotation.patch.js"></script>
<div id="box_wrapper">
  <div id="rotate_limit"></div>
  <div id="rotate_handle"></div>
  <div id="image_box"></div>
</div>


dial.offset().left: <span id="left"></span>
<br> dial.offset().top: <span id="top"></span>
<br> dial.centerX: <span id="centerX"></span>
<br> dial.centerY: <span id="centerY"></span>
<br> pageX: <span id="pageX"></span>
<br> pageY: <span id="pageY"></span>
<br> offset: <span id="offset"></span>
<br> newOffset: <span id="newOffset"></span>
<br> RAD2DEG: <span id="RAD2DEG"></span>
<br> r: <span id="r"></span>
<br>

CSS

#box_wrapper {
  position: relative;
  margin-left: 180px;
  margin-top: 100px;
  width: 180px;
  height: 180px;
}

#image_box {
  background: url('https://placehold.it/80x80/c9112d/fff&text=Image');
  background-size: 100% 100%;
  position: absolute;
  top: 40px;
  left: 40px;
  width: 100px;
  height: 100px;
  border: 4px solid white;
  -webkit-transform: rotate(0deg);
  transform: rotate(0deg);
}

#image_box:hover {
  border: 4px solid black;
}

#rotate_limit {
  background: transparent;
  position: absolute;
  height: 180px;
  width: 180px;
  top: 0;
  left: 0;
  -webkit-border-radius: 180px;
  -moz-border-radius: 180px;
  border-radius: 180px;
  border: dashed #ccc 1px;
  z-index: -1;
}

#rotate_handle {
  background: url('https://s32.postimg.org/re2hwf3fp/rotate_handle_down.png') no-repeat;
  position: absolute;
  background-size: 100% 100%;
  left: 18px;
  top: 18px;
  width: 20px;
  height: 20px;
}

.ui-resizable-handle {
  border: 3px solid greenyellow;
  cursor: pointer;
}

JavaScript

var RAD2DEG = 180 / Math.PI;

var dial = $("#image_box");
var pointerEl = $("#rotate_handle")[0];
var canvasEl = $("#rotate_limit")[0];
var canvas = {
  width: canvasEl.offsetWidth,
  height: canvasEl.offsetHeight,
  top: canvasEl.offsetTop,
  left: canvasEl.offsetLeft
};

canvas.center = [canvas.left + canvas.width / 2, canvas.top + canvas.height / 2];
canvas.radius = canvas.width / 2;

pointerEl.ondragstart = function() {
  return false;
};

/*
pointerEl.onmousedown = function(e) {
  rotate = true;
};
*/

function limit(x, y) {
  x = x - canvas.center[0];
  y = y - canvas.center[1];
  var radians = Math.atan2(y, x);
  return {
    x: Math.cos(radians) * canvas.radius + canvas.center[0],
    y: Math.sin(radians) * canvas.radius + canvas.center[1]
  };
}

$('#left').text(dial.offset().left);
$('#top').text(dial.offset().top);
dial.centerX = dial.offset().left + dial.width() / 2;
dial.centerY = dial.offset().top + dial.height() / 2;
$('#centerX').text(dial.centerX);
$('#centerY').text(dial.centerY);

var offset, rotate = false;

pointerEl.onmousedown = function(e) {
  rotate = true;
  offset = Math.atan2(dial.centerY - e.pageY, e.pageX - dial.centerX);
  $('#pageX').text(e.pageX);
  $('#pageY').text(e.pageY);
  $('#offset').text(offset);
};

$(document).mouseup(function() {
  rotate = false;
});

$(document).mousemove(function(e) {
  if (rotate) {
    var newOffset = Math.atan2(dial.centerY - e.pageY, e.pageX - dial.centerX);
    $('#newOffset').text(newOffset);
    var r = (offset - newOffset) * RAD2DEG;
    $('#RAD2DEG').text(RAD2DEG);
    $('#r').text(r);
    dial.css('-webkit-transform', 'rotate(' + r + 'deg)');
    dial.css('transform', 'rotate(' + r + 'deg)');
    //$('#box').css('-webkit-transform', 'rotate(' + r + 'deg)');
    //$('#box').css('transform', 'rotate(' + r + 'deg)');

    var result = limit(e.pageX, e.pageY);
    pointerEl.style.left = result.x + "px";
    pointerEl.style.top = result.y + "px";
  }
});

dial.resizable({
  handles: 'ne, nw, se,...