Ответ 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%
является хорошей идеей, поскольку сама трансформация может варьировать ширину и высоту элемента, сверху и слева. -
Чтобы найти угол, вы не должны вызывать
atan2
только в точке мыши, так как он будет вычислять только угол между этой точкой и верхним левым углом страницы. Вам нужен угол между этой точкой и началом: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
После этого вы получите поворот (для одной итерации пользователя как минимум).
Вы заметите, что дескриптор не точно соответствует мышью, и причина в выборе точки начала - по умолчанию для верхнего/левого угла элемента. Отрегулируйте его где-нибудь внутри элемента (возможно, используя атрибут 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);
Здесь приведен рабочий пример . (Примечание: я исправил вложенность ваших обработчиков мыши, поэтому они не добавляются снова после каждого нажатия)
$(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>