Инерция / Давление воздуха для качания / поворота делителя, когда он перемещается / перетаскивается
Я провел последние 6 часов, пытаясь найти ответ на свой вопрос, но безуспешно.
У меня есть некоторые div, которые можно перетаскивать на моем сайте. В то время как я перетаскиваю их, я хочу реалистичный эффект, как будто есть воздушное давление, и они немного сдвинуты, поэтому они будут немного качаться, когда их перетаскивают.
Пример изображения того, как должен работать эффект, можно увидеть здесь: Эффект ускорения инерции (не знаю, как называется эффект)
Я погуглил эти слова в самых разных сочетаниях: "Javascript, ускорение, инерция, свинг, физика, вращение, движение, давление воздуха, трение, свисание", не найдя ничего, чтобы помочь мне.
Тем не менее, я обнаружил нечто похожее на этот JSFiddle[ 1 ] (ссылка ниже). Я также натолкнулся на Google Gravity[ 2 ] (ссылка ниже), который дает эффект, которого я хочу достичь, когда вы перетаскиваете элемент и перемещаете мышь в сторону.
[1] JSFiddle я нашел: www.jsfiddle.net/gVCWE/150/
[2] Google Gravity: www.mrdoob.com/projects/chromeexperiment/google-gravity/
Я попытался создать эффект сам здесь:
var fn = {
activeElm : null,
pos : {},
transPos : {},
startPos : null,
moveSteps : new Array(15),
init : function(){
var self = this;
document.addEventListener('mousedown', function(e){self.handleStart.call(self, e);});
document.addEventListener('mousemove', function(e){self.handleMove.call(self, e);});
document.addEventListener('mouseup', function(e){self.handleStop.call(self, e);});
},
handleStart : function(e){
if(fn.hasClass('dragme', e.target)){
fn.activeElm = e.target;
e.preventDefault();
fn.startPos = {x : e.pageX, y : e.pageY};
var lastPos = {x : fn.activeElm.getAttribute('data-posX'), y : fn.activeElm.getAttribute('data-posY')};
if(lastPos['x'] && lastPos['y']){
fn.transPos = {x : parseFloat(lastPos['x']), y : parseFloat(lastPos['y'])};
}else{
fn.transPos = {x : 0, y : 0};
}
fn.pos = {x : fn.transPos['x'], y : fn.transPos['y']};
fn.loop();
}
},
handleMove : function(e){
if(!fn.activeElm)
return;
fn.pos = {x : fn.transPos['x'] + e.pageX - fn.startPos['x'], y : fn.transPos['y'] + e.pageY - fn.startPos['y']};
},
handleStop : function(e){
if(!fn.activeElm)
return;
fn.activeElm.setAttribute('data-posX', fn.pos['x']);
fn.activeElm.setAttribute('data-posY', fn.pos['y']);
fn.activeElm = null;
},
addStep : function(move){
var arr = fn.moveSteps;
arr = arr.slice(1, arr.length);
arr.push(move);
fn.moveSteps = arr;
},
loop : function(){
if(!fn.activeElm)
return false;
fn.requestAnimFrame(fn.loop);
fn.addStep(fn.pos['x']);
fn.animate();
},
animate : function(){
var arr = fn.moveSteps;
var rotaSpeed = arr[arr.length-1] - arr[0];
// The Math.min- and max are there, so we can be sure the angle won't rotate more than 90° and -90°
var rotation = 'rotate(' + Math.max(Math.min(90, rotaSpeed), -90) + 'deg)';
if(!fn.activeElm)
return;
var obj = fn.activeElm.parentNode.style;
obj.webkitTransform = obj.MozTransform = obj.msTransform = obj.OTransform = obj.transform = 'translate(' + fn.pos['x'] + 'px, ' + fn.pos['y'] + 'px) ' + rotation;
},
requestAnimFrame : function(callback){
return window.requestAnimationFrame && window.requestAnimationFrame(callback) ||
window.webkitRequestAnimationFrame && window.webkitRequestAnimationFrame(callback) ||
window.mozRequestAnimationFrame && window.mozRequestAnimationFrame(callback) ||
window.oRequestAnimationFrame && window.mozRequestAnimationFrame(callback) ||
window.msRequestAnimationFrame && window.msRequestAnimationFrame(callback) ||
window.setTimeout(callback, 1000 / 60);
},
hasClass : function(classname, obj){
return new RegExp(' ' + classname + ' ').test(' ' + obj.className + ' ');
}
}
fn.init();
- Что я делаю: я вычисляю, сколько мышь переместила за последние 15 движений мыши, а затем устанавливаю степень поворота на это значение. Поэтому, когда мышь движется быстро, промежуток между 15 движениями мыши будет большим, и, следовательно, вращение бокса будет большим. Когда мышь движется медленно, промежуток будет меньше, а вращение окна будет не таким большим, как раньше.
Но результат не выглядит реалистичным, особенно если вы быстро двигаете мышь. Я думаю, что функция Math.sin необходима для более реалистичного "облегчения" вращения при перемещении div. Я понятия не имею, как я могу рассчитать эту физику, поэтому, если у кого-то есть идея, источники, примеры, формулы, что бы то ни было или просто знать название эффекта, это было бы здорово.
//С наилучшими пожеланиями
2 ответа
Без какой-либо реальной симуляции, я сделал что-то настраиваемое, чтобы взять скорость двух последних ходов, чтобы установить угол вашего деления.
this.drag = function (elem, e) {
//
//ROTATION
//
//If there's an end animation I kill it.
endAnimation.stop();
//Saving the mouse position
history.push(e);
//When we have positions to compare
if(history.length > 1){
if (history.length > 2) {
history.shift();
}
var dX = history[0].clientX - history[1].clientX; // Delta
var tAngle = Math.ceil(angle * -dX); // Angle to reach
var rotation = 'rotate('+ tAngle +'deg)';
$(elem).css({WebkitTransform: rotation});
$(elem).css({'-moz-transform': rotation});
$(elem).css({transform: rotation});
}
}
При этом вы можете изменить переменную угла, чтобы увеличить или уменьшить угол.
Я также добавил некоторую инерцию при падении, используя другую тему
Вы можете увидеть результат здесь http://jsfiddle.net/Spope/tzqwx/
Вы ищете 2D динамику твердого тела.
Попробуйте использовать физический движок для достижения желаемого эффекта с гораздо меньшими усилиями и отличными результатами.
Обратите внимание, что Google Gravity использует Box2Djs, физический движок, порт Box2DFlash для JavaScript. Если вы посмотрите на код страницы, вы найдете, как правильно настроить Box2djs.
С Box2d вы сможете настроить свойства мира и объектов (например, гравитацию, трение) и другие физические свойства.
Ниже приведен пример кода, который присутствует в Google Gravity для работы с Box2d:
// init box2d
worldAABB = new b2AABB();
worldAABB.minVertex.Set(-200, -200);
worldAABB.maxVertex.Set(window.innerWidth + 200, window.innerHeight + 200);
world = new b2World(worldAABB, new b2Vec2(0, 0), true);
// Get box2d elements
elements = getElementsByClass("box2d");
for (var i = 0; i < elements.length; i++) {
properties[i] = getElementProperties(elements[i]);
}
for (var i = 0; i < elements.length; i++) {
var element = elements[i];
element.style.position = 'absolute';
element.style.left = properties[i][0] + 'px';
element.style.top = properties[i][1] + 'px';
element.style.width = properties[i][2] + 'px';
element.addEventListener('mousedown', onElementMouseDown, false);
element.addEventListener('mouseup', onElementMouseUp, false);
element.addEventListener('click', onElementClick, false);
bodies[i] = createBox(world, properties[i][0] + (properties[i][2] >> 1), properties[i][1] + (properties[i][3] >> 1), properties[i][2] / 2, properties[i][3] / 2, false);
// Clean position dependencies
while (element.offsetParent) {
element = element.offsetParent;
element.style.position = 'static';
}
}
Этот кусок кода просто чтобы помочь вам найти код, который имеет значение. Внимательно изучите код Google Gravity, чтобы узнать, как использовать Box2djs для создания желаемого эффекта.
Если вы решите использовать физический движок, вы можете предпочесть Box2dWeb вместо Box2Djs, потому что он обновляется и хранится в одном файле js.
Реализовать realist effect
по своему усмотрению это тяжело и требует большего, чем простое использование синуса или косинуса. Может быть, есть какой-то визуально похожий эффект, но, опять же, реалистический эффект, который труднее достичь без применения хорошего физического моделирования.
2D Динамика твердого тела - MIT OpenCourseWare