February 4, 2015

How to avoid blurring of the text in Famo.us during transition in Zdirection

Famo.us is a new JavaScript framework that includes an open source 3D layout engine fully integrated with a 3D physics animation engine that can render to DOM, Canvas, or WebGL.
Famo.us is a great framework to create really cool UI’s with 2d/3d animations of any kind. However I’ve noticed some bugs (which is ok as the framework is new) and some limitations due to the fact that some browsers could not correctly apply 3dmatrices. One of the hugest limitation on my opinion is blurring of the text during z-translate of the elements. This means that we simply can not use perspective transitions if our elements contain text.
I was trying to find workaround and as a result came up with the following solution:

1) do actual transition in z-direction
2)  scale projection of the surface back to its original size
3) resize our element to fit needed projection size
4) resize font

here is the codepen link

define(function (require, exports, module) {
    var Engine = require('famous/core/Engine');
    var Surface = require('famous/core/Surface');
    var Transform = require('famous/core/Transform');
    var StateModifier = require('famous/modifiers/StateModifier');
    var Transitionable = require('famous/transitions/Transitionable');

    var perspective = 1000;
    var fontValue = 100;      //initially font-size is 100%
    var surfSize = [100, 100];

    var mainContext = Engine.createContext();
    mainContext.setPerspective(perspective);
    var transitionable = new Transitionable(0);

    var mySurface = new Surface({
        size: surfSize,
        properties: {
            backgroundColor: 'red',
            textAlign: 'center',
            color: 'white',
            fontSize: fontValue + '%',
            lineHeight: surfSize[1] + 'px'
        },
        content: 'Click Me'
    });

    var transitionModifier = new StateModifier({
        origin: [.5, .5],
        align: [.5, .5],
        transform: Transform.translate(0, 0, 0.01)
    });

    mainContext.add(transitionModifier).add(mySurface);

    function translateZ(dist, transition) {
        transitionable.reset(0);
        transitionable.set(dist, transition);

        function prerender() {
            var currentDist = transitionable.get();
            //perspective formula: dist = perspective(1 - 1/scaleFactor)
            var currentScale = 1 / (1 - currentDist / perspective);
            var currentSize = [surfSize[0] * currentScale, surfSize[1] * currentScale];
            var currentFontValue = fontValue * currentScale;  

            //f.e: bring closer => make projection scaleFactor times bigger
            var transitionTransform = Transform.translate(0, 0, currentDist);
            //scaling back to avoid text blurring            
            var scaleTransform = Transform.scale(1 / currentScale, 1 / currentScale, 1);
            transitionModifier.setTransform(Transform.multiply(transitionTransform, scaleTransform));

            mySurface.setSize(currentSize); //resize to get correct projection size                                                     
            mySurface.setOptions({
                properties: {
                    fontSize: currentFontValue + '%', //resizing font;                                                  
                    lineHeight: currentSize[1] + 'px' //align text;                                                  
                }
            })

            if (currentDist === dist) {
                Engine.removeListener('prerender', prerender);
            }
        }

        Engine.on('prerender', prerender);
    }

    Engine.on('click', function () {
        translateZ(750, { curve: 'easeOutBounce', duration: 2000 });
    });
});
This approach could be extended to use ContainerSurfaces as a containers where you will set fontSize in %. ContainerSurface will propagate this property to its children.