Resize a div using the drag and drop handle while rotating

I could find similar questions related to jQuery UI lib, or just css without a drag handle, but nothing with pure maths.

What I'm trying to accomplish is to have a resizable and rotate div. It is still so easy, and I could do it.

But when turning, it becomes more complicated, the resize knob performs the opposite calculation: when the size is reduced, the size decreases, but not increases.

Besides the calculation, I would like to be able to change the cursor of the resizing knob according to the rotation, to always make sense. To do this, I was thinking about determining which quadrant is the descriptor for resizing, and apply the class to change the cursor via css.

  • I do not want to reinvent the wheel, but I want to have a lightweight code and a simple user interface. So my requirement is jQuery, but nothing more. no jQuery UI.
  • I could have developed to achieve this, but now it is becoming too mathematical for me. I am completely fixated, so I need your help to find out when there is enough turn to recount the result.

In the end, I'm looking for UX improvements if anyone has an idea or the best examples to show me!

Here is my code and Codepen to try: http://codepen.io/anon/pen/rrAWJA

<html>
<head>
    <style>
    html, body {height: 100%;}

    #square {
        width: 100px;
        height: 100px;
        margin: 20% auto;
        background: orange;
        position: relative;
    }
    .handle * {
        position: absolute;
        width: 20px;
        height: 20px;
        background: turquoise;
        border-radius: 20px;
    }
    .resize {
        bottom: -10px;
        right: -10px;
        cursor: nwse-resize;
    }
    .rotate {
        top: -10px;
        right: -10px;
        cursor: alias;
    }
    </style>
    <script type="text/javascript" src="js/jquery.js"></script>
    <script>
        $(document).ready(function()
        {
            new resizeRotate('#square');
        });

        var resizeRotate = function(targetElement)
        {
            var self = this;
            self.target = $(targetElement);
            self.handles = $('<div class="handle"><div class="resize" data-position="bottom-right"></div><div class="rotate"></div></div>');
            self.currentRotation = 0;
            self.positions = ['bottom-right', 'bottom-left', 'top-left', 'top-right'];

            self.bindEvents = function()
            {
                self.handles
                    //=============================== Resize ==============================//
                    .on('mousedown', '.resize', function(e)
                    {
                        // Attach mouse move event only when first clicked.
                        $(document).on('mousemove', function(e)
                        {
                            var topLeft = self.target.offset(),
                                bottomRight = {x: topLeft.left + self.target.width(), y: topLeft.top + self.target.height()},
                                delta = {x: e.pageX - bottomRight.x, y: e.pageY - bottomRight.y};

                            self.target.css({width: '+=' + delta.x, height: '+=' + delta.y});
                        })
                        .one('mouseup', function(e)
                        {
                            // When releasing handle, round up width and height values :)
                            self.target.css({width: parseInt(self.target.width()), height: parseInt(self.target.height())});
                            $(document).off('mousemove');
                        });
                    })
                    //============================== Rotate ===============================//
                    .on('mousedown', '.rotate', function(e)
                    {
                        // Attach mouse move event only when first clicked.
                        $(document).on('mousemove', function(e)
                        {
                            var topLeft = self.target.offset(),
                                center = {x: topLeft.left + self.target.width() / 2, y: topLeft.top + self.target.height() / 2},
                                rad = Math.atan2(e.pageX - center.x, e.pageY - center.y),
                                deg = (rad * (180 / Math.PI) * -1) + 135;

                            self.currentRotation = deg;
                            // console.log(rad, deg);
                            self.target.css({transform: 'rotate(' + (deg)+ 'deg)'});
                        })
                        .one('mouseup', function(e)
                        {
                            $(document).off('mousemove');
                            // console.log(self.positions[parseInt(self.currentRotation/90-45)]);
                            $('.handle.resize').attr('data-position', self.positions[parseInt(self.currentRotation/90-45)]);
                        });
                    });
            };
            self.init = function()
            {
                self.bindEvents();
                self.target.append(self.handles.clone(true));
            }();
        }
    </script>
</head>
<body>
    <div id="all">
        <div id="square"></div>
    </div>
</body>
</html>

Thanks for the help!

+4
source share
1 answer

Below is a modification of your code that provides what you want:

$(document).ready(function() {
  new resizeRotate('#square');
});

var resizeRotate = function(targetElement) {
  var self = this;
  self.target = $(targetElement);
  self.handles = $('<div class="handle"><div class="resize" data-position="bottom-right"></div><div class="rotate"></div></div>');
  self.currentRotation = 0;
  self.w = parseInt(self.target.width());
  self.h = parseInt(self.target.height());
  self.positions = ['bottom-right', 'bottom-left', 'top-left', 'top-right'];

  self.bindEvents = function() {
    self.handles
      //=============================== Resize ==============================//
      .on('mousedown', '.resize', function(e) {
        // Attach mouse move event only when first clicked.
        $(document).on('mousemove', function(e) {
            var topLeft = self.target.offset();           

            var centerX = topLeft.left + self.target.width() / 2;
            var centerY = topLeft.top + self.target.height() / 2;

            var mouseRelativeX = e.pageX - centerX;
            var mouseRelativeY = e.pageY - centerY;

            //reverse rotation
            var rad = self.currentRotation * Math.PI / 180;
            var s = Math.sin(rad);
            var c = Math.cos(rad);
            var mouseLocalX = c * mouseRelativeX + s * mouseRelativeY;
            var mouseLocalY = -s * mouseRelativeX + c * mouseRelativeY;

            self.w = 2 * mouseLocalX;
            self.h = 2 * mouseLocalY;
            self.target.css({
              width: self.w,
              height: self.h
            });
          })
          .one('mouseup', function(e) {
                $(document).off('mousemove');
          });
      })
      //============================== Rotate ===============================//
      .on('mousedown', '.rotate', function(e) {
        // Attach mouse move event only when first clicked.
        $(document).on('mousemove', function(e) {
            var topLeft = self.target.offset(),
              center = {
                x: topLeft.left + self.target.width() / 2,
                y: topLeft.top + self.target.height() / 2
              },
              rad = Math.atan2(e.pageX - center.x, center.y - e.pageY) - Math.atan(self.w / self.h),
              deg = rad * 180 / Math.PI;

            self.currentRotation = deg;
            self.target.css({
              transform: 'rotate(' + (deg) + 'deg)'
            });
          })
          .one('mouseup', function(e) {
            $(document).off('mousemove');
            $('.handle.resize').attr('data-position', self.positions[parseInt(self.currentRotation / 90 - 45)]);
          });
      });
  };
  self.init = function() {
    self.bindEvents();
    self.target.append(self.handles.clone(true));
  }();
}

The main changes are as follows:

. .

( - Math.atan(self.w / self.h)).

, (.. self.currentRotation + Math.atan(self.w / self.h) * 180 / Math.PI). . , , 0..90, 90..180 .. , atan2.

: , .

+2

Source: https://habr.com/ru/post/1657720/


All Articles