Ascendro Blog

Zeige Ergebnisse 1-1 von 1.

Ascendro Point Animation

Clean, Intuitive, Professional... See the story behind our website's front page animation.

The animation should represent actual data, lots of data, be fast, usable and look good.

The concept behind was to communicate how we can organize customer ideas into a well defined product. Each point represents a product, on mouse over this points should align into a grid – each row representing a product category, each column representing a customer.

On normal sized screens this is about 100 animated points already. Not considering the amount on bigger screens.

As a mouse over animation this shouldn’t take longer than half a second and be fluid.

The next problem was the path the animated items should take – they shouldn’t go straight up to their organized positions but going a curve – requiring a lot of math.

First prototypes using classical jQuery animations or pure Javascript DOM manipulation didn’t result in an acceptable performance and it was clear that a lot of research has to be done in order to generate acceptable paths for the animated object.

The solution: SVG Pathes

Scalable Vector Graphics are around since quite some time (W3C published Version 1.0 of the Specification 2001) and is supported by all the serious browsers out there. As the website needs to work also without Javascript, IE <= 8 Users will still be able to access the website.

The SVG Path element defines a line containing several curved or straight lines. Defined by mathematic formulas we can shape a path how we intend to.

Using this paths we generate a path from a random start to the organized endpoint, adding additional fixpoints for generating curves. (We don’t care about the mathematics here, we just used two points at the edge of the container).

All animated objects get a path from their start point to their endpoint

Initialization of such a graph:

Code (JS):

var x = 0;var y = 0;
var element = null;

//Two fixpoints, generating the curved pathes
var helpingPoint1 = new Array(middleX,0);
var helpingPoint2 = new Array(middleX,windowHeight);

//Generating 2 dimensional matrix
for (x = 0;x <  Math.floor(windowWidth/settings.distanceX);x++) {
    for (y = 0;y < Math.floor(windowHeight/settings.distanceY) && (settings.maxRows == 0 || y < settings.maxRows);y++) {
        //Base of animated object is a div element
        element = $('');
        //Provided function pointer from the caller, giving the possibility to attach own content in the animated object
        element.append(settings.elementContentCallback(x,y));

        //Generating a chaotic coordinate within the system
        var chaotic = randomPointInEllipse(windowWidth - 2 * settings.offsetX,windowHeight - 2 * settings.offsetY,settings.uniformChaos);
        //Saving start, end, fixpoint positions and the length of the path in the animated object
        element.data = {
            ordered : new Array(settings.offsetX + x * settings.distanceX, settings.offsetY + y * settings.distanceY),
            chaotic : new Array(settings.offsetX + Math.floor(chaotic[0] + middleX),settings.offsetY + Math.floor(chaotic[1] + middleY)),
            p1 : helpingPoint1,
            p2 : helpingPoint2,
            length : 0
        };
        //Generationg SVG Path element, and giving it the information in order to generate the path.
        element.path = document.createElementNS('http://www.w3.org/2000/svg','path'); 
        element.path.setAttribute('d','M'+element.data.chaotic.join(',')+'C'+element.data.p1.join(',')+' '+element.data.p2.join(',')+' '+element.data.ordered.join(','));

        //Saving the length of the path
        element.data.length = element.path.getTotalLength();

        //Setting the animated object on its starting position
        element.css('left',element.data.chaotic[0]+'px');
        element.css('top',element.data.chaotic[1]+'px');

        //Adding the element to the list of animated elements
        elements.push(element);

        //Adding the element to the dom
        parent.append(element);
    }
}

If we want to see the pathes we just need to add the generated path element to an SVG Element:

Code (JS):

$(SVGElement).append(element.path);

For specific informations about the path element please look at http://www.w3.org/TR/SVG/paths.html. For our animation it was enough to define the start and endpoint and too fix points, the path can generate a curve with, but if needed more complex paths can be generated.

The important thing for our animation is the length of the path. The SVG Path element has a method called getPointAtLength, returning a specific point of the path after a specific length.

If we are at 50% process of the animation we want to get the point of the graph which is at 0.5*length of the path. Same for 75%,1%,100% etc.
As ever animated object has a own unique path and length, this method needs to be called for every single object in every single animation frame.
It’s getting clear that this Method can get the bottleneck of the animation performance, depending on how the underlying implementation of the SVG Object was realized.

Code (JS):

//Storing a reference to the animation time interval
var animationTimeEvent = null;
//Start time of the animation
var animationStartTime = 0;
//Duration of the animation
var animationDuration = 0;
//Frame counter for analysis
var animationFrameCounter = 0;

//Starting the animation, with a duration and a frame per second limitation
function animationStart(duration,fps) {
    //Setting default values, if parameters are not given
    if(typeof(duration)==='undefined') duration = settings.duration;
    if(typeof(fps)==='undefined') fps = settings.fps;

    //Stop previous started animation, if any
    clearInterval(animationTimeEvent);

    //Setting start values
    animationStartTime = new Date;
    animationFrameCounter = 0;
    animationDuration = duration;            

    //Animation main “loop” as a javascript interval
    animationTimeEvent = setInterval(function(){
        //Calculation procentual process of the animation
        var now = new Date;
        var elapsed = now-animationStartTime;
        var percent = elapsed/animationDuration;

        //Counting and publishing frames of the animation
        animationFrameCounter = animationFrameCounter + 1;
        if (settings.depugSVGElement) {
            $(settings.depugSVGElement + ' text').text(animationFrameCounter/elapsed*1000);
        }
        //If the procentual progress is >= 100% the animation is finished
        if (percent>=1){
            percent = 1;
            animationStartTime = 0;
            animationFrameCounter = 0;
            clearInterval(animationTimeEvent);
        }

        //Processing all the animated object, getting the position on the path in that animation state and making the transformation in the dom
        var i = 0;
        var len = elements.length;
        for (i = 0;i < len;i++) {
            var newPosition = elements[i].path.getPointAtLength(elements[i].data.length*percent);
            elements[i].css('left',newPosition.x+'px');
            elements[i].css('top',newPosition.y+'px');
        }
    },Math.floor(1000 / fps));    
}

Performance:

The animation needed to be fluid and, more important, finished within the half second. A low performing computer may have less frames but still should finish in the same time. This is important in order to let the user not wait that much time until the mouse over animation is finished.

The human eye can process 10 to 12 separate images per second on average – unfortunately that’s not the target frame rate as the human eye can notice different changes much faster than that.
16-24 FPS are enough to show motion, 46 as the minimum for motion which will not strain the eye.

As we don’t want to show a two hour movie, 24 FPS should be acceptable for us, 50 FPS the maximum we should allow. (We don’t need to waste processor time)

Implementing an upper limit for a framerate is very easy, you just need to wait 1000/MAXFPS between each animation step. The lower limitation is simply given by processor runtime assigned to the process.

But how do we ensure that the animation is finished in time? We can do that by executing a dynamic amount of frames. So a 500ms animation doesn’t has 25 animation cycles (on 50FPS) but as much animation cycles which can fit in a time period of 500ms. (Maximum 25 for the upper FPS limit).

Each cycle it needs to be calculated how many time passed since the start of the animation and how long the animation should be. From this information we can get the percentual stage of the animation.

Each cycle need to calculate how many time passed since the start of the animation and how long the animation should be. From this information we can get the percentual stage of the animation.

A low performing computer who needs 250ms to run one animation step will therefore just do 2 Frames until the animation is finished. It will not be a fluid animation but the user don’t has to wait more time until he can perform use the website.

During performance tests it was possible to have 2100 animated objects with 30 Frames per Seconds on a 3Ghz Processor, calling the getPointAtLength method. . Adding the DOM Manipulation it went down to 4 Frames per Seconds.

Some statistics for the complete animation:

Element Count Frames per Second
2088 4.95
1032 6.68
516 9.92
344 (Max expected) 16.09
216 (Big Screen) 28.23
144 (Average expected) 40.96
72 (Small Screen) 107.5

We have a very linear performance impact and the animation is fluid in our expected use cases.

Mission accomplished.

The animation can be tested on our website www.ascendro.de

LEAVE A COMMENT

Keine Ergebnisse gefunden.