惯性/气压在移动/拖动时摆动/旋转div

时间:2013-05-23 20:51:02

标签: javascript physics drag mousemove inertia

我花了最后6个小时试图找到问题的答案,但没有任何运气。

我的网站上有一些可拖动的div。当我拖动它们时,我想要一个逼真的效果,就像有一个气压而且它们被推了一下,所以当它们被拖动时它们会摆动一点。

效果如何起作用的示例图像可以在这里看到:The inertia acceleration effect(不知道效果是什么)

我用许多不同的组合搜索这些单词“Javascript,加速度,惯性,摆动,物理,旋转,移动,气压,摩擦,悬挂”,但没有找到任何帮助我的东西。

虽然,我在这个JSFiddle上发现了一种类似的效果[1](链接如下)。我还遇到了Google Gravity [2](下面的链接),当你拖动一个元素并将鼠标移到一边时,它具有我想要实现的效果。

[1] JSFiddle我发现:www.jsfiddle.net/gVCWE/150 /

[2] Google Gravity:www.mrdoob.com/projects/chromeexperiments/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();

JSFiddle Demo

  • 我的操作:我计算在最后15次鼠标移动过程中鼠标移动了多少,然后将旋转度设置为该值。因此,当鼠标快速移动时,15个鼠标移动之间的跨度将变大,因此框旋转将很大。当鼠标移动缓慢时,跨度会变小,框旋转也不会像以前那么大。

但结果看起来并不现实,尤其是当您快速移动鼠标时。我认为需要函数Math.sin来使旋转中更加逼真的“缓动”,因为div会移动。 我不知道如何计算这个物理,所以如果有人有想法,来源,例子,公式,什么或者只知道效果的名称那就太棒了。

//祝你好运

2 个答案:

答案 0 :(得分:1)

您正在寻找2D Rigid Body Dynamics

考虑使用物理引擎以更少的努力和更好的结果来实现您想要的效果。

请注意,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 Rigid Body Dynamics - 麻省理工学院开放式课程

答案 1 :(得分:1)

在没有任何真实模拟的情况下,我做了一些可自定义的事情,以最后2次移动的速度来设置div的角度。

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

使用它,您可以更改角度变量以增大或减小角度。

我还使用另一个topic

添加了一些惯性

您可以在此处查看结果http://jsfiddle.net/Spope/tzqwx/