javascript smooth animation from X,Y to X1,Y1

2019-03-28 12:31发布

问题:

I'd like to sloothly move an image (or an element) from its actual X, Y location to X1, Y1.

When the distance between X and X1 is equal to that between Y and Y1 its easy. But what if the X difference is say 100px and Y diff is 273px?

Being new to Javascript, I don't want to re-invent the wheel! Besides, since I'm learning, I do NOT want to use jQuery or the likes. I want pure javascript.

Please supply with simple script :-)

回答1:

One solution:

function translate( elem, x, y ) {
    var left = parseInt( css( elem, 'left' ), 10 ),
        top = parseInt( css( elem, 'top' ), 10 ),
        dx = left - x,
        dy = top - y,
        i = 1,
        count = 20,
        delay = 20;

    function loop() {
        if ( i >= count ) { return; }
        i += 1;
        elem.style.left = ( left - ( dx * i / count ) ).toFixed( 0 ) + 'px';
        elem.style.top = ( top - ( dy * i / count ) ).toFixed( 0 ) + 'px';
        setTimeout( loop, delay );
    }

    loop();
}

function css( element, property ) {
    return window.getComputedStyle( element, null ).getPropertyValue( property );
}

Live demo: http://jsfiddle.net/qEVVT/1/



回答2:

Doing smooth animation on systems with a variety of different capabilities (CPU, graphics power, other things going on on the computer) is not a trivial task. A proper implementation involves developing an effective "tweening" algorithm that can figure out adaptively (as the animation runs) what increments to be using in the animation in order to stay on schedule and be as smooth as possible.

The best way to do this is to stand on the shoulders of others and use what has been invented before. In this day and age, I would never try to write this myself from scratch. It's there to use in CSS3 transitions/animations, but those aren't yet supported everywhere. It's there to use or analyze in jQuery and YUI3. My first choice would be to use one of the frameworks that has a rich set of capabilities here. You don't have to use the framework for anything else, you can just use it for the animation if you want. YUI3 will even let you construct a library that has the least code in it possible for just what you want. jQuery isn't very big to start with.

If you're still dead set against using one of the libraries, then download the source to the relevant modules for each library and study how they do it. Build a sample app in each and step through how it works, setting breakpoints at interesting spots. That will be the best teacher and show you how to build an effective tweening algorithm that can adapt to the speed capabilities of the host computer.

To give you an idea of how a tweening algorithm works for a straight animation (with linear easing), you make an initial calculation of what you want your animation step value to be for the time you want the animation to run. This is probably just a guess as to what the system can support. You divide the number of steps that creates into the time the animation runs and you set a timer for that amount of time so you know when to run the next step. You then run one or two steps of the animation and you see how much time has actually elapsed. If the computer can't keep up with your step value, you will be behind schedule and you will have to adapt and pick a larger step.

Now, if you want to do something other than linear easing, there's obviously even more involved.

Firefox and Chrome have also implemented some new experiemental APIs to help with smooth animation. I discovered this myself when looking at the jQuery source because it uses it when it's available. In Chrome it's called webkitRequestAnimationFrame and you can read about it here in a Firefox blog post.



回答3:

If you are targeting modern browsers, CSS transitions make the life easier (Example for firefox, for other browsers, change the -moz prefix):

<body>
    <input type="button" onclick="move()" value="press" />
    <div id="sq" style="position:absolute; top:50px; left:50px; height:50px; width:50px; background-color:Black; -moz-transition : all 0.8s ease 0s;" />
</body>

And the script

 function move() {
            var sq = document.getElementById("sq");
            sq.style.left = "300px";
            sq.style.top = "150px";
        }


回答4:

If I was going to write it from scratch I would start with something like this:

function linearEase(start, end, percent) {
    return start + ((end - start) * percent);
}

function animateTo(settings) {
    var elem = settings.element;
    var ease = settings.ease;

    var start = { left: elem.offsetLeft, top: elem.offsetTop };

    var lastTime = new Date().getTime();
    var timeLeft = settings.totalTime;

    function update() {
        var currentTime = new Date().getTime();
        var elapsed = currentTime - lastTime;
        timeLeft -= elapsed;
        lastTime = currentTime;

        var percentDone = 1 - timeLeft/settings.totalTime;

        elem.style.top = ease(start.top, settings.top, percentDone) + "px" ;
        elem.style.left = ease(start.left, settings.left, percentDone) + "px" ;

        if(timeLeft > 0) {
            setTimeout(update, 33);
        }   
    }

    update();
}

For example, to move a div to (50,50) over the next two seconds.

var elem = document.getElementById("animatable");

setTimeout(function() {
    animateTo({
        element: elem, 
        left: 50, 
        top: 50, 
        totalTime: 2000,
        ease: linearEase
    })
}, 10);

Which is a fairly standard pattern for doing this kind of stuff. Getting the element position and setting the style stuff could be better implemented for sure. But abstracting out an ease function will make your life a lot easier in the long run. I've provided a simple linear ease, but other more complicated easing algorithms would abide by that same interface.

Another thing to note is that timeouts and intervals are not guaranteed to run at a set time, so its usually best to set the total time that you want the transition to take to run, and then figure out how much time has elapsed since the last time you rendered.

Also if you are animating a bunch of elements at once, I would definitely refactor this to a single "render loop". calls to the animateTo would push workers into a queue of workers, but only have setTimeout loop that calculates the time elapsed then invokes each worker, so you don't have a bazillion timeout closures floating around.

Anyway, fiddle here