【问题标题】:Inner div inside an outer rotated div does not follow mouse in case dragging with jQuery UI在使用 jQuery UI 拖动的情况下,外部旋转 div 内的内部 div 不会跟随鼠标
【发布时间】:2012-06-04 08:49:39
【问题描述】:

我在外部 div 中有一个内部 div。内部 div 是可拖动的,外部 div 旋转 40 度。这是一个测试用例。在实际情况下,它可以是任何角度。还有另一个名为 point 的 div,其位置如图所示。 (我来自 Flash 背景。在 Flash 中,如果我要拖动内部 div,即使它包含在外部旋转 div 中,它也会跟随鼠标。)但是在 HTML 中,内部 div 不会跟随鼠标,因为它可以看到从小提琴。我希望 div 'point' 完全跟随鼠标。这可能吗。我尝试使用三角函数来处理它,但无法让它工作。

http://jsfiddle.net/bobbyfrancisjoseph/kB4ra/8/

【问题讨论】:

  • 嘿,对不起,这听起来很厚,但你需要的只是每次移动带有内部 id 的 div 时移动 id 点的 div 吗?让我知道,只需稍作改动,我就为您准备了一个演示! B-)
  • 请展示演示。我似乎做错了。
  • 这绝对是插件的问题。你在code.google.com/p/jqueryrotate/issues/list 上报告过这个吗?
  • 你接受没有 jQuery 吗?

标签: javascript jquery html jquery-ui graphics


【解决方案1】:

这是我解决这个问题的方法。

http://jsfiddle.net/2X9sT/21/

我把点放在旋转的 div 之外。这样我就可以保证拖动事件会产生正常的行为(不会向奇怪的方向跳跃)。我使用可拖动处理程序将点附加到鼠标光标。

在拖动事件中,我转换拖动偏移以反映新值。这是通过以与旋转角度相反的方向围绕外部 div 中心旋转偏移量来完成的。

我测试了它,它似乎可以在 IE9、Firefox 和 Chrome 中运行。

您可以尝试不同的角度值,它应该可以正常工作。

我还修改了 HTML,以便可以将相同的逻辑应用于页面中的多个 div。

编辑:

我更新了脚本,以考虑 cmets 中建议的收容行为以及级联旋转。

我也正在过期将outer div 拖动到另一个 div 中。现在它几乎可以工作了。我只需要能够更新拖动的 div 的中心来修复拖动行为。

尝试拖动红色的 div。

http://jsfiddle.net/mohdali/kETcE/39/

【讨论】:

  • 如何通过overflowcontainment?包含#inner div,使其保留在#outer中?无法承受的方法是+1,简直太棒了!
  • 由于我们只是更改 CSS 的 top 和 left 属性,因此可以通过在值上应用上下带来轻松应用包含。检查这个小提琴jsfiddle.net/4XJgP
  • 主要缺点 - 正如我在自己的回答中指出的那样 - 是 transformOffset 是一个非常具体的函数,仅适用于这个非常具体的转换(旋转),如果任何其他转换是添加到任何父元素(如果您已经在使用旋转进行操作,这很有可能)。
  • @DavidMulder 是的。我对嵌套旋转的效果做了一些测试。我想出了一个仍然需要修复的初始解决方案。现在我得到了正确的方向但是有一个阻力偏移,因为我没有正确检测到第二个元素的旋转中心。我尝试了您的方法来获取矩阵,但它没有提供内部元素的全部转换。 matrix 中也没有考虑 css topleft。请查看jsfiddle.net/mohdali/kETcE/5
  • @arttronics 感谢您的建议。我编辑了我的答案,为包含和级联旋转添加了一个组合脚本。
【解决方案2】:

可能这是您的 jquery 库的问题,或者您可以通过分配内部 div 和外部 div 的 z-order 值来检查这一点,确保您为内部 div 提供更高的数字。

【讨论】:

    【解决方案3】:

    我现在在工作,所以我不能为你做这项工作,但我可以解释最简洁解决你问题的方法背后的数学原理(可能不是最简单的解决方案,但不像其他一些技巧,一旦实施,它会很多更加灵活)。

    首先你必须意识到你正在使用的旋转插件正在对你的元素应用一个变换(transform: rotate(30deg)),然后你的浏览器将它变成一个矩阵(matrix(0.8660254037844387, 0.49999999999999994, -0.49999999999999994, 0.8660254037844387, 0, 0))。 其次,有必要了解,通过旋转一个元素,子元素的轴是绝对旋转的(在寻找了很长时间之后,没有任何真正的技巧可以绕过这个,这有道理),因此唯一的方法是将孩子从父母身边带走,正如其他一些答案所暗示的那样,但我假设这不是您的应用程序中的一个选项。

    现在,我们需要做的是取消父矩阵的原始矩阵,这是一个两步过程。首先,我们需要使用以下代码找到矩阵:

    var styles = window.getComputedStyle(el, null);
    
    var matrix = styles.getPropertyValue("-webkit-transform") ||
                 styles.getPropertyValue("-moz-transform") ||
                 styles.getPropertyValue("-ms-transform") ||
                 styles.getPropertyValue("-o-transform") ||
                 styles.getPropertyValue("transform");
    

    接下来,矩阵将是一个字符串,如上所示,您需要将其解析为可以使用的数组(有 jquery 插件可以做到这一点)。完成后,您将需要取矩阵的逆矩阵(在您的示例中归结为 rotate(-30deg)),这可以使用例如 this library(或您的数学书:P)来完成。

    最后你需要做逆矩阵时间(使用我之前提到的矩阵库)一个平移矩阵(使用this tool 来弄清楚它们的外观(平移是沿 x 和 y 轴的移动,有点像left 和 top 在相对定位的元素上,但硬件加速和矩阵转换 css 属性的一部分))这将为您提供一个新矩阵,您可以将其应用于您的子元素,为您提供与父元素在同一轴上的平移.

    现在,您可以通过使用 lefttop 和手动三角函数1 来大大简化此操作(完全绕过对逆矩阵甚至矩阵的全部需求),但这有一个明显的缺点,它适用于正常旋转,并且需要根据使用的每个特定情况进行更改。

    哦,如果您现在认为 flash 更容易,相信我,在 HTML/CSS 中旋转轴的方式很有意义,if you want flash like behavior use this library


    1 例如,这就是 Mohamed Ali 在他的回答中所做的(his jsFiddle 中的 transformOffset 函数)。


    免责声明,我做这些东西已经有一段时间了,我对矩阵的理解从来没有非常好,所以如果你发现任何错误,请指出/修复它们。

    【讨论】:

    • 我同意这适用于任何类型的变换(不仅仅是旋转)。但是,您的方法将仅返回局部变换矩阵。为了处理父元素的转换,我认为我们需要遍历 DOM 树并修改所有的翻译。
    • @MohamedAli:是的,但是用一个简单的while循环遍历父母并“添加”矩阵应该相当简单(现在我不确定它是点积还是叉积矩阵......或不同的东西(我需要尝试知道)),不像添加各种旋转或旋转与其他东西,但是,你是对的,遍历dom是必要的,但这仍然是相当的通用(因为您不需要单独定义父状态)。
    • 您也可以像我在jsfiddle.net/kB4ra/118 中所做的那样,简单地遍历父母并在每一步转换 x 和 y 坐标 - 这将是实施此方法的合适起点。
    • 请注意,不同的浏览器以不同的顺序返回矩阵的各个部分,因此您必须根据它来自哪个 CSS 属性来区别对待。
    • @DavidMulder 我更新了我的答案,包括遍历 DOM。它现在正在使用级联旋转 div。您能否检查一下并将您的解决方案应用于一般转换?
    【解决方案4】:

    仅对于 Webkit,webkitConvertPointFromPageToNode 函数处理缺失的行为:

       var point = webkitConvertPointFromPageToNode(
           document.getElementById("outer"),
           new WebKitPoint(event.pageX, event.pageY)
       );
    

    jsFiddle:http://jsfiddle.net/kB4ra/108/

    要覆盖其他浏览器,您可以使用此 StackOverflow 答案中描述的方法:https://stackoverflow.com/a/6994825/638544

    function coords(event, element) {
      function a(width) {
        var l = 0, r = 200;
        while (r - l > 0.0001) {
          var mid = (r + l) / 2;
          var a = document.createElement('div');
          a.style.cssText = 'position: absolute;left:0;top:0;background: red;z-index: 1000;';
          a.style[width ? 'width' : 'height'] = mid.toFixed(3) + '%';
          a.style[width ? 'height' : 'width'] = '100%';
          element.appendChild(a);
          var x = document.elementFromPoint(event.clientX, event.clientY);
          element.removeChild(a);
          if (x === a) {
            r = mid;
          } else {
            if (r === 200) {
              return null;
            }
            l = mid;
          }
        }
        return mid;
      }
        var l = a(true),
            r = a(false);
        return (l && r) ? {
            x: l,
            y: r
        } : null;
    }
    

    这有一个缺点是当鼠标在目标元素之外时不起作用,但是应该可以将它覆盖的区域扩展任意数量(尽管很难保证它覆盖整个窗口不管多大)。

    jsFiddle:http://jsfiddle.net/kB4ra/122/

    这可以通过添加 mousemove 事件扩展为应用于#point:

    $('#outer').mousemove(function(event){
       var point = convertCoordinates(event, $("#outer"));
    
        $("#point").css({left: point.x+1, top: point.y+1});          
    });
    

    请注意,我将#point的x和y坐标调整了1px,以防止它直接位于鼠标下方;如果我不这样做,那么它会阻止拖动#inner。另一种解决方法是将处理程序添加到 #point 以检测鼠标事件并将它们传递给直接位于 #point 下方的任何元素(以及 stopPropagation,以便它们不会在较大的页面元素上运行两次)。

    jsFiddle:http://jsfiddle.net/kB4ra/123/

    【讨论】:

    • 这是一个更接近于正确处理 Firefox 的版本:jsfiddle.net/kB4ra/118(仍然需要代码来解释转换,显然很难获得相关信息)。
    • 找到了一种真正适用于 Firefox 的方法。我已将其编辑到我的答案中。
    • 在我的回答中添加了#point 的处理。此外,我注意到用于旋转的 jQuery 插件的 getRotateAngle 函数 - 它可以解释 jsfiddle.net/kB4ra/118 中的旋转(即使在#outer 之外使用鼠标也可以使用)。跨度>
    【解决方案5】:

    这基本上可以满足您的需求,尽管它有问题。绑定拖动事件处理程序,截取ui对象并修改为使用父元素的偏移X和Y。所有 X、Y、顶部、左侧等都在这些对象中。今天我有更多时间的时候,我会试着给你一个更好的例子。祝你好运!

    http://jsfiddle.net/kB4ra/107/

    【讨论】:

    • 非常感谢..我几乎放弃了..我将开始赏金..250分..干杯...
    • @thomallen 我试过小提琴,但灰色的小矩形在 FF 12.0 中根本无法拖动,并且在 webkit 浏览器中闪烁很多。
    • @Tom,闪烁的问题是由于 event.offsetX & Y 不时返回NaN
    • 在 Chrome 中,闪烁是由 event.offsetX & Y 返回 1 而不是 NaN 引起的。这又是由鼠标在#inner 元素上移动引起的,导致偏移坐标相对于它。 Firefox 损坏是由于 event.offsetX & Y 总是返回 undefined。
    • @Brilliand,好吧,chrome 在我的实验中返回了NaN
    【解决方案6】:

    在我看来,如果您不旋转 div,则 div 会完全跟随鼠标。 这可能是插件的问题..也许您可以正确模拟可拖动功能?

    【讨论】:

    • 我来自 Flash 背景。在 Flash 中,如果我们要旋转外部影片剪辑并要求内部影片剪辑跟随鼠标,它会很容易工作。发生了本地到全局/全局到本地的转换,它由闪存在内部处理。但是在这里,转换似乎没有正确发生。在我有限的智力中,这似乎是个问题:-)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-23
    相关资源
    最近更新 更多