src/lib/util/TimelineUtils.ts
import { TweenMax, TimelineMax, Tween } from 'gsap';
import { ICreateTimelineOptions } from '../interface/ICreateTimelineOptions';
import TransitionDirection from '../enum/TransitionDirection';
import isFunction from 'lodash/isFunction';
/**
* The create timeline method creates a new TimelineLite or TimelineMax timeline
*
* @param {ICreateTimelineOptions} options
* @returns {TimelineMax}
*/
export function createTimeline(options: ICreateTimelineOptions): TimelineMax {
let forward = true;
let lastTime = 0;
const timeline = new TimelineMax({
paused: true,
onUpdate: () => {
// GreenSock does not support onReverseStart on a timeline therefore we have this little method
// chat checks for the direction and if it's changed we handle it as if it's a reverse start
const newTime = timeline.time();
if ((forward && newTime < lastTime) || (!forward && newTime > lastTime)) {
forward = !forward;
if (!forward && isFunction(options.onReverseStart)) {
options.onReverseStart();
}
}
lastTime = newTime;
},
onStart: () => {
// Reset the last time when we restart the timeline
lastTime = 0;
// Trigger the callback if needed
if (isFunction(options.onStart)) options.onStart();
},
onComplete: isFunction(options.onComplete) ? options.onComplete : null,
onReverseComplete: () => {
// When the transition out is completed we have to reset the last
// time otherwise the transition will no longer work.
lastTime = 0;
// Trigger the callback if needed
if (isFunction(options.onReverseComplete)) options.onReverseComplete();
},
});
return timeline;
}
/**
* Sometimes you want to fully kill a timeline and strip all the added
* inline styles. This method accepts a timeline and it will remove all
* the inline styling and kill the timeline instance.
*
* @param {TimelineMax} timeline
* @returns {void}
*/
export function killAndClearTimeline(timeline: TimelineMax): void {
clearTimeline(timeline);
timeline.kill();
}
/**
* Sometimes you do not want to kill the timeline but only kill the inline
* styling. This method accepts a timeline and it will remove all the inline styling.
*
* @param {TimelineMax} timeline
* @returns {void}
*/
export function clearTimeline(timeline: TimelineMax): void {
// debugger;
timeline.getChildren().forEach(target => {
if ((<Tween>target).target) {
// Note: When resetting a timeline clearing just the css properties does not clear the properties like autoAlpha or scale
TweenMax.set((<Tween>target).target, { clearProps: 'all' });
} else {
clearTimeline(<TimelineMax>target);
}
});
timeline.clear();
}
/**
* When you want to clone a timeline (for example when you want to nest it within
* another timeline but also still want to be able to play the original timeline
* this is the method you are looking for. It will create a new TimeLineLite or
* TimelineMax and re-add all the original animations and event listeners.
*
* @param {gsap.gsap.TimelineMax} source
* @param {TransitionDirection} direction
* @param {boolean} useTimelineMax
* @returns {TimelineMax}
*/
export function cloneTimeline(source: TimelineMax, direction: TransitionDirection): TimelineMax {
const children = source.getChildren(false);
const timeline = new TimelineMax(source.vars);
const parseChild = (child, timeline) => {
if (child.getChildren) {
const children = child.getChildren(false);
const subTimeline = new TimelineMax(child.vars);
// Parse the child animations
children.forEach(child => parseChild(child, subTimeline));
// Add the timeline to the parent timeline
timeline.add(subTimeline.restart(), child._startTime);
} else {
if (child.vars.startAt) {
if (direction === TransitionDirection.OUT) {
throw new Error('Do not use fromTo when nesting transitionOutTimelines, use to instead!');
}
const from = JSON.parse(JSON.stringify(child.vars.startAt));
// Clone the vars
const to = child.vars;
// Create the fromTo tween
timeline.fromTo(child.target, child._duration, from, to, child._startTime);
} else {
if (child.vars.runBackwards) {
// When nesting timelines and the user defines a root timeline with a from the clone will
// have incorrect styling because the base styling is off!
// timeline.from(child.target, child._duration, child.vars, child._startTime);
throw new Error(
'Do not use from while nesting transitionInTimelines, use fromTo instead!',
);
} else {
timeline.to(child.target, child._duration, child.vars, child._startTime);
}
}
}
};
children.forEach(child => parseChild(child, timeline));
return timeline;
}