旋转时使用拖动手柄调整div的大小

时间:2016-10-14 07:37:56

标签: javascript jquery math

我可以找到涉及jQuery UI lib的类似问题,或者只有没有拖动句柄的css,但纯数学没有。

我尝试执行的是拥有可调整大小且可旋转的div。到目前为止这么容易,我可以做到。

但旋转时会变得更复杂,调整大小手柄会以相反的方式进行计算:当拖离形状时,它会减小尺寸而不是增加。

除了计算之外,我希望能够根据旋转更改调整大小手柄的光标以使其始终有意义。 为此我想要检测哪个象限是调整大小句柄并应用一个类来通过css更改光标。

  1. 我不想重新发明轮子,但我希望拥有一个轻量级代码和简单的UI。所以我的要求是jQuery,但没有别的。没有jQuery UI。
  2. 我可以发展直到达到这个目标但现在对我来说太过数学了......我很困惑这就是为什么我需要你的帮助来检测旋转何时足以使计算反转。
  3. 最后,如果有人有想法或更好的例子向我展示,我正在寻找用户体验改进!

    以下是我的代码和Codepen: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>
    

    感谢您的帮助!

2 个答案:

答案 0 :(得分:2)

以下是对代码的修改,以实现您的目标:

$(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));
  }();
}

主要变化如下:

在resize事件中,基于当前旋转将鼠标位置转换为局部坐标系。然后根据鼠标在本地系统中的位置确定大小。

旋转事件考虑了框的宽高比(- Math.atan(self.w / self.h)部分)。

如果要根据当前旋转更改光标,请检查手柄的角度(即self.currentRotation + Math.atan(self.w / self.h) * 180 / Math.PI)。例如。如果每个象限有一个光标,只需检查该值是否在0..90,90..180之间,依此类推。如果atan2返回负数,您可能需要查看文档。

注意:偶尔的闪烁是由于盒子没有垂直居中造成的。

答案 1 :(得分:2)

Nico的答案基本上是正确的,但最终结果计算不正确->这导致闪烁的BTW。发生的事情是大小的增加或减小被2倍乘法加倍。应该将原始的半角宽度添加到新的半角宽度中,以计算出正确的新宽度和高度。

代替此:

self.w = 2 * mouseLocalX;
self.h = 2 * mouseLocalY;

应该是这样:

self.w = (self.target.width() / 2) + mouseLocalX;
self.h = (self.target.height() / 2) + mouseLocalY;