【问题标题】:Resize div with drag handle when rotated旋转时使用拖动手柄调整 div 的大小
【发布时间】:2018-07-17 07:04:38
【问题描述】:

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

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

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

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

  1. 我不想重新发明轮子,但我想要一个轻量级的代码和简单的 UI。所以我的要求是jQuery,但没有别的。没有 jQuery UI。
  2. 我可以一直发展到实现这一目标,但现在它对我来说太数学化了。我很困惑,这就是为什么我需要你的帮助来检测旋转何时足以使计算反转。

如果有人有想法或更好的例子给我看,我最终会寻求 UX 改进!

这是我的代码和一个 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>

感谢您的帮助!

【问题讨论】:

  • 将鼠标位置转换为矩形的局部坐标系(通过其逆变换),最终得到基本情况,您似乎已经很好地处理了。
  • 感谢您的帮助。但是你如何检测它何时必须是反向的,你必须检测我们是否在某个象限中?
  • 为什么一定要检测?它将永远是相反的。如果它没有被变换(即变换是单位矩阵),那么逆矩阵也将是单位矩阵。
  • 感谢您的支持,但我需要更多帮助,我编辑了我的问题并开始了赏金。你能帮我编辑我的代码或一个例子给我看吗?

标签: javascript jquery math


【解决方案1】:

这是对您的代码的修改,以实现您想要的效果:

$(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 返回负数以及何时返回负数,您可能需要查看文档。

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

【讨论】:

  • 感谢您的回复。这正是我正在寻找的。除了现在我看到闪烁的结果,如果你玩得太多,就会失去手柄,而且不是很用户友好,毕竟我对我的想法有点失望。任何人都可以建议我进行一些 UX 改进吗? :)
  • 如果稍加注意,可能可以避免闪烁。上面的代码只是为了说明到局部坐标的转换。失去手柄是什么意思?
  • 当您旋转然后拖走时,有时手柄会丢失,然后就像您尝试将元素拖出浏览器时一样。所以我用你的代码更新了我的 codepen 并添加了 min-width 和 min-height 以防止如果你拖到 0 就无法使用正方形。codepen.io/anon/pen/BLgVOZ。闪烁仍然存在,我理解你所说的框不是垂直对齐的意思,但是如果这个工具应该应用于所见即所得的图像,我怎么能做到这一点。我无法垂直对齐它们。为什么在我第一次尝试时底部会出现闪烁,而在我的第一次尝试中却没有?
  • 在缩放后调整位置以固定中心可能会有帮助。
  • 固定中心是指绝对/相对定位和拖动时计算上/左位置?
【解决方案2】:

Nico 的答案大部分是正确的,但最终结果计算不正确 -> 这导致 BTW 闪烁。正在发生的事情是大小的增加或减少被 2x 乘法加倍。应将原来的半宽加到新的半宽上,计算出正确的新宽高。

而不是这个:

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

应该是这样的:

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

【讨论】:

  • 谢谢@thomas,我将 Nico 的答案粘贴到 codepen 中并用您的更正进行了编辑,效果很好!太糟糕了,我在 2 年前和 jQuery 一起放弃了这个想法哈哈。 codepen.io/anon/pen/OwREWe
猜你喜欢
  • 2013-12-20
  • 2010-10-14
  • 1970-01-01
  • 2015-04-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多