如何使物体随拖动而旋转?如何使用正弦或余弦获得围绕原点的旋转点?

10

我已经寻找了很长时间,但找不到更好的方法来解决我的问题,
使 div 可以通过每个手柄进行拖动、旋转和调整大小,就像这两个例子一样:1 2,现在它可以被拖动,但旋转有问题..

关于Prasanth K C, Chango, Yi Jiang 等人的回答,这些代码可能不正确,
1. 它应该围绕原点具有一个旋转点。
2. 需要考虑半径。

但我不知道如何在这里使用正弦或余弦来使旋转考虑半径?
任何建议将不胜感激。 http://jsfiddle.net/tBgLh/8/

var dragging = false, target_wp;   
$('.handle').mousedown(function(e) {
    var o_x = e.pageX, o_y = e.pageY; // origin point
    e.preventDefault();
    e.stopPropagation();
    dragging = true;
    target_wp=$(e.target).closest('.draggable_wp');

    $(document).mousemove(function(e) {
        if (dragging) {
            var s_x = e.pageX, s_y = e.pageY; // start rotate point
            if(s_x !== o_x && s_y !== o_y){ //start rotate
                var s_rad = Math.atan2(s_y, s_x);
                var degree = (s_rad * (360 / (2 * Math.PI)));
                target_wp.css('-moz-transform', 'rotate(' + degree + 'deg)');
                target_wp.css('-moz-transform-origin', '50% 50%');
                target_wp.css('-webkit-transform', 'rotate(' + degree + 'deg)');
                target_wp.css('-webkit-transform-origin', '50% 50%');
                target_wp.css('-o-transform', 'rotate(' + degree + 'deg)');
                target_wp.css('-o-transform-origin', '50% 50%');
                target_wp.css('-ms-transform', 'rotate(' + degree + 'deg)');
                target_wp.css('-ms-transform-origin', '50% 50%');
            }
        }
    })
    $(document).mouseup(function() {
        dragging = false
    })
})// end mousemove

HTML

<div class="draggable_wp">
    <div class="el"></div>
    <div class="handle"></div>
</div>

请查看以下链接:https://dev59.com/LVTTa4cB1Zd3GeqPpBIF - Arsen K.
@Webars 感谢您的回复。我之前已经查看了这个问题,实际上在我的问题中,平滑旋转的示例来自 seelts 的答案。我尝试使用那个插件,但它只能与 jQuery UI 和旧版本的 jQuery 兼容...而另一个插件我不知道如何使用手柄拖动。 - user1775888
2个回答

23
你的方法存在两个问题:
  1. The origin shouldn't be where the user clicked (that is the handle), but a fixed point in your div:

    target_wp=$(e.target).closest('.draggable_wp');
    //var o_x = e.pageX, o_y = e.pageY; // origin point
    var o_x = target_wp.offset().left,
        o_y = target_wp.offset().top; // origin point
    

    You will use the clicked point also, but for something else (more later):

    var h_x = e.pageX, h_y = e.pageY; // clicked point
    

    Finally, the origin should be fixed (i.e. should not change between rotations). One way of doing so is preserving it as a data attribute (there are other options though):

    if ( !target_wp.data("origin") )
        target_wp.data("origin", { left:target_wp.offset().left,
                                   top:target_wp.offset().top    });
    var o_x = target_wp.data("origin").left, 
        o_y = target_wp.data("origin").top; // origin point
    

    Update: One good candidate for the origin is the CSS property transform-origin, if present - it should ensure that the mouse follow the handle as closely as possible. This is an experimental feature, however, so the actual resulsts may vary. P.S. I'm not sure setting it to 50% 50% is a good idea, since the transformation itself may vary the element's width and height, top and left.

  2. To find the angle, you should not call atan2 on the mouse point only, since it will only calculate the angle between that point and the top left corner of the page. You want the angle between that point and the origin:

    var s_rad = Math.atan2(s_y - o_y, s_x - o_x); // current to origin
    

    That'll lead you halfway, but it will still behave oddly (it will rotate around the element origin, but not following the handle as you expect). To make it follow the handle, you should adjust the angle in relation to the clicked point - which will serve as a base for the amount to rotate:

    s_rad -= Math.atan2(h_y - o_y, h_x - o_x); // handle to origin
    

    After that you get the rotation working (for one user iteration at least).

您会注意到,手柄并不完全跟随鼠标移动,原因是选择了起点 - 默认为元素的左上角。将其调整为元素内部的某个位置(可以使用 data- 属性),它应该按预期工作。
但是,如果用户与手柄交互多次,仅仅设置旋转角度是不够的,您必须更新上一次迭代中的任何内容。因此,我正在添加一个 last_angle 变量,它将在第一次单击时设置,然后在拖动过程中添加到最终角度中:
// on mousedown
last_angle = target_wp.data("last_angle") || 0;

// on mousemove
s_rad += last_angle; // relative to the last one

// on mouseup    
target_wp.data("last_angle", s_rad);

这是最终的工作示例。(注意:我修复了鼠标处理程序的嵌套,使它们在每次单击后不会再次添加)

$(function () {
    var dragging = false,
        target_wp,
        o_x, o_y, h_x, h_y, last_angle;
    $('.handle').mousedown(function (e) {
        h_x = e.pageX;
        h_y = e.pageY; // clicked point
        e.preventDefault();
        e.stopPropagation();
        dragging = true;
        target_wp = $(e.target).closest('.draggable_wp');
        if (!target_wp.data("origin")) target_wp.data("origin", {
            left: target_wp.offset().left,
            top: target_wp.offset().top
        });
        o_x = target_wp.data("origin").left;
        o_y = target_wp.data("origin").top; // origin point
        
        last_angle = target_wp.data("last_angle") || 0;
    })

    $(document).mousemove(function (e) {
        if (dragging) {
            var s_x = e.pageX,
                s_y = e.pageY; // start rotate point
            if (s_x !== o_x && s_y !== o_y) { //start rotate
                var s_rad = Math.atan2(s_y - o_y, s_x - o_x); // current to origin
                s_rad -= Math.atan2(h_y - o_y, h_x - o_x); // handle to origin
                s_rad += last_angle; // relative to the last one
                var degree = (s_rad * (360 / (2 * Math.PI)));
                target_wp.css('-moz-transform', 'rotate(' + degree + 'deg)');
                target_wp.css('-moz-transform-origin', '50% 50%');
                target_wp.css('-webkit-transform', 'rotate(' + degree + 'deg)');
                target_wp.css('-webkit-transform-origin', '50% 50%');
                target_wp.css('-o-transform', 'rotate(' + degree + 'deg)');
                target_wp.css('-o-transform-origin', '50% 50%');
                target_wp.css('-ms-transform', 'rotate(' + degree + 'deg)');
                target_wp.css('-ms-transform-origin', '50% 50%');
            }
        }
    }) // end mousemove
    
    $(document).mouseup(function (e) {
        dragging = false
        var s_x = e.pageX,
            s_y = e.pageY;
        
        // Saves the last angle for future iterations
        var s_rad = Math.atan2(s_y - o_y, s_x - o_x); // current to origin
        s_rad -= Math.atan2(h_y - o_y, h_x - o_x); // handle to origin
        s_rad += last_angle;
        target_wp.data("last_angle", s_rad);
    })
})
.draggable_wp {
    position: absolute;
    left: 150px;
    top: 150px;
}
.el {
    width: 25px;
    height: 50px;
    background-color: yellow;
}
.handle {
    position: absolute;
    left:0;
    top:-75;
    width: 25px;
    height: 25px;
    background-color: blue;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<div class="draggable_wp">
    <div class="el"></div>
    <div class="handle"></div>
</div>


2
你的代码没问题,唯一的问题是当你尝试将target_wp作为jQuery对象访问时 - 当你将其用作常规元素时。将此放在你的onmousemove开头,它就会工作:target_wp = $(target_wp);演示)我还建议不要同时重复使用dragging进行旋转和平移:两个代码同时运行,会产生错误(检查控制台)。在上面的示例中,我为旋转创建了一个新的变量 - dragging2 - mgibsonbr
1
一直尝试实现这个功能,但一直没有成功。不过,通过你的解释,我现在完全理解了背后的数学原理。你讲得非常清晰易懂。你应该写教程!我希望我能多次点赞.... - David John Welsh
@mgibsonbr 我发现了这个解决方案,并尝试添加“可拖动”功能。http://jsfiddle.net/tBgLh/234/ 这样会导致旋转变得不稳定。你能否帮忙看一下这个示例? - aVC
2
每次移动元素时,您都必须更新其存储的原点,否则旋转角度仍将以先前的原点作为参考。这里是一个更新的示例。更改的部分包括:1)最后一部分,您定义了draggable;2)mouseup - 在拖动期间错误触发,但只应在旋转时触发。 - mgibsonbr
@aVC这是因为没有要更新的“origin”。在这里,已经修复:http://jsfiddle.net/tBgLh/238/。 - mgibsonbr
显示剩余9条评论

0

他们在变换属性中使用了一个矩阵函数。你可以通过将矩阵(元素坐标)与旋转矩阵相乘来旋转元素。

transform:  matrix(a, c, b, d, tx, ty)

更多信息和示例请参见:CSS3矩阵()变换,适用于数学挑战者


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