ractivejs / ractive

Next-generation DOM manipulation
http://ractive.js.org
MIT License
5.94k stars 397 forks source link

Question: CSS animations for transitions? #1008

Closed rstacruz closed 7 years ago

rstacruz commented 10 years ago

Is there a transitions adaptor out there today to perform transitions as basic CSS animations? It'd be nice to be able to do, say:

<div intro="animate:fadein">

...which will add the fadein class to the element and wait for the animationend event. I believe this is easily achievable, but I was wondering if there was already a plugin written for this.

Rich-Harris commented 10 years ago

Not to my knowledge. Would definitely make sense to have a plugin that did that. I'm slightly less optimistic about how easy it'd be - the plugin would have to know whether an animation had been defined in CSS, whether there was a valid @keyframes declaration, and whether CSS animations are supported in the current environment - but I would love to be proven wrong!

Here's a simple version - http://jsfiddle.net/rich_harris/QmR33/. Here's the exact same thing, but without a @keyframes declaration - it all falls apart, because it's listening for an event that never happens. Would be great if there was a way to work around that issue.

rstacruz commented 10 years ago

The way I thought of implementing this was to have a timeout. In pseudo-code:

teardown = _.once(function() { ... });
node.on('animationend', teardown);
setTimeout(teardown, 1000);

Not exactly the best way to handle it, but I've yet to find a way to detect if animations are ongoing. (I imagine it's possible though through some hackery, but I'd avoid things that would unnecessarily do DOM layout thrashing.)

martypdx commented 10 years ago

For [webkit]AnimationEnd, you need to guard the event with if(e.target!=node) { return } as it will fire for child nodes, for transitionEnd it only fires on the transitioned node.

Rich-Harris commented 10 years ago

@martypdx wow. Only one possible response to that

463px-facepalm

martypdx commented 10 years ago

@Rich-Harris haha yeah, it was fun figuring that out. If it helps anyone, here's some code I wrote a couple of years ago using jQuery (webkit only). I tried to come up with names for the different variations (transition, animation, leave class, remove class), but I just ended up having to look into the code to figure out what each one meant. I like the idea of a more generic "just works"

function changeFn(type, removeClass){
    var eventName = 'webkit' + type + 'End'
    return function(className, delegate, cb){
        if( typeof delegate === 'function'){ 
            cb = delegate
            delegate = this
        }
        if(!delegate.nodeType) { delegate = delegate[0] }

        var $this = $(this)
            .addClass(className)
            .on(eventName, function(e){
                if(e.target!=delegate) { return }

                $this.off(eventName)
                if(removeClass) { $this.removeClass(className) }
                cb()
            } ) 
    }
}

$.fn.segue = changeFn('Transition' , true)
$.fn.transition = changeFn('Transition', false)
$.fn.bridge = changeFn('Animation', true)
$.fn.animation = changeFn('Animation', false)
cfenzo commented 10 years ago

Here's a quick animate.css transition wrapper... (requires animate.css)

(function ( global, factory ) {

    'use strict';

    // Common JS (i.e. browserify) environment
    if ( typeof module !== 'undefined' && module.exports && typeof require === 'function' ) {
        factory( require( 'ractive' ) );
    }

    // AMD?
    else if ( typeof define === 'function' && define.amd ) {
        define([ 'ractive' ], factory );
    }

    // browser global
    else if ( global.Ractive ) {
        factory( global.Ractive );
    }

    else {
        throw new Error( 'Could not find Ractive! It must be loaded before the ractive.transitions.animatecss plugin' );
    }

}( typeof window !== 'undefined' ? window : this, function ( Ractive ) {

    'use strict';

    var ANIMATION_END_EVENT = (function(){
        var eventName,
            ANIMATION_END_EVENT_NAMES = {
                'animation': 'animationend',
                '-o-animation': 'oAnimationEnd',
                '-moz-animation': 'animationend',
                '-webkit-animation': 'webkitAnimationEnd',
                '-ms-animation': 'animationend'
            },
            fakeEl = document.createElement('fakeelement');

        for (eventName in ANIMATION_END_EVENT_NAMES) {
            if (typeof(fakeEl.style[eventName]) !== 'undefined') {
                return ANIMATION_END_EVENT_NAMES[eventName];
            }
        }
        return null;
    })();

    var animateCSS = function ( t, transition ) {
        // Process parameters. No error checking for non-existing transitions(!!)
        transition = transition || (t.isIntro ? Ractive.transitions.animatecss.defaultIntro : Ractive.transitions.animatecss.defaultOutro);

        // add 'animated' class if not present
        if(!t.node.classList.contains('animated')) t.node.classList.add('animated');
        // add the transition class, the transitions starts immediately
        t.node.classList.add(transition);
        // After the animation has ended, call `t.complete()` to signal that we've finished
        t.node.addEventListener(ANIMATION_END_EVENT,function(){
            t.node.classList.remove(transition);
            t.complete();
        });
    };

    Ractive.transitions.animatecss = animateCSS;
    Ractive.transitions.animatecss.defaultIntro = 'slideInDown';
    Ractive.transitions.animatecss.defaultOutro = 'slideOutUp';

}));

usage:

<div intro="animatecss:fadeIn" outro="animatecss:fadeOut">woosh</div>

Allthough fun to use, we've moved on to using velocity.js' UI pack for transitions (feels better to not rely on transitionend really, and it's actually smaller in size than animate.css, which is quite large). Will try to get it up to speed with the latest velocity.js UI pack changes tomorrow and post back :)

julianshapiro commented 10 years ago

@cfenzo: I'm actually interested in building (or having someone else build) a Velocity plugin for Ractive.... As a new (and proud!) Ractive user, I'd like to make this happen in the next few weeks.

cfenzo commented 10 years ago

@julianshapiro: interesting.. There's probably more use cases for velocity+Ractive than transitions only tho.. right? :) What we've done in the project where we're using it now, is having a small wrapper push the whole UI pack as custom transitions, so you can do intro="transition.fadeIn:ms/options". Works like a charm :)

julianshapiro commented 10 years ago

Interesting. Would love to get my hands on that, please :) Also, as for more use cases: That's my hope (fingers crossed).

martypdx commented 10 years ago

There's probably more use cases for velocity+Ractive than transitions only tho.. right? :)

@cfenzo: Probably decorator too, using data state to trigger animations

cfenzo commented 10 years ago

@julianshapiro I'll clean it up a bit and share after work tomorrow :) @martypdx yeah, that too!

Rich-Harris commented 10 years ago

@cfenzo @julianshapiro I was meaning to ask, is the velocity plugin in a shareable state yet? The demo page looks pretty solid, would love to get this up on the plugins section of the docs :-)

julianshapiro commented 10 years ago

@cfenzo has been patiently waiting on me to take a look at it, but I've been preoccupied with the removal of Velocity's jQuery dependency.

I will finally look at this tomorrow and get back to both of you in this thread. Ty!

cfenzo commented 10 years ago

@Rich-Harris as @julianshapiro mentions above, we've been waiting for the removal of Velocity.js' jQuery dependency. It's really all about the AMD loading and how it ends up being implemented in Velocity (how to get Velocity in various environments, with or without jQuery). Hopefully it will be ready for primetime in a day or two :)

(But we do use it in production already ;) )

constantx commented 9 years ago

@julianshapiro @Rich-Harris would love a velocity-power transition plugin as well :)

constantx commented 9 years ago

@cfenzo just checkout your ractive-transitions-velocity, look like it still has jQuery dependency, any plan to migrate?