Skip to content

Instantly share code, notes, and snippets.

@barneycarroll
Last active June 11, 2021 05:06
Show Gist options
  • Star 19 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save barneycarroll/c69fbe0786e37c941baf to your computer and use it in GitHub Desktop.
Save barneycarroll/c69fbe0786e37c941baf to your computer and use it in GitHub Desktop.
A factory for decorating Mithril modules / views / elements with incoming and outgoing animations.
var animating = false;
// Define an animator consisting of optional incoming and outgoing animations.
// alwaysAnimate is false unless specified as true: false means an incoming animation will only trigger if an outgoing animation is also in progress.
// forcing dontClone to true means the outward animation will use the original element rather than a clone. This could improve performance by recycling elements, but can lead to trouble: clones have the advantage of being stripped of all event listeners.
function animator( incoming, outgoing, alwaysAnimate, dontClone ){
// The resulting animator can be applied to any number of components
return function animate( x, y, z ){
var config;
var parent;
var next;
// When used as a config function
if( x.nodeType ){
return animationConfig( x, y, z );
}
// When passed a virtual DOM node (the output of m)
else if( x.attrs ){
return bindConfigTo( x );
}
// When applied to a Mithril module / component
else if( x.view ){
return {
controller : x.controller || noop,
view : function animatedView( ctrl ){
return bindConfigTo( x.view( ctrl ) );
}
};
}
function bindConfigTo( node ){
config = node.attrs.config;
node.attrs.config = animationConfig;
return node;
}
function animationConfig( el, init, context ){
var output;
var onunload;
if( config ){
output = config( el, init, context );
// If the root element already has a config, it may also have an onunload which we should take care to preserve
onunload = context.onunload;
}
if( !init ){
if( incoming && alwaysAnimate || animating ){
incoming( el, noop, context );
}
context.onunload = outgoing ? onunload ? function onunloadWrapper(){
teardown();
onunload();
} : teardown : onunload;
parent = el.parentElement;
next = el.nextSibling;
}
return output;
function teardown(){
var insertion = dontClone ? el : el.cloneNode( true );
var reference = null;
if( next && parent && next.parentNode === parent ){
reference = next;
}
animating = true;
setTimeout( function resetAnimationFlag(){
animating = false;
}, 0 );
parent.insertBefore( insertion, reference );
outgoing( insertion, function destroy(){
if( parent.contains( insertion ) ){
parent.removeChild( insertion );
}
}, context );
}
}
};
}
function noop(){}
@brainwork
Copy link

When passing modules/components in here it actually fails to use the existing config in this version, because of the line config = view.config; which should also read config = view.attrs.config; as it does in other places.

@barneycarroll
Copy link
Author

@brainwork finally got around to fixing that. Thanks!

@benmerckx
Copy link

Setting the dontClone option doesn't seem to work (anymore?). The element doesn't seem to be inserted again in teardown. Any idea if that can be fixed?

@togoog
Copy link

togoog commented Mar 9, 2017

this version for mithril.js v1.0.1 report error

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment