// ACT J anima
/* ======================================= v 0.6.1
@ args obj/string used as @args->target if is string
@ target obj/string element or selector
@ mode string element / items
@ delay float delay between items (seconds)
@ play string onLoad / onClick / custom event
@ animation array array of animation actions [name, duration, value]
@ animArray array used as @args->animation if @args is a string
======================================= */
function ACTJanima( args, animArray ){
animArray = animArray || [];
const defaults = {
target : '.Janima',
mode : 'element', // element, items
delay : 0.1,
units : 'px',
dir : 'in',
animation : [/* ['name', duration], .. */],
}
let animDefaults = {
time : 1,
offset : 100,
scale : 200,
blur : 10,
rot : 0,
fade : 0,
x : 100,
y : 100,
z : 0,
ease : 'ease',
play : false, // onLoad, onClick
}
const ease = {
easeIn : 'ease-in',
easeOut : 'ease-out',
easeInOut : 'ease-in-out',
ease : 'ease',
linear : 'linear',
elastic : 'cubic-bezier(0.25, 0.00, 0.30, 1.65)',
elasticOut: 'cubic-bezier(0.00, 0.00, 0.40, 1.35)',
}
let $target,
ITEMS = {},
anim = {};
// Initialize
init();
if ( ! $target.length ) return;
initElements();
playbackEvents()
// --------------------------------------
function init(){
args = args || {};
if ( typeof args == 'string' ) {
args = {
target: args,
animation: animArray,
};
}
args = $.extend(true, {}, defaults, args);
// in default mode, scan for elements with aj- classes and no Janima
if ( args.target == '.Janima' ) {
$('[class*="AJ-"]').addClass('Janima')
}
$target = args.target;
if ( typeof args.target == 'string' ) $target = $( args.target );
}
function initElements() {
// Loop through targets
$target.each(function(idx){
// set id
let id = 'A-' + $.now() + idx;
let _$targ = $(this)
if ( _$targ.is('[class*="aj-items"]') ) args.mode = 'items';
if ( _$targ.is('[class*="AJ-items"]') ) args.mode = 'items';
_$targ.attr('data-anima', id )
ITEMS[id] = {
mode : args.mode,
keys : [],
values : [],
}
// Parse Animation
if ( ! parseAnimation( _$targ ) ) return;
// get values of first step
getAnimationValues( _$targ, ITEMS[id].values );
// initialize elements
if ( ITEMS[id].mode == 'element' ) {
_$targ.css({
transition : '0s all',
//visibility : 'hidden',
})
initElement( _$targ );
} else {
// or initialize children
_$targ.children().css({
transition : '0s all',
//visibility : 'hidden',
})
_$targ.children().each(function(idx2){
$(this).attr('data-anima', id )
initElement( $(this) );
})
}
})
}
function parseAnimation( element ){
animDefaults.x = animDefaults.offset;
animDefaults.y = animDefaults.offset;
let id = element.attr('data-anima')
// get animation from classes
if ( element.attr('class') ) {
let classList = element.attr('class').split(/\s+/);
classList.forEach(clss =>{
if ( clss.substr(0,3).toLowerCase() == 'aj-' ) {
ITEMS[id].values.push( clss.substr(3) );
}
})
}
// get animation from Javascript arguments
if ( typeof args.animation == 'string' ) args.animation = [ args.animation ]
if ( args.animation.length ) {
args.animation.forEach(step =>{
ITEMS[id].values.push( step );
})
}
if ( ! ITEMS[id].values.length ) return false;
// parse animation values
let tmp = []
ITEMS[id].values.forEach(step =>{
let values = {}
step.split('-').forEach(v=>{
let key = v.split(':')[0];
let val = v.split(':')[1] || animDefaults[ key ];
if ( key == 'left' ) val = -val || -animDefaults.x;
if ( key == 'top' ) val = -val || -animDefaults.y;
if ( key == 'left' ) { key = 'x'; val = parseFloat( val ) }
if ( key == 'right' ) { key = 'x'; val = parseFloat( val ) }
if ( key == 'top' ) { key = 'y'; val = parseFloat( val ) }
if ( key == 'bottom' ) { key = 'y'; val = parseFloat( val ) }
if ( typeof val == 'string' ) val = val.replace('__','-')
if ( typeof val == 'undefined' || val == null || Number.isNaN(val) )
val = animDefaults[ key ]
// play
if ( key == 'play' ) {
val = val || 'onLoad';
ITEMS[id].play = val;
// items
} else if ( key == 'items' ) {
if ( val ) ITEMS[id].delay = parseFloat( val )
// out
} else if ( key == 'out' ) {
if ( val ) ITEMS[id].dir = 'out';
// rest values
} else {
values[ key ] = val
}
})
tmp.push( values )
})
ITEMS[id].values = tmp;
return true;
}
function getAnimationValues( element, values ){
let units = '%';
let id = element.attr('data-anima')
let XX = 0,
YY = 0,
ZZ = 0,
SC = 1,
RT = 0,
BL = 0,
OP = 1;
ITEMS[id].keys[0] = {
transform : `translate3d(0, 0, 0) scale(1) rotate(0deg)`,
filter : 'blur(0px)',
opacity : element.css('opacity') || OP,
}
// Loop through animation values
let idx = 0;
$.each(values, function(i, animation){
idx++;
ITEMS[id].keys[idx] = {
_time: animDefaults.time,
_ease: animDefaults.ease,
}
$.each(animation, function(key, val){
// control
if ( key == 'time' ) ITEMS[id].keys[idx]._time = parseFloat( val );
if ( key == 'ease' ) ITEMS[id].keys[idx]._ease = ease[ val ];
// fade
if ( key == 'fade' ) OP = parseFloat( val );
// top / bottom
if ( key == 'y' ) {
val = parseFloat(val);
YY = (YY + val);
}
// left / right
if ( key == 'x' ) {
val = parseInt(val);
XX = (XX + val);
}
// scale
if ( key == 'scale' ) SC = parseFloat( val ) / 100;
// rotation
if ( key == 'rot' ) RT = parseFloat( val )
// blur
if ( key == 'blur' ) BL = parseFloat( val )
})
let u = args.units
ITEMS[id].keys[idx].transform =
`translate3d(${XX+u}, ${YY+u}, ${ZZ+u}) scale(${SC}) rotate(${RT}deg)`
ITEMS[id].keys[idx].filter = `blur(${BL}px)`;
ITEMS[id].keys[idx].opacity = OP;
if (args.dir != 'out' && idx == 1 ) {
XX = 0; YY = 0; ZZ = 0; SC = 1; RT = 0; BL = 0; OP = 1;
}
})
if (args.dir != 'out') {
let tmp0 = JSON.stringify( ITEMS[id].keys[0] )
let tmp1 = JSON.stringify( ITEMS[id].keys[1] )
ITEMS[id].keys[0] = JSON.parse( tmp1 )
ITEMS[id].keys[1] = JSON.parse( tmp0 )
ITEMS[id].keys[1]._ease = ITEMS[id].keys[0]._ease
ITEMS[id].keys[1]._time = ITEMS[id].keys[0]._time
delete ITEMS[id].keys[0]._ease;
delete ITEMS[id].keys[0]._time;
}
return ITEMS[id];
}
function initElement( element ){
let id = element.attr('data-anima')
// Set starting position
element.css('transition', 'unset');
$.each(ITEMS[id].keys[0], function(key, val){
if ( key.substr(0,1) != '_' ) element.css(key, val);
})
}
// --------------------------------------
function play( _$target ){
_$target = _$target || $( args.target )
_$target.each(function(){
let id = $(this).attr('data-anima')
if ( $(this).is('[class*="aj-items"]') ) ITEMS[id].mode = 'items';
if ( $(this).is('[class*="AJ-items"]') ) ITEMS[id].mode = 'items';
if ( ITEMS[id].mode == 'element' ) animate( $(this) )
if ( ITEMS[id].mode == 'items' ) animateItems( $(this) )
})
}
function playbackEvents(){
$target.each(function(){
let _$target = $(this);
let id = _$target.attr('data-anima')
if ( ITEMS[id].play ) {
if ( ITEMS[id].play == 'onLoad' ) play( _$target )
else if ( ITEMS[id].play == 'onClick' ||
ITEMS[id].play == 'click' ) _$target.click( play )
else {
if ( ITEMS[id].play == 'reveal' ) ITEMS[id].play = 'revealMid'
ITEMS[id].play = ITEMS[id].play.replace('__', '-')
$('body').one(ITEMS[id].play, function(){ play( _$target ) })
_$target.one( ITEMS[id].play, function(){ play( _$target ) })
}
}
})
}
// --------------------------------------
function animateItems( _target ){
_target.children().each(function(idx){
setTimeout(e =>{
animate( $(this) )
}, idx * args.delay * 1000)
})
}
function animate( element ){
let id = element.attr('data-anima')
if ( !id ) return;
let time = 0;
// Animation Loop
ITEMS[id].keys.forEach((animation, i) =>{
if ( i == 0 ) return;
setTimeout(function(element, animation, i){
applyAnimationValues( element, animation, i )
}, time * 1000, element, animation, i)
time += parseFloat( animation._time || 0 );
})
// Animation End
//setTimeout( animationEnd, time * 1000 )
}
function applyAnimationValues( element, animation, idx ){
let id = element.attr('data-anima')
element.css('visibility', 'visible')
element.css('transition', animation._time + 's transform, ' +
animation._time + 's opacity, ' +
animation._time + 's filter ' + animation._ease)
$.each(animation, function(key, val){
if ( key.substr(0,1) != '_' ) element.css(key, val)
})
}
// --------------------------------------
return {
play : play,
}
}