0

I have an image which rotates as expected using the handle, like in this link... When I try to drag, drop and resize the same image using Jquery UI draggable and resizable, I can't able to achieve the rotate image as like in this link... Please don't prescribe me to use rotate Jquery UI, the resize function does not work after rotate using JQuery UI rotatable. Please help me to work this fine... Thanks in Advance..

$('#image_box').draggable().resizable({
        handles: 'ne, nw, se, sw' 
});
ArunValaven
  • 1,753
  • 2
  • 25
  • 44
  • Seems to be working fine for me... What exactly is the issue in second link? – T J Jul 01 '16 at 12:09
  • @T J.. When i try to rotate using the rotate handle it gets drag, move from one place to another place.. – ArunValaven Jul 01 '16 at 12:14
  • @ArunValaven when you click to rotate, it does rotate, yet also drags. I see `dragging = true;` in the mousedown function, should that be false? – Twisty Jul 01 '16 at 18:20
  • @Twisty... If i change to dragging = false, then dragging related to rotate not works.. dragging is the variable related to rotate functionality.. I use dragging functionality by Jquery UI draggable. – ArunValaven Jul 02 '16 at 17:22
  • @ArunValaven do you need to detect `dragging` in other parts of your script? Is there a reason you're not making use of `start`, `drag`, and `stop` in `draggable()`? – Twisty Jul 03 '16 at 15:34
  • @Twisty.. Now i have updated the second link by changing the variable name from dragging to rotate for better understanding. I used Jquery UI draggable for the whole div id => "image_box", inside div id=>"rotate_handle" is used for rotate functionality without using any Jquery UI. So, that's why when i try to rotate using "rotate_handle", the outer div => image_box drags(using Jquery UI draggable) the whole div moves along with "rotate_handle" div. – ArunValaven Jul 04 '16 at 05:27
  • @Twisty.. And now i come to your point, i need to do something to stop dragging the whole div while i try to rotate. So the draggable functions like start, drag, and stop in draggable() are not able to use now. Please help me what to do now..? – ArunValaven Jul 04 '16 at 05:27
  • 1
    @ArunValaven I found an older version of this and updated it to work with modern versions of jQuery. https://jsfiddle.net/Twisty/vLbr5exc/ I am tinkering with using a transparent canvas to constrain the movement yet I think the `limit()` and `distance()` could be used without the need for a canvas container. Some food for thought. – Twisty Jul 05 '16 at 16:04

1 Answers1

1

Posting this answer for the moment might return to update it once I have worked out the "bugs", yet this does do what you are asking about.

Current Working Example: https://jsfiddle.net/Twisty/npmtfLt6/22/

HTML

<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>

First, we have to separate the handle so that we can move it freely. We also need to contain it within a wrapper. I have added the #canvas for now, yet as I mentioned in my comment, I think it can be accomplished without this element.

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;
}

Minor changes here for new elements and how we position them in our wrapper.

jQuery

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;
};

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)');

    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, sw'
});

$("#box_wrapper").draggable({
  handle: dial
});

Not a lot of changes to your personal code, since the math works. Just a few organization changes. Since you want to drag all of this about, we make the wrapper draggable, but set the handle to be #image_box. This allows us to move the box when we grab it, but leaves the rotate handle free to move independently. This way we can rotate without dragging the whole element.

When the rotate handle is moved, it is constrained (mostly) and the pageX and pageY are then used to calculate the transformation.

The positioning of the rotate handle is not as smooth as the rotation itself. You might have some insight on that, but this does achieve the function you needed thus far. If you desire, we can work on improving it.

See Also:

UPDATE

Corrected the "bugs". Working example: https://jsfiddle.net/Twisty/npmtfLt6/28/

  1. Improved calcDeg() for this specific use case:
function calcDeg(e){
   // Retrieve degree from current handle position vs. circle center
   var mPos = {
     x : e.x - canvas.center[0],
     y : e.y - canvas.center[1]
   }; 
   var getAtan = Math.atan2(mPos.y, mPos.x);    
   return getAtan*180/Math.PI;
}
  1. Rounded degree value for CSS:
var r = calcDeg(result);
$('#r').text(Math.round(r));
dial.css({
   '-webkit-transform': 'rotate(' + Math.round(r) + 'deg)',
   'transform': 'rotate(' + Math.round(r) + 'deg)'
});
  1. Adjusted pointer styling and position to be more dynamic:
#rotate_handle {
  background: url('https://s32.postimg.org/re2hwf3fp/rotate_handle_down.png') no-repeat;
  position: absolute;
  background-size: 100% 100%;
  left: 100%;
  top: 50%;
  margin-top: -10px;
  margin-left: -10px;
  width: 20px;
  height: 20px;
}
  1. Adjusted Resize such that limit and wrapper are adjusted with image:
dial.resizable({
    handles: 'ne, nw, se, sw',
    alsoResize: "#box_wrapper, #rotate_limit"
});

Resizing works, yet I would advise setting aspectRatio: true. This will keep the image square and this allow the limiter to become oblong or turn into an ellipse.

This may not work for your needs, so you may want to consider writing a function to be executed during resize that could increase the width, height, and border-radius of the wrapper and limiter properly.

I hope this fully answers your question.

Community
  • 1
  • 1
Twisty
  • 30,304
  • 2
  • 26
  • 45
  • Improved by using `newOffset`: https://jsfiddle.net/Twisty/npmtfLt6/23/ – Twisty Jul 05 '16 at 18:11
  • Found this and improved the rotation / connection to pointer: http://stackoverflow.com/questions/27766647/drag-move-an-image-inside-a-circle-circular-movement Update: https://jsfiddle.net/Twisty/npmtfLt6/24/ – Twisty Jul 05 '16 at 19:27
  • I go through the above given codes and visited the link.., i can't able to rotate the handle with the image. The handle not working good while at rotate and resize. When i resize the image, the handle positioned in the same place. This is due to designed the rotate handle out from the image div in your code.. Thanks for your effort... – ArunValaven Jul 07 '16 at 06:13
  • I have found one solution to make rotate the image using the handle. Resize, Rotate and Drag won't affect the rotate_handle.. Used `$("#image_box").draggable( "option", "cancel", "#image_box" );` and `$("#image_box").draggable( "option", "cancel", false );` in the code to stop dragging the image_box while rotate.. [link](https://jsfiddle.net/arunvalaven/npmtfLt6/36/) – ArunValaven Jul 07 '16 at 06:17