【问题标题】:How to make object rotate with drag, how to get a rotate point around the origin use sin or cos?如何通过拖动使对象旋转,如何使用 sin 或 cos 获得围绕原点的旋转点?
【发布时间】:2013-01-30 08:39:37
【问题描述】:

我已经搜索了很长时间,但找不到更好的方法来解决我的问题,
使 div 可拖动,按每个句柄旋转和调整大小,例如这 2 个示例 1 2,现在它可以拖动了,但是可以旋转..

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

但是我不知道如何在这里使用 sin 或 cos 来使旋转考虑半径?
任何建议将不胜感激。 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>

【问题讨论】:

  • @Webars 感谢您的回复。我以前检查过这个问题,实际上在我的,平滑旋转示例来自seelts answer 我尝试使用该插件,但它只能与 jquery ui 和旧版本 jquery 一起使用......以及我不使用的其他插件不知道如何用手柄拖动

标签: javascript jquery css math


【解决方案1】:

你的方法有两个问题:

  1. 原点不应该是用户单击的位置(即句柄),而是 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
    

    也使用点击点,但用于其他用途(稍后会更多):

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

    最后,原点应该是固定的(即不应该在旋转之间改变)。这样做的一种方法是将其保留为 data 属性(不过还有其他选项):

    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
    

    更新: 来源的一个很好的候选者是 CSS 属性 transform-origin,如果存在 - 它应该确保鼠标尽可能靠近手柄。但是,这是一项实验性功能,因此实际结果可能会有所不同。附言我不确定将其设置为 50% 50% 是否是个好主意,因为转换本身可能会改变元素的宽度和高度、顶部和左侧。

  2. 要找到角度,您不应只在鼠标点上调用atan2,因为它只会计算该点与页面左上角之间的角度。你想要那个点和原点之间的角度:

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

    这会引导你halfway,但它的行为仍然很奇怪(它会围绕元素原点旋转,但不会像你期望的那样跟随句柄)。要使其跟随手柄,您应该调整相对于点击点的角度 - 这将作为旋转量的基础:

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

    之后,您将获得轮换 working(至少对于一个用户迭代)。

您会注意到手柄并没有精确地跟随鼠标,原因是原点的选择 - 默认为元素的上/左角。将其调整到元素内部的某个位置(可能使用data- 属性),它应该可以按预期工作。

但是,如果用户多次与手柄交互,仅设置旋转角度是不够的,您必须更新上次迭代期间的任何内容。所以我添加了一个last_angle var,它将在第一次单击时设置,然后在拖动过程中添加到最终角度:

// 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);

这是最后的working example。 (注意:我修复了鼠标处理程序的嵌套,因此每次单击后不会再次添加它们)

$(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>

【讨论】:

  • 如果我结合可拖动代码,旋转然后拖动移动wp位置,然后旋转它会因为原点改变而跳转到另一个角度?需要更新新点吗? jsfiddle.net/tBgLh/12
  • 是的。旋转的问题是它改变了左上角的位置,所以你不能依靠它来确定正确的原点。这就是我第一次旋转元素时使用data 保存它的原因。如果您拖动(或调整大小)元素,则必须更新该原点以保持正确的结果。 (顺便说一句,我使用左上角作为原点,但为了获得更准确的值,您应该使用与定义为 x-transform-origin 的 CSS 相同的值)
  • 您的代码很好,唯一的问题是当您尝试将 target_wp 作为 jQuery 对象访问时 - 当您将其用作常规元素时。将它放在onmousemove 的开头,它将起作用:target_wp = $(target_wp); (demo) 我还建议不要将dragging 用于旋转和平移:两个代码同时运行,产生错误(检查控制台)。在上面的示例中,我创建了一个新的用于旋转 - dragging2
  • 已经尝试实现了一段时间,但没有取得多大成功。不过,有了你的解释,我现在完全理解了它背后的数学原理。你已经说得很清楚了,很容易理解。你应该写教程!我希望我可以多次投票......
  • 每次移动元素时,都必须更新其存储的原点,否则旋转角度仍会以之前的原点为参考。这是updated example。更改的部分是:1)最后一位,您在其中定义了draggable; 2) mouseup - 在拖动过程中被错误触发,但应该只在旋转时触发。
【解决方案2】:

他们在变换属性中使用矩阵函数。您可以通过旋转矩阵通过多重播放矩阵(元素坐标)来旋转元素。

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

更多信息和示例在这里:The CSS3 matrix() Transform for the Mathematically Challenged

【讨论】: