外部旋转div内部的div在使用jQuery UI拖动时未跟随鼠标移动

18
我有一个内部div和外部div。内部div是可拖动的,而外部div被旋转40度。这是一个测试用例。在实际情况中可能是任何角度。还有另一个名为point的div,它的位置如图所示。(我来自Flash背景。在Flash中,如果我要拖动内部div,即使它包含在旋转的外部div中,它也会跟随鼠标。)但是在HTML中,如从fiddle中所见,内部div不会跟随鼠标移动。我希望div 'point'能够精确地跟随鼠标。这可能吗?我尝试使用三角函数来解决它,但无法使其正常工作。 http://jsfiddle.net/bobbyfrancisjoseph/kB4ra/8/

嘿,抱歉如果听起来有点笨拙,但你所需要做的就是每当带有内部id的div移动时,移动id为point的div。让我知道,我已经为你准备好了一个演示!B-) - Tats_innit
请展示演示。我似乎做不对。 - Bobby Francis Joseph
1
这绝对是插件的问题。你是否已经在http://code.google.com/p/jqueryrotate/issues/list上报告了这个问题? - naveen
你能接受没有使用jQuery吗? - user1365010
6个回答

9

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

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

我将点放在旋转的div外面。这样,我可以确保拖动事件会产生正常行为(不会在奇怪的方向跳跃)。我使用可拖动的处理程序将点附加到鼠标光标上。
在拖动事件中,我通过以相反方向围绕外部div中心旋转偏移量来转换拖动偏移量以反映新值。
我测试了它,它似乎在IE9、Firefox和Chrome中都能工作。
您可以尝试不同的角度值,它应该可以正常工作。
我还修改了HTML,使得可以将相同的逻辑应用于页面上的多个div。
编辑:
我更新了脚本以考虑包含行为以及评论中建议的级联旋转。
我还在尝试使外部div在另一个div内可拖动。现在它几乎可以工作了。我只需要能够更新拖动的div的中心以修复拖动行为。
试着拖动红色的div。

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


1
由于我们只是更改CSS的top和left属性,因此通过在值上应用上下边界来应用约束将变得很容易。请查看此fiddle http://jsfiddle.net/4XJgP/。 - Mohamed Ali
1
主要的缺点 - 正如我在自己的答案中指出的那样 - 是 transformOffset 是一个非常特定的函数,仅适用于这个非常特定的转换(旋转),如果任何其他转换被添加到任何父元素中,它将会出现问题(如果您已经在进行旋转操作,则很可能会发生这种情况)。 - David Mulder
@MohamedAli:没错,但将所有父元素的矩阵“相加”要比将具有两个不同旋转中心的两个元素的旋转相加要容易得多。或者例如具有旋转和倾斜的父元素。 - David Mulder
1
@arttronics 谢谢您的建议。我编辑了我的答案,添加了一个结合了容器和级联旋转的脚本。 - Mohamed Ali
我按照您的指示进行操作后,拖动后得到了正确的位置。但是那些可拖动的div没有被放下,如果我注释掉helper代码,它就会被放下。请查看http://jsbin.com/xozase/1/edit?js,output。只有一次我能够拖动。请再看一遍。 - Sarath Babu Nuthimadugu
显示剩余4条评论

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)),可以使用此库(或您的数学书:P)进行操作。
最后,您需要对逆矩阵执行矩阵乘法(使用我之前提到的矩阵库),再加上一个平移矩阵(使用此工具查看其外观(平移是沿x和y轴移动,有点像相对定位元素的左侧和顶部,但硬件加速并且是矩阵变换CSS属性的一部分)),这将给您一个新矩阵,您可以将其应用于子元素,使其在与父元素相同的轴上移动。
现在,您可以通过使用lefttop和手动三角函数1来极大地简化此过程,仅适用于旋转(绕过了逆矩阵甚至矩阵的整个需求),但其明显的缺点是它仅适用于常规旋转并且需要根据使用情况进行更改。
哦,如果您现在认为flash更容易,相信我,HTML/CSS中轴线的旋转方式非常有道理,如果您想要类似Flash的行为,请使用此库

1这就是Mohamed Ali在他的答案中所做的事情(在他的jsFiddle中的transformOffset函数)。


免责声明,我已经有一段时间没有做这些工作了,而且我的矩阵理解从来不是非常好的,因此如果您发现任何错误,请务必指出/纠正。


我同意这种方法可以处理所有类型的变换(不仅包括旋转)。但是你的方法只会返回本地变换矩阵。为了处理父元素上的变换,我认为我们需要遍历DOM树并反转所有的平移。 - Mohamed Ali
@MohamedAli:没错,但是用一个简单的while循环遍历父级并“添加”矩阵应该相当简单(现在我不确定是矩阵的点积还是叉积...或者是其他什么(我需要尝试才能知道)),不像添加各种旋转或旋转与其他东西的组合那样复杂,但是是的,你说得对,遍历DOM是必要的,但这仍然是相当通用的(因为您不需要单独定义父状态)。 - David Mulder
您还可以简单地遍历父级并在每一步转换x和y坐标,就像我在http://jsfiddle.net/kB4ra/118/中所做的那样——这将是实施此方法的一个合适起点。 - Brilliand
请注意,不同的浏览器以不同的顺序返回矩阵的部分,因此您必须根据它来自哪个CSS属性而有所不同地处理它。 - Brilliand
2
@DavidMulder 我更新了我的答案,包括遍历DOM。现在它可以与级联旋转的div一起工作。你能否请检查并应用你的解决方案到一般的转换中? - Mohamed Ali

2

仅适用于Webkit的webkitConvertPointFromPageToNode函数处理缺失的行为:

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

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

为了覆盖其他浏览器,您可以使用在这个StackOverflow答案中描述的方法:https://dev59.com/bmw15IYBdhLWcg3wLI3I#6994825

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坐标调整了1像素,以防止它直接位于鼠标下方;如果我没有这样做,那么它会阻止拖动#inner。另一种解决方法是向#point添加处理程序来检测鼠标事件并将它们传递给直接位于#point下面的任何元素(并停止传播,以便它们不会在较大的页面元素上运行两次)。
jsFiddle:http://jsfiddle.net/kB4ra/123/

这里有一个版本,更接近正确处理Firefox:http://jsfiddle.net/kB4ra/118/(仍需要代码来处理变换,这似乎很难获取信息)。 - Brilliand
找到了一种实际在Firefox中可行的方法。我已将其编辑到我的答案中。 - Brilliand
我在我的答案中添加了对#point的处理。此外,我注意到jQuery插件中使用的getRotateAngle函数用于旋转 - 这将使得在http://jsfiddle.net/kB4ra/118中考虑旋转变得可行(即使鼠标在#outer之外也可以工作)。 - Brilliand

0

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


我来自Flash背景。在Flash中,如果我们旋转外部MovieClip并要求内部MovieClip跟随鼠标移动,这将很容易实现。Flash内部会处理本地到全局/全局到本地的转换。但是在这里,该转换似乎没有正确进行。在我的有限智力中,这似乎是问题所在 :-) - Bobby Francis Joseph

0

这基本上可以做到你需要的,但它有些错误。绑定拖动事件处理程序,拦截ui对象并修改它以使用父元素的偏移X和Y。所有的X、Y、top、left等都在这些对象中。我会尽量在今天稍晚一点的时候给你一个更好的例子。祝你好运!

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


非常感谢您,我差点放弃了它,我将开始一个赏金任务,250分,干杯! - Bobby Francis Joseph
1
@thomallen 我试过了这个fiddle,但在FF 12.0中,那个小灰色矩形根本无法拖动,在Webkit浏览器中闪烁得很厉害。 - toniedzwiedz
@Tom,闪烁问题是由于事件偏移量 offsetX 和 Y 时不时返回 NaN 导致的。 - Starx
在Chrome浏览器中,闪烁是由于event.offsetX和Y返回1而不是NaN引起的。这又是由鼠标移动到#inner元素上导致偏移坐标相对于它的结果。Firefox的错误是由于event.offsetX和Y始终返回未定义。 - Brilliand
@Brilliand,嗯,我的实验中Chrome返回了“NaN”。 - Starx
Bobby,如果我有时间,我今晚会解决这个问题。抱歉耽搁了。 - thomallen

-1

可能这是您的jQuery库的问题,或者您可以通过分配内部div和外部div的z-order值来检查此问题,请确保您为内部div赋予更高的数字。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接