animatry
Version:
Javascript animation library.
2,035 lines • 82.6 kB
JavaScript
let globalOptions = {};
function setGlobalOptions(options) {
globalOptions = { ...globalOptions, ...options };
}
const controllerFunctions = () => {
return {
onUpdate: () => { },
onChange: () => { },
onStart: () => { },
onComplete: () => { },
onReverseStart: () => { },
onReverseComplete: () => { },
onRepeat: () => { },
};
};
const controllerOptions = () => {
return {
id: undefined,
delay: 0,
delayRecharge: false,
duration: 1,
repeat: 0,
iterationDelay: 0,
alternate: false,
paused: false,
ease: undefined,
alternateEase: undefined,
playhead: 0,
iteration: 0,
reversed: false,
timeScale: 1,
at: undefined,
preRender: false,
stagger: 0,
keyframes: undefined,
backwards: false,
};
};
const controllerSettings = () => {
return Object.assign({}, controllerFunctions(), controllerOptions(), globalOptions);
};
var _a;
class Ease {
static steps(numSteps = 3) {
const stepSize = 1 / numSteps;
const halfStepSize = stepSize / 2;
return (x) => {
const centeredX = x + halfStepSize;
const stepIndex = Math.floor(centeredX / stepSize);
return stepIndex * stepSize;
};
}
static cubicBezier(p1x, p1y, p2x, p2y) {
function cubic(t, a1, a2) {
const c = 3 * a1;
const b = 3 * (a2 - a1) - c;
const a = 1 - c - b;
return ((a * t + b) * t + c) * t;
}
function derivativeCubic(t, a1, a2) {
const c = 3 * a1;
const b = 3 * (a2 - a1) - c;
const a = 1 - c - b;
return (3 * a * t + 2 * b) * t + c;
}
return function (x) {
let t = x;
for (let i = 0; i < 5; i++) {
const xEstimate = cubic(t, p1x, p2x);
const derivative = derivativeCubic(t, p1x, p2x);
if (Math.abs(xEstimate - x) < 1e-5)
return cubic(t, p1y, p2y);
t -= (xEstimate - x) / derivative;
}
return cubic(t, p1y, p2y);
};
}
;
static parse(ease) {
if (typeof ease == 'string') {
const easeFunctionMatch = ease.match(/(\w+)\(([^)]*)\)/);
if (easeFunctionMatch) {
const functionName = easeFunctionMatch[1];
const argsString = easeFunctionMatch[2].trim();
const args = argsString ? argsString.split(',').map(arg => parseFloat(arg)) : [];
if (this[functionName] !== undefined) {
return this[functionName](...args);
}
animatry.warn(`ease '${functionName}' is not defined.`);
return _a.linear();
}
if (this[ease] == undefined) {
animatry.warn(`ease '${ease}' is not defined.`);
return _a.linear();
}
return this[ease]();
}
return ease;
}
}
_a = Ease;
Ease.none = () => (x) => x;
Ease.linear = () => (x) => x;
Ease.powerIn = (power = 1) => (x) => Math.pow(x, power + 1);
Ease.powerOut = (power = 1) => (x) => 1 - Math.pow(1 - x, power + 1);
Ease.powerInOut = (power = 1) => (x) => x < 0.5 ? 0.5 * Math.pow(2 * x, power + 1) : 1 - 0.5 * Math.pow(2 * (1 - x), power + 1);
Ease.bounceIn = () => (x) => 1 - _a.bounceOut()(1 - x);
Ease.bounceOut = () => (x) => {
if (x < 1 / 2.75) {
return 7.5625 * x * x;
}
else if (x < 2 / 2.75) {
return 7.5625 * (x -= 1.5 / 2.75) * x + 0.75;
}
else if (x < 2.5 / 2.75) {
return 7.5625 * (x -= 2.25 / 2.75) * x + 0.9375;
}
else {
return 7.5625 * (x -= 2.625 / 2.75) * x + 0.984375;
}
};
Ease.bounceInOut = () => (x) => x < 0.5 ? 0.5 * _a.bounceIn()(x * 2) : 0.5 * _a.bounceOut()(x * 2 - 1) + 0.5;
Ease.elasticIn = (frequency = 6) => (x) => 1 - _a.elasticOut(frequency)(1 - x);
Ease.elasticOut = (frequency = 6) => (x) => {
if (x < 0)
return 0;
if (x > 1)
return 1;
const decay = Math.pow(0.025, x);
const oscillation = Math.pow(x, frequency ** 2);
const smoothing = Math.pow(1 - x, 1);
return 1 + Math.sin(x * frequency * Math.PI - Math.PI / 2) * decay * (1 - oscillation) * smoothing;
};
Ease.elasticInOut = (frequency = 6) => (x) => x < 0.5 ? _a.elasticIn(frequency)(x * 2) * 0.5 : _a.elasticOut(frequency)(x * 2 - 1) * 0.5 + 0.5;
Ease.backIn = (magnitude = 1.70158) => (x) => x * x * ((magnitude + 1) * x - magnitude);
Ease.backOut = (magnitude = 1.70158) => (x) => 1 + ((x - 1) ** 2 * ((magnitude + 1) * (x - 1) + magnitude));
Ease.backInOut = (magnitude = 1.70158) => (x) => x < 0.5 ? 0.5 * _a.backIn(magnitude)(x * 2) : 0.5 * _a.backOut(magnitude)(x * 2 - 1) + 0.5;
class Controller {
constructor(options) {
this.delayProgress = 0;
this.iterationDelayProgress = 0;
this.isPaused = false;
this.isReversed = false;
this.playhead = 0;
this.frame = -1;
this.tick = 0;
this.initialized = false;
this.options = Object.assign(controllerSettings(), options);
this.options.ease = Ease.parse(this.options.ease);
this.options.alternateEase = Ease.parse(this.options.alternateEase);
this.playhead = this.options.playhead;
this.setReversed(this.options.reversed);
Promise.resolve().then(() => {
if (!this.getParent() && !this.options.paused && !this.isPaused)
this.play();
});
}
_render() {
if (this.parent != undefined)
return;
const now = performance.now();
const deltaTime = (this.isReversed ? this.tick - now : now - this.tick) * this.getTimeScale() / 1000;
this.tick = now;
if (!this.isReversed && this.delayProgress < 1 || this.options.delayRecharge && this.isReversed && this.getTotalProgress() == 0) {
this.setDelayProgress(this.getDelay() > 0 ? (this.getDelayProgress() + deltaTime / this.getDelay()) : 1);
}
else {
this.setTotalProgress(this.getTotalProgress() + deltaTime / (this.getTotalDuration() || 1e-8));
}
if (!this.isPaused) {
this.frame = requestAnimationFrame(this._render.bind(this));
}
}
play(seek = undefined) {
if (this.playhead == 1)
return;
this.isPaused = false;
this.tick = performance.now();
this.isReversed = false;
if (this.frame == -1)
this._render();
if (seek != undefined)
this.seek(seek);
}
pause() {
this.isPaused = true;
cancelAnimationFrame(this.frame);
this.frame = -1;
if (this.getDelayProgress() != 1) {
this.setDelayProgress(0);
}
}
reverse() {
if (this.playhead == 0)
return;
this.isPaused = false;
this.tick = performance.now();
this.isReversed = true;
if (this.frame == -1)
this._render();
}
continue() {
if (this.isReversed) {
this.reverse();
}
else {
this.play();
}
}
restart() {
this.pause();
this.setTotalProgress(0);
this.play();
}
reset() {
this.setDelayProgress(0);
this.setTotalProgress(0);
}
seek(elapsed) {
this.setTotalElapsed(elapsed);
}
complete() {
this.setTotalProgress(1);
}
lastPlay() {
this.setIteration(this.getReversed() ? 0 : this.getRepeat());
}
getInitialized() {
return this.options.preRender || this.initialized;
}
setInitialized() {
this.initialized = true;
}
getParent() {
return this.parent;
}
setParent(parent) {
this.parent = parent;
}
getId() {
var _a;
return (_a = this.options.id) !== null && _a !== void 0 ? _a : '';
}
setId(id) {
this.options.id = id;
}
getDuration() {
return Math.min(this.options.duration, 10 ** 8);
}
setDuration(duration, keepProgress = true) {
if (!keepProgress) {
this.setProgress((this.getProgress() / duration) * this.getDuration());
}
this.options.duration = duration;
return this;
}
getProgress() {
return Controller.getProgress(this, this.playhead);
}
setProgress(progress) {
this.setTotalElapsed(this.getIteration() * (this.getDuration() + this.getIterationDelay()) + (this.isAlternating() ? 1 - progress : progress) * this.getDuration());
return this;
}
getElapsed() {
return this.getProgress() * this.getDuration();
}
setElapsed(elapsed) {
this.setProgress(this.getDuration() == 0 ? elapsed : elapsed / this.getDuration());
return this;
}
getTotalProgress() {
return this.playhead;
}
setTotalProgress(totalProgress, events = true) {
totalProgress = animatry.clamp(totalProgress, 0, 1);
if (this.getReversed()) {
if (totalProgress != 0)
this.isPaused = false;
}
else {
if (totalProgress != 1)
this.isPaused = false;
}
if (events) {
if (this.getTotalProgress() == 0 && totalProgress > 0)
this.options.onStart(this);
if (this.getTotalProgress() < 1 && totalProgress == 1)
this.options.onComplete(this);
if (this.getTotalProgress() == 1 && totalProgress < 1)
this.options.onReverseStart(this);
if (this.getTotalProgress() > 0 && totalProgress == 0)
this.options.onReverseComplete(this);
if (this.getIteration() != Controller.getIteration(this, totalProgress))
this.options.onRepeat(this);
}
this.playhead = totalProgress;
if (this.isPlaying()) {
if (this.getReversed()) {
if (totalProgress == 0) {
if (!this.options.delayRecharge || this.getDelayProgress() == 0) {
this.pause();
}
}
}
else {
if (totalProgress == 1) {
this.pause();
}
}
}
}
getTotalElapsed() {
return this.getTotalProgress() * (this.getTotalDuration() || 1e-8);
}
setTotalElapsed(totalElapsed) {
this.setTotalProgress(totalElapsed / (this.getTotalDuration() || 1e-8));
}
getDelay() {
return this.options.delay;
}
setDelay(delay, restartDelay = undefined) {
if (restartDelay == undefined) {
restartDelay = this.delayProgress !== 1;
}
const delayBefore = this.options.delay;
this.options.delay = delay;
if (restartDelay) {
this.delayProgress = 0;
return this;
}
this.delayProgress = animatry.clamp((this.delayProgress / delay) * delayBefore, 0, delay);
return this;
}
getDelayProgress() {
if (this.getDelay() == 0)
return 1;
return this.delayProgress;
}
setDelayProgress(delayProgress) {
this.delayProgress = animatry.clamp(delayProgress, 0, 1);
return this;
}
getDelayElapsed() {
return this.getDelayProgress() * this.getDelay();
}
setDelayElapsed(delayElapsed) {
this.setDelayProgress(delayElapsed / this.getDelay());
return this;
}
getReversed() {
return this.isReversed;
}
setReversed(reversed) {
this.isReversed = reversed;
}
getRepeat() {
const { repeat, duration } = this.options;
if (this.getDuration() == 0)
return repeat == -1 ? 10 ** 8 : repeat;
return Math.floor(repeat == -1 ? 10 ** 8 / duration : Math.min(repeat, 10 ** 8 / duration));
}
setRepeat(repeat, keepIterations = true) {
const iteration = this.getIteration();
const progress = this.getProgress();
this.options.repeat = repeat;
this.setIteration(iteration);
this.setProgress(progress);
if (!keepIterations)
this.setIteration(0);
if (iteration > repeat)
this.setTotalProgress(1);
return this;
}
getAlternate() {
return this.options.alternate;
}
setAlternate(alternate, smoothJump = true) {
const wasAlternating = this.isAlternating();
if (this.getAlternate() != alternate) {
this.options.alternate = alternate;
if (smoothJump && wasAlternating !== this.isAlternating()) {
this.setProgress(1 - this.getProgress());
}
}
return this;
}
getIteration() {
if (this.getTotalDuration() == 0) {
return Math.round(this.getTotalProgress()) * this.getRepeat();
}
if (this.getTotalProgress() == 1) {
return this.getRepeat();
}
const duration = this.getDuration() + this.getIterationDelay();
const totalElapsed = this.getTotalDuration() * this.playhead;
return Math.floor(totalElapsed / duration);
}
setIteration(iteration, smoothJump = true) {
if (smoothJump && this.getAlternate() && this.getIteration() % 2 != iteration % 2) {
this.setProgress(1 - this.getProgress());
}
this.setTotalElapsed(iteration * (this.getDuration() + this.getIterationDelay()) + (this.isAlternating() ? this.getDuration() - this.getElapsed() : this.getElapsed()));
return this;
}
getIterationDelay() {
return this.options.iterationDelay;
}
setIterationDelay(iterationDelay) {
const progress = this.getProgress();
const iteration = this.getIteration();
this.options.iterationDelay = iterationDelay;
this.setIteration(iteration);
this.setProgress(progress);
return this;
}
getIterationDelayProgress() {
return this.iterationDelayProgress;
}
setIterationDelayProgress(iterationDelayProgress) {
this.iterationDelayProgress = animatry.clamp(iterationDelayProgress, 0, 1);
return this;
}
getTimeScale() {
return this.options.timeScale;
}
setTimeScale(timeScale) {
this.options.timeScale = timeScale;
}
getTotalDuration() {
return (this.getRepeat() + 1) * this.getDuration() + this.getRepeat() * this.getIterationDelay();
}
getEasedProgress() {
return Controller.getEasedProgress(this, this.playhead);
}
getEasedElapsed() {
return this.getEasedProgress() * this.getDuration();
}
isAlternating() {
return Controller.isAlternating(this, this.playhead);
}
isPlaying() {
return !this.isPaused;
}
onChange(callback) {
this.options.onChange = callback;
}
onUpdate(callback) {
this.options.onUpdate = callback;
}
onStart(callback) {
this.options.onStart = callback;
}
onRepeat(callback) {
this.options.onRepeat = callback;
}
onComplete(callback) {
this.options.onComplete = callback;
}
onReverseStart(callback) {
this.options.onReverseStart = callback;
}
onReverseComplete(callback) {
this.options.onReverseComplete = callback;
}
static getProgress(instance, ph) {
if (instance.getDuration() == 0) {
if (instance.getRepeat() > 0) {
const value = Math.ceil(animatry.clamp(ph * instance.getRepeat(), 0, 1));
return (Controller.isAlternating(instance, ph) ? 1 - value : value);
}
return Math.round(ph);
}
if (ph == 1 && instance.getIterationDelay() == 0) {
if (instance.getAlternate()) {
if (Controller.isAlternating(instance, ph)) {
return 0;
}
}
return 1;
}
const duration = instance.getDuration() + instance.getIterationDelay();
const totalElapsed = (instance.getTotalDuration() || 1e-8) * ph;
let elapsed = totalElapsed % duration;
let progress = animatry.clamp(elapsed / instance.getDuration(), 0, 1);
return (Controller.isAlternating(instance, ph) ? 1 - progress : progress);
}
static getEasedProgress(instance, ph) {
var _a;
const progress = Controller.getProgress(instance, ph);
const easeFunction = (Controller.isAlternating(instance, instance.options.backwards ? 1 - ph : ph) && instance.options.alternateEase !== undefined)
? instance.options.alternateEase
: ((_a = instance.options.ease) !== null && _a !== void 0 ? _a : Ease.powerInOut());
return instance.options.backwards ? 1 - easeFunction(progress) : easeFunction(progress);
}
static getIteration(instance, ph) {
if (ph == 0) {
return Math.round(ph) * instance.getRepeat();
}
if (ph == 1) {
return instance.getRepeat();
}
const duration = instance.getDuration() + instance.getIterationDelay();
const totalElapsed = (instance.getTotalDuration() || 1e-8) * ph;
return Math.floor(totalElapsed / duration);
}
static isAlternating(instance, ph) {
return instance.getAlternate() && Controller.getIteration(instance, ph) % 2 == 1;
}
}
const _timestamp = (previous, time, labels, object = undefined) => {
var _a, _b, _c, _d, _e;
let prevStart = (_a = previous === null || previous === void 0 ? void 0 : previous.getStartTime()) !== null && _a !== void 0 ? _a : 0;
let prevEnd = (_b = previous === null || previous === void 0 ? void 0 : previous.getEndTime()) !== null && _b !== void 0 ? _b : 0;
if (typeof time === 'number')
return time !== null && time !== void 0 ? time : prevEnd;
if (!time && prevEnd >= 10 ** 8) {
animatry.warn(`Placing a tween after someting infinite will not work.`);
}
if (!time)
return prevEnd;
const [, label, calc, number, unit] = time.match(/(^[A-Za-z][\w]*|^<|^>)?([+-]=)?([+-]?\d*\.?\d+)?(\w*|%)?$/) || [];
if ((unit && !number && !label) || (unit && !number)) {
animatry.warn(`invalid format '${time}'`);
return 0;
}
const hasLabel = /^[A-Za-z]/.test(time);
if (hasLabel && labels[label] == undefined)
label ? animatry.warn(`label '${label}' not defined`) : animatry.warn(`invalid format '${time}'`);
const start = hasLabel ? labels[label] : /^</.test(time) ? prevStart : prevEnd;
let additive = hasLabel || /^[<>]/.test(time) || unit === '%' || /[+-]=/.test(calc);
let nmbr = parseFloat(number) || 0;
if (calc === null || calc === void 0 ? void 0 : calc.startsWith('-'))
nmbr *= -1;
let unitMultiplier;
if (unit === '%') {
if (!object) {
unitMultiplier = (((_d = (_c = previous === null || previous === void 0 ? void 0 : previous.getAnimation()) === null || _c === void 0 ? void 0 : _c.getParent()) === null || _d === void 0 ? void 0 : _d.getDuration()) || 0) / 100;
}
else if (/[+-]=/.test(calc) || (hasLabel && !calc)) {
unitMultiplier = ((object === null || object === void 0 ? void 0 : object.getTotalDuration()) || 1e-8) / 100;
}
else {
unitMultiplier = (((_e = previous === null || previous === void 0 ? void 0 : previous.getAnimation()) === null || _e === void 0 ? void 0 : _e.getTotalDuration()) || 1e-8) / 100;
}
}
else if (unit === 'ms') {
unitMultiplier = 1 / 1000;
}
else {
unitMultiplier = 1;
}
const res = (additive ? start : 0) + nmbr * unitMultiplier;
return isNaN(res) ? prevEnd : res;
};
class Attached {
constructor(animation, time) {
this.animation = animation;
this.time = time;
}
getAnimation() {
return this.animation;
}
getStartTime() {
return this.time + this.animation.getDelay();
}
getEndTime() {
return this.getStartTime() + this.animation.getTotalDuration() / this.animation.getTimeScale() + (this.animation.getTotalDuration() == 0 ? 1e-8 : 0);
}
}
class Timeline extends Controller {
constructor(options = {}) {
super(Object.assign({
duration: 0,
ease: Ease.none()
}, options));
this.attacheds = [];
this.labels = {};
this.controller = options;
}
getController() {
return this.controller;
}
setTotalProgress(totalProgress, events = true) {
totalProgress = animatry.clamp(totalProgress, 0, 1);
const easedElapsed = Controller.getEasedProgress(this, totalProgress) * this.getDuration();
let reversed = totalProgress < this.getTotalProgress() !== this.isAlternating();
const attachedList = reversed ? this.attacheds.slice().reverse() : this.attacheds;
const shouldInitialize = this.options.preRender;
attachedList.forEach(attached => {
const animation = attached.getAnimation();
let elapsed = easedElapsed - attached.getStartTime();
const isConditionMet = reversed ? easedElapsed < attached.getEndTime() : easedElapsed > attached.getStartTime();
if (shouldInitialize || isConditionMet) {
animation.setInitialized();
}
if (isConditionMet) {
if (animation.getIteration() > 0) {
if (animation.isAlternating()) {
animation.setTotalProgress(1, elapsed >= attached.getEndTime());
}
}
animation.setTotalElapsed(elapsed);
}
});
super.setTotalProgress(totalProgress, events);
}
_updateDuration(crop = false) {
let maxTime = 0;
if (!crop) {
maxTime = this.getDuration();
}
this.attacheds.forEach(attached => {
if (attached.getEndTime() > maxTime) {
maxTime = attached.getEndTime();
}
});
this.setDuration(maxTime, false);
}
label(name, time = undefined) {
if (!/^[A-Za-z][\w-]*$/.test(name)) {
animatry.warn(`'${name}' includes invalid characters`);
return this;
}
this.labels[name] = _timestamp(this.attacheds[this.attacheds.length - 1], time, this.labels);
return this;
}
add(object, time = undefined) {
var _a, _b;
if (!(object instanceof Controller)) {
animatry.warn(`invalid object '${object}' did you intend to use .to() instead of .add() ?`);
object = animatry.to(object, time);
time = undefined;
}
let previous = this.attacheds[this.attacheds.length - 1];
const start = _timestamp(previous, (_b = (_a = time !== null && time !== void 0 ? time : object.options.at) !== null && _a !== void 0 ? _a : previous === null || previous === void 0 ? void 0 : previous.getEndTime()) !== null && _b !== void 0 ? _b : 0, this.labels, object);
if (object.options.duration >= 10 ** 8 && (object.options.repeat != 0 || this.options.repeat != 0)) {
object.setRepeat(0);
this.setRepeat(0);
}
else if (this.options.repeat != 0 && object.options.repeat == -1) {
this.setRepeat(0);
}
object.setParent(this);
const attached = new Attached(object, start);
this.attacheds.push(attached);
this._updateDuration();
if (!this.options.paused)
this.play();
return this;
}
play(seek = undefined) {
this.attacheds.forEach(attached => {
attached.getAnimation().play();
});
super.play(seek);
}
reverse() {
this.attacheds.forEach(attached => {
attached.getAnimation().reverse();
});
super.reverse();
}
seek(elapsed) {
if (typeof elapsed === 'string' && /^[A-Za-z]|^[<>]/.test(elapsed)) {
let previous = this.attacheds[this.attacheds.length - 1];
elapsed = _timestamp(previous, elapsed, this.labels);
}
super.seek(elapsed);
}
fromTo(el, from, to, time = undefined) {
return this.add(animatry.fromTo(el, from, to), time);
}
to(el, to, time = undefined) {
return this.add(animatry.to(el, to), time);
}
from(el, from, time = undefined) {
return this.add(animatry.from(el, from), time);
}
set(el, options, time = undefined) {
return this.add(animatry.set(el, options), time);
}
}
const keyframes = (tween, element, from, to) => {
const timeline = new Timeline();
const options = tween.options.keyframes;
const duration = tween.getDuration() || 1e-8;
const preRender = tween.options.preRender;
timeline.options.ease = options.ease ? Ease.parse(options.ease) : Ease.parse('none');
const fromValues = animatry.filterObjects(from, controllerSettings())[1];
if (options instanceof Array) {
const frameCount = Object.keys(options).length;
options.forEach((keyframe, index) => {
timeline.add(new Tween(element, index == 0 ? fromValues : {}, {
...keyframe,
at: index * duration / frameCount,
duration: duration / frameCount,
...(preRender && index === 0 && { preRender: true })
}));
});
}
else {
const keyframes = animatry.filterObjects(options, { ease: null, easeEach: null })[1];
const toValues = animatry.filterObjects(to, controllerSettings())[1];
if (Object.keys(keyframes)[0].includes('%')) {
const percentageKey = Object.keys(keyframes).sort((a, b) => parseFloat(a) - parseFloat(b));
let lastAppeared = {};
const keyframeProperties = Array.from(new Set(Object.values(keyframes).flatMap(Object.keys)));
const additionalKeyframes = {
'0%': { ...Object.fromEntries(Object.entries(fromValues).filter(([key]) => keyframeProperties.includes(key))) },
'100%': { ...Object.fromEntries(Object.entries(toValues).filter(([key]) => keyframeProperties.includes(key))) }
};
const combinedPercentages = [...new Set([
...(Object.keys(additionalKeyframes['0%']).length > 0 ? ['0%'] : []),
...percentageKey,
...(Object.keys(additionalKeyframes['100%']).length > 0 ? ['100%'] : [])
])];
const combinedKeyframes = combinedPercentages.reduce((acc, percentage) => ({
...acc,
[percentage]: {
...(additionalKeyframes[percentage] || {}),
...(keyframes[percentage] || {}),
},
}), {});
combinedPercentages.forEach(key => {
const filtered = animatry.filterObjects(combinedKeyframes[key], controllerOptions());
const controller = filtered[0];
const frameOptions = filtered[1];
let numberKey = 1 / 100 * Number.parseFloat(key);
Object.keys(frameOptions).forEach(property => {
var _a, _b, _c, _d, _e;
if (numberKey != 0) {
timeline.add(new Tween(element, lastAppeared[property] != null ? { [property]: combinedKeyframes[(lastAppeared[property] * 100) + '%'][property] } : {}, {
[property]: frameOptions[property],
duration: (numberKey - ((_a = lastAppeared[property]) !== null && _a !== void 0 ? _a : 0)) * duration,
...controller,
ease: (_d = (_c = (_b = controller['ease']) !== null && _b !== void 0 ? _b : options['easeEach']) !== null && _c !== void 0 ? _c : tween.options.ease) !== null && _d !== void 0 ? _d : Ease.powerInOut(),
...(preRender && !lastAppeared[property]) ? { preRender: true } : {},
}), ((_e = lastAppeared[property]) !== null && _e !== void 0 ? _e : 0) * duration);
}
lastAppeared[property] = numberKey;
});
timeline.setDuration(duration);
});
}
else {
const keys = Object.keys(keyframes);
keys.forEach(key => {
var _a;
const values = keyframes[key];
const valueCount = values.length;
const stepDuration = duration / Math.max(1, valueCount - 1);
for (let index = 0; index < (valueCount === 1 ? 1 : valueCount - 1); index++) {
timeline.add(new Tween(element, {
[key]: values[index]
}, {
[key]: valueCount === 1 ? values[0] : values[index + 1],
at: valueCount === 1 ? 0 : index * stepDuration,
duration: valueCount === 1 ? duration / valueCount : stepDuration,
ease: (_a = options['easeEach']) !== null && _a !== void 0 ? _a : tween.options.ease,
...(preRender && index === 0 ? { preRender: true } : {}),
}));
}
});
}
}
return timeline;
};
const regex = /^\s*([+-]=)?\s*([+-]?\d*\.?\d+)\s*([a-z%Q]+)?\s*$/;
const isSignableNumber = (s) => regex.test(s);
const combineSignedNumbers = (uc, prop, a, b) => {
[a, b] = unifySignedNumberUnits(uc, prop, a, b);
return [false, b[0] ? a[1] + b[1] : b[1], b[2]];
};
const toSignedNumber = (s) => {
if (s == undefined) {
animatry.warn('empty signed number');
return [false, 0, ''];
}
const match = s.toString().match(regex);
if (!match) {
animatry.warn('invalid signed number');
return [false, 0, ''];
}
const array = match === null || match === void 0 ? void 0 : match.slice(1);
return [!!array[0], parseFloat(array[1]) * (/^-/.test(array[0]) ? -1 : 1), array[2]];
};
const unifySignedNumberUnits = (uc, prop, from, to) => {
if (!from || !to)
return [[false, 0, ''], [false, 0, '']];
const [, fromN, fromU] = from;
const [toS, toN, toU] = to;
if (CSS.supports(prop, '1deg') || CSS.supports('transform', `${prop}(1deg)`)) {
return [
[false, (uc.getAngle(fromU !== null && fromU !== void 0 ? fromU : 'deg') / uc.getAngle(toU !== null && toU !== void 0 ? toU : 'deg')) * fromN, toU !== null && toU !== void 0 ? toU : 'deg'], [toS, toN, toU !== null && toU !== void 0 ? toU : 'deg']
];
}
else if (CSS.supports(prop, '1px') || CSS.supports('transform', `${prop}(1px)`) || /^(el[wh]|)$/.test(prop)) {
return [
[false, (uc.getLength(prop, fromU !== null && fromU !== void 0 ? fromU : 'px') / uc.getLength(prop, toU !== null && toU !== void 0 ? toU : 'px')) * fromN, toU !== null && toU !== void 0 ? toU : 'px'], [toS, toN, toU !== null && toU !== void 0 ? toU : 'px']
];
}
else if (CSS.supports(prop, '1') || CSS.supports('transform', `${prop}(1)`)) {
return [
[false, fromN, ''], to
];
}
animatry.warn(`unit conversion failed with invalid property '${prop}'`);
return [[false, 0, ''], to];
};
const convertSignedNumbers = (uc, prop, from, to, fallback) => {
var _a;
from = from !== null && from !== void 0 ? from : fallback;
to = (_a = to !== null && to !== void 0 ? to : from) !== null && _a !== void 0 ? _a : fallback;
return unifySignedNumberUnits(uc, prop, combineSignedNumbers(uc, prop, fallback, from), combineSignedNumbers(uc, prop, fallback, to));
};
const stringifySignedNumber = (sn) => {
return `${sn[1]}${sn[2]}`;
};
const validateColor = (c) => CSS.supports('color', c);
const colorToRgba = (color) => {
var _a, _b;
let res = [];
if (/^rgb(a)?\(/.test(color)) {
const match = color.match(/^(?:rgb(?:a)?\()(\d*\.?\d+)\s*,\s*(\d*\.?\d+)\s*,\s*(\d*\.?\d+)(?:\s*,\s*(\d*\.?\d+))?\)/);
if (!match) {
console.warn('invalid rgba format');
}
const [r, g, b, a = 1] = (match !== null && match !== void 0 ? match : [0, 0, 0, 0]).slice(1);
res = [r, g, b, a];
}
else if (/^hsl(a)?\(/.test(color)) {
const match = color.match(/^(?:hsl(?:a)?\()(\d*\.?\d+)(?:%)?\s*,\s*(\d*\.?\d+)(?:%)?\s*,\s*(\d*\.?\d+)(?:%)?(?:\s*,\s*(\d*\.?\d+)(?:%)?)?\)/);
let [h, s, l, a] = ((_a = match === null || match === void 0 ? void 0 : match.slice(1)) !== null && _a !== void 0 ? _a : [0, 0, 0, 0]).map(x => (x ? parseFloat(x) : 1));
s /= 100;
l /= 100;
const c = (1 - Math.abs(2 * l - 1)) * s;
const x = c * (1 - Math.abs((h / 60) % 2 - 1));
const m1 = l - c / 2;
const [r1, g1, b1] = h < 60 ? [c, x, 0] :
h < 120 ? [x, c, 0] :
h < 180 ? [0, c, x] :
h < 240 ? [0, x, c] :
h < 300 ? [x, 0, c] : [c, 0, x];
const [r, g, b] = [r1 + m1, g1 + m1, b1 + m1].map(val => Math.round(val * 255));
res = [r, g, b, a];
}
else if (/^#/.test(color)) {
const match = new RegExp(`^#(\\w(?:\\w)?)(\\w(?:\\w)?)(\\w(?:\\w)?)${color.length === 5 || color.length === 9 ? '(\\w(?:\\w)?)' : ''}$`).exec(color);
if (!match)
console.warn('invalid hex format');
const [r, g, b, a = 255] = ((_b = match === null || match === void 0 ? void 0 : match.slice(1)) !== null && _b !== void 0 ? _b : ['#fff'])
.map(x => (x.length === 1 ? x + x : x))
.map(x => parseInt(x, 16));
res = [r, g, b, a / 255];
}
else if (/^transparent/.test(color)) {
res = [0, 0, 0, 0];
}
else if (CSS.supports('color', color)) {
const canvas = document.createElement('canvas');
canvas.width = 1;
canvas.height = 1;
const ctx = canvas.getContext('2d', { willReadFrequently: true });
ctx.fillStyle = color;
ctx.fillRect(0, 0, 1, 1);
const d = ctx.getImageData(0, 0, 1, 1).data;
res = [...d].slice(0, 3).concat(d[3] / 255);
}
return ['rgba(', res[0], ',', res[1], ',', res[2], ',', res[3], ')'];
};
function unifyAllValues(uc, from, to) {
let res1 = [];
let res2 = [];
from.forEach((fromItem, index) => {
const toItem = to[index];
const comparisonResult = unifyValue(uc, '', fromItem, toItem, fromItem !== null && fromItem !== void 0 ? fromItem : toItem);
res1.push(...stringifySignedNumber(comparisonResult[0]));
res2.push(...stringifySignedNumber(comparisonResult[1]));
res1.push(' ');
res2.push(' ');
});
return [res1, res2];
}
const parseBorderOrOutline = (prop, b) => {
var _a;
const res = {};
b = b.replace('none', '#fff solid 0px');
((_a = b.match(/(?:[^\s()]+|\([^\)]*\))+/g)) !== null && _a !== void 0 ? _a : []).forEach(d => {
if (validateColor(d)) {
res[`${prop}Color`] = d;
}
else if (/^(none|hidden|dotted|dashed|solid|double|groove|ridge|inset|outset)$/.test(d)) {
res[`${prop}Style`] = d;
}
else if (isSignableNumber(d)) {
res[`${prop}Width`] = d;
}
});
return res;
};
const parseMarginOrPadding = (key, input) => {
const [top, right = top, bottom = top, left = right] = input.toString().trim().split(/\s+/);
return { [`${key}Top`]: top, [`${key}Right`]: right, [`${key}Bottom`]: bottom, [`${key}Left`]: left };
};
function parseBorderRadius(key, input) {
const [h, v = ""] = input.split("/").map(part => part.trim().split(/\s+/));
const expand = (vals) => vals.length === 1 ? Array(4).fill(vals[0]) :
vals.length === 2 ? [vals[0], vals[1], vals[0], vals[1]] :
vals.length === 3 ? [vals[0], vals[1], vals[2], vals[1]] : vals.slice(0, 4);
const horizontal = expand(h);
const vertical = expand(Array.isArray(v) && v.length ? v : horizontal);
return {
borderTopLeftRadius: `${horizontal[0]} ${vertical[0]}`,
borderTopRightRadius: `${horizontal[1]} ${vertical[1]}`,
borderBottomRightRadius: `${horizontal[2]} ${vertical[2]}`,
borderBottomLeftRadius: `${horizontal[3]} ${vertical[3]}`,
};
}
function disassambleBorderCornerRadius(value) {
let res = value.split(' ');
if (res.length == 1) {
res = [...res, ...res];
}
return res;
}
function parsePositionProperty(key, value) {
let d = value.split(' ');
let res = [];
let maxValues = key === 'backgroundPosition' ? 2 : 3;
d.forEach(o => {
if (/^(left|right)$/.test(o)) {
res[0] = o;
}
else if (/^(top|bottom)$/.test(o)) {
res[1] = o;
}
else if (/^(\d+(\.\d+)?(px|%)?)$/.test(o) || o === 'center') {
if (!res[0]) {
res[0] = o;
}
else if (!res[1]) {
res[1] = o;
}
else if (maxValues === 3 && !res[2]) {
res[2] = o;
}
}
});
[res[0], res[1], res[2]].forEach((o, i) => {
var _a;
if (i < maxValues && !o) {
res[i] = i === 2 ? '0px' : d[0] !== res[i === 0 ? 1 : 0] ? d[0] : (_a = d[1]) !== null && _a !== void 0 ? _a : '50%';
}
});
return res
.slice(0, maxValues)
.map(v => v
.replace(/left|top/, '0%')
.replace(/center/, '50%')
.replace(/right|bottom/, '100%'));
}
function parseAndUnifyShadows(property, shadowString1, shadowString2) {
function parseShadows(shadowString) {
shadowString = shadowString.replace('none', 'black');
const flatShadows = [];
shadowString.split(',').forEach(shadow => {
const parts = shadow.trim().split(/\s+/);
const color = parts.find(part => validateColor(part));
const [x = '0px', y = '0px', blur = '0px', spread = '0px'] = parts.filter(part => part !== color);
if (property === "box-shadow") {
flatShadows.push(x, y, blur, spread, color, ',');
}
else {
flatShadows.push(x, y, blur, color, ',');
}
});
if (flatShadows[flatShadows.length - 1] === ',') {
flatShadows.pop();
}
return flatShadows;
}
const shadowArray1 = parseShadows(shadowString1);
const shadowArray2 = parseShadows(shadowString2);
const maxLength = Math.max(shadowArray1.length, shadowArray2.length);
const defaultShadowBox = [',', "0px", "0px", "0px", "0px", "rgba(0,0,0,0)"];
const defaultShadowText = [',', "0px", "0px", "0px", "rgba(0,0,0,0)"];
while (shadowArray1.length < maxLength) {
shadowArray1.push(...(property === "box-shadow" ? defaultShadowBox : defaultShadowText));
}
while (shadowArray2.length < maxLength) {
shadowArray2.push(...(property === "box-shadow" ? defaultShadowBox : defaultShadowText));
}
return [[...shadowArray1], [...shadowArray2]];
}
function parseAndUnifyFilters(from, to) {
function filterToArray(filter) {
const regex = /(\w+-?\w*)\(([^()]*\([^()]*\)[^()]*)\)|(\w+-?\w*)\(([^()]*)\)/g;
const filters = [];
let match;
while ((match = regex.exec(filter)) !== null) {
filters.push([match[1] || match[3], match[2] || match[4]]);
}
return filters;
}
const defaultFilters = {
'blur': '0px',
'brightness': '100%',
'contrast': '100%',
'grayscale': '0',
'hue-rotate': '0deg',
'invert': '0',
'opacity': '100%',
'saturate': '100%',
'sepia': '0',
'drop-shadow': '0px 0px 0px black',
};
function unifyFilters(from, to) {
const resFrom = [];
const resTo = [];
const parsedFrom = filterToArray(from);
const parsedTo = filterToArray(to);
const filterTypesFrom = parsedFrom.map((filter) => filter[0]);
const filterTypesTo = parsedTo.map((filter) => filter[0]);
let i = 0;
while (i < parsedFrom.length && i < parsedTo.length && filterTypesFrom[i] === filterTypesTo[i]) {
resFrom.push(parsedFrom[i]);
resTo.push(parsedTo[i]);
i++;
}
if (i === parsedFrom.length) {
parsedTo.slice(i).forEach((object) => {
resFrom.push([object[0], defaultFilters[object[0]]]);
resTo.push(object);
});
}
else if (i === parsedTo.length) {
parsedFrom.slice(i).forEach((object) => {
resFrom.push(object);
resTo.push([object[0], defaultFilters[object[0]]]);
});
}
else {
parsedFrom.forEach((object) => {
resFrom.push(object);
resTo.push([object[0], defaultFilters[object[0]]]);
});
parsedTo.forEach((object) => {
resFrom.push([object[0], defaultFilters[object[0]]]);
resTo.push(object);
});
}
return [resFrom, resTo];
}
return unifyFilters(from, to);
}
function unifyValue(uc, propertyKey, from, to, fb) {
var _a, _b;
from = from !== null && from !== void 0 ? from : fb;
to = (_a = to !== null && to !== void 0 ? to : from) !== null && _a !== void 0 ? _a : fb;
propertyKey = animatry.camelToKebab(propertyKey);
if (/^border-\w+-\w+-radius$/.test(propertyKey)) {
const arrayFrom = disassambleBorderCornerRadius(from);
const arrayTo = disassambleBorderCornerRadius(to);
const arrayFallback = disassambleBorderCornerRadius(fb);
const a = unifyValue(uc, 'elw', arrayFrom[0], arrayTo[0], arrayFallback[0]);
const b = unifyValue(uc, 'elh', arrayFrom[1], arrayTo[1], arrayFallback[1]);
return [[...a[0], ' ', ...b[0]], [...a[1], ' ', ...b[1]]];
}
if (/^transform-origin$/.test(propertyKey)) {
const arrayFrom = parsePositionProperty(propertyKey, from);
const arrayTo = parsePositionProperty(propertyKey, to);
const arrayFallback = parsePositionProperty(propertyKey, fb);
const x = unifyValue(uc, 'elw', arrayFrom[0], arrayTo[0], arrayFallback[0]);
const y = unifyValue(uc, 'elh', arrayFrom[1], arrayTo[1], arrayFallback[1]);
const z = unifyValue(uc, '', arrayFrom[2], arrayTo[2], arrayFallback[2]);
return [[...x[0], ' ', ...y[0], ' ', ...z[0]], [...x[1], ' ', ...y[1], ' ', ...z[1]]];
}
if (/^background-position$/.test(propertyKey)) {
const arrayFrom = parsePositionProperty(propertyKey, from);
const arrayTo = parsePositionProperty(propertyKey, to);
const arrayFallback = parsePositionProperty(propertyKey, fb);
const x = unifyValue(uc, 'elw', arrayFrom[0], arrayTo[0], arrayFallback[0]);
const y = unifyValue(uc, 'elh', arrayFrom[1], arrayTo[1], arrayFallback[1]);
return [[...x[0], ' ', ...y[0]], [...x[1], ' ', ...y[1]]];
}
if (/^(box|text)-shadow$/.test(propertyKey)) {
const parsedShadows = parseAndUnifyShadows(propertyKey, from, to);
return unifyAllValues(uc, parsedShadows[0], parsedShadows[1]);
}
if (/^filter$/.test(propertyKey)) {
const res = parseAndUnifyFilters(from, to);
let parsedFilters = [[], []];
res[0].forEach((obj, index) => {
const start = obj[1];
const end = res[1][index][1];
const result = /^drop-shadow$/.test(obj[0]) ?
unifyAllValues(uc, parseAndUnifyShadows('text-shadow', start, end)[0], parseAndUnifyShadows('text-shadow', start, end)[1]) :
unifyValue(uc, /^invert$/.test(obj[0]) ? 'opacity' : /^hue-rotate$/.test(obj[0]) ? 'rotate' : '', start, end, start !== null && start !== void 0 ? start : end);
parsedFilters[0].push(`${obj[0]}(`, ...stringifySignedNumber(result[0]), ') ');
parsedFilters[1].push(`${obj[0]}(`, ...stringifySignedNumber(result[1]), ') ');
});
return parsedFilters;
}
if (/^opacity$/.test(propertyKey)) {
if (typeof from === 'string')
from = from.replace(/(\d+)%/g, (_, p1) => (p1 / 100).toString());
if (typeof to === 'string')
to = to.replace(/(\d+)%/g, (_, p1) => (p1 / 100).toString());
}
if (/^(letter|word)-spacing$/.test(propertyKey)) {
if (typeof from === 'string')
from = from.replace('normal', '0px');
if (typeof to === 'string')
to = to.replace('normal', '0px');
}
if (validateColor(from) || validateColor(to)) {
return [colorToRgba(from), colorToRgba(to)];
}
if (isSignableNumber((_b = from) !== null && _b !== void 0 ? _b : to)) {
return convertSignedNumbers(uc, propertyKey, toSignedNumber(from), toSignedNumber(to), toSignedNumber(fb));
}
return [[from], [to]];
}
function unifyProperties(uc, propertyKeys, from, to) {
const res = [{}, {}, {}];
propertyKeys.forEach(propertyKey => {
var _a, _b, _c;
if (/^autoHide$/.test(propertyKey)) {
if (propertyKeys.includes('visibility')) {
animatry.warn(`remove 'visibility' in order to use 'autoHide'`);
}
}
else if (!(propertyKey in document.documentElement.style)) {
animatry.warn(`Unknown property '${propertyKey}'. Missing plugin?`);
propertyKeys = propertyKeys.filter(k => k !== propertyKey);
return;
}
else if (!CSS.supports(animatry.camelToKebab(propertyKey), (_a = from[propertyKey]) !== null && _a !== void 0 ? _a : to[propertyKey]) && !CSS.supports(animatry.camelToKebab(propertyKey), ((_b = from[propertyKey]) !== null && _b !== void 0 ? _b : to[propertyKey]) + 'px')) {
animatry.warn(`Property ${propertyKey} has invalid value ${(_c = from[propertyKey]) !== null && _c !== void 0 ? _c : to[propertyKey]}`);
return;
}
[from, to].forEach(object => {
if (!object[propertyKey])
return;
const splitableFunctions = [
[/^(border(Top|Right|Bottom|Left)?|outline)$/, parseBorderOrOutline],
[/^(margin|padding)$/, parseMarginOrPadding],
[/^borderRadius$/, parseBorderRadius]
];
for (const [regex, parseFunction] of splitableFunctions) {
if (regex.test(propertyKey)) {
const newProperties = parseFunction(propertyKey, object[propertyKey]);
Object.assign(object, newProperties);
propertyKeys = propertyKeys
.filter(k => k !== propertyKey)
.concat(Object.keys(newProperties).filter(k => !propertyKeys.includes(k)));
delete object[propertyKey];
break;
}
}
});
});
propertyKeys.forEach(propertyKey => {
[res[0][propertyKey],] = unifyValue(uc, propertyKey, undefined, undefined, uc.css[propertyKey]);
[res[1][propertyKey], res[2][propertyKey]] = unifyValue(uc, propertyKey, from[propertyKey], to[propertyKey], uc.css[propertyKey]);
});
return res;
}
const RAD_TO_DEGREE = 180 / Math.PI;
const vectorLength = ([x, y, z]) => Math.sqrt(x * x + y * y + z * z);
const normalizeVector = ([x, y, z], length) => [x, y, z].map(s => s * 1 / length);
const crossProduct = ([x1, y1, z1], [x2, y2, z2]) => [y1 * z2 - z1 * y2, z1 * x2 - x1 * z2, x1 * y2 - y1 * x2];
const dotProduct = ([x1, y1, z1], [x2, y2, z2]) => x1 * x2 + y1 * y2 + z1 * z2;
const linearCombination = ([x1, y1, z1], [x2, y2, z2], s1, s2) => [x1 * s1 + x2 * s2, y1 * s1 + y2 * s2, z1 * s1 + z2 * s2];
function decomposeMatrix(matrix) {
const dM = new DOMMatrix(matrix);
const ma = [
[dM.m11, dM.m12, dM.m13],
[dM.m21, dM.m22, dM.m23],
[dM.m31, dM.m32, dM.m33],
];
const scale = new Array(3);
const skew = new Array(3);
scale[0] = vectorLength(ma[0]);
ma[0] = normalizeVector(ma[0], scale[0]);
skew[0] = dotProduct(ma[0], ma[1]);
ma[1] = linearCombination(ma[1], ma[0], 1.0, -skew[0]);
scale[1] = vectorLength(ma[1]);
ma[1] = normalizeVector(ma[1], scale[1]);
skew[0] /= scale[1];
skew[1] = dotProduct(ma[0], ma[2]);
ma[2] = linearCombination(ma[2], ma[0], 1.0, -skew[1]);
skew[2] = dotProduct(ma[1], ma[2]);
ma[2] = linearCombination(ma[2], ma[1], 1.0, -skew[2]);
skew[0] = Math.atan(skew[0]);
skew[1] = Math.atan(skew[1]);
skew[2] = Math.atan(skew[2]);
scale[2] = vectorLength(ma[2]);
ma[2] = normalizeVector(ma[2], scale[2]);
skew[1] /= scale[2];
skew[2] /= scale[2];
if (dotProduct(ma[0], crossProduct(ma[1], ma[2])) < 0) {
for (let i = 0; i < 3; i++) {
scale[i] *= -1;
ma[i] = ma[i].map(val => -val);
}
}
const quaternion = new Array(4);
quaternion[0] = 0.5 * Math.sqrt(Math.max(1 + ma[0][0] - ma[1][1] - ma[2][2], 0));
quaternion[1] = 0.5 * Math.sqrt(Math.max(1 - ma[0][0] + ma[1][1] - ma[2][2], 0));
quaternion[2] = 0.5 * Math.sqrt(Math.max(1 - ma[0][0] - ma[1][1] + ma[2][2], 0));
quaternion[3] = 0.5 * Math.sqrt(Math.max(1 + ma[0][0] + ma[1][1] + ma[2][2], 0));
if (ma[2][1] > ma[1][2])
quaternion[0] = -quaternion[0];
if (ma[0][2] > ma[2][0])
quaternion[1] = -quaternion[1];
if (ma[1][0] > ma[0][1])
quaternion[2] = -quaternion[2];
const [qx, qy, qz, qw] = quaternion;
const qw2 = qw * qw;
const qx2 = qx * qx;
const qy2 = qy * qy;
const qz2 = qz * qz;
const test = qx * qy + qz * qw;
const unit = qw2 + qx2 + qy2 + qz2;
const rotation = test > 0.49999 * unit
? [0, 2 * Math.atan2(qx, qw) * RAD_TO_DEGREE, 90]
: test < -0.49999 * unit
? [0, -2 * Math.atan2(qx, qw) * RAD_TO_DEGREE, -90]
: [
Math.round(Math.atan2(2 * qx * qw - 2 * qy * qz, 1 - 2 * qx2 - 2 * qz2) * RAD_TO_DEGREE),
Math.round(Math.atan2(2 * qy * qw - 2 * qx * qz, 1 - 2 * qy2 - 2 * qz2) * RAD_TO_DEGREE),
Math.round(Math.asin(2 * (qx * qy + qz * qw)) * RAD_TO_DEGREE)
];
return {
translateX: dM.m41,
translateY: dM.m42,
translateZ: dM.m43,
scaleX: Math.round(scale[0] * 1000) / 1000,
scaleY: Math.round(scale[1] * 1000) / 1000,
scaleZ: Math.round(scale[2] * 1000) / 1000,
rotateX: rotation[0],
rotateY: rotation[1],
rotateZ: rotation[2],
skewX: Math.round(skew[0] * RAD_TO_DEGREE * 1000) / 1000,
skewY: Math.round(skew[1] * RAD_TO_DEGREE * 1000) / 1000,
};
}
function matrixToAbsolute(uc, matrix) {
const propToPx = (prop, value) => {
const res = unifySignedNumberUnits(uc, prop, unifyMatrixEntry(prop, value), toSignedNumber('0px'))[0];
return `${res[1]}${res[2]}`;
};
const splitMatrix = (val) => { var _a; return (_a = (val !== null && val !== void 0 ? val : '').match(/(\w+\()([^()]*)(\))/g)) !== null && _a !== void 0 ? _a : []; };
const getParams = (val) => { var _a; return (_a = (val !== null && val !== void 0 ? val : '').match(/(\w+\()([^,]+)(?:,\s*([^,]+))?(?:,\s*([^,]+))?\)/)) !== null && _a !== void 0 ? _a : []; };
const handleMatrix = (obj, axes) => `${obj[0]}${axes.map((axis, index) => propToPx(axis, obj[index + 1])).join(', ')})`;
const step1 = splitMatrix(matrix);
const step2 = step1.map((each) => /^translate3d\(|^translate\(|^translate[XYZ]\(/.test(each)
? getParams(each).slice(1).filter((e) => e !== undefined)
: each);
return step2.flatMap((obj) => {
if (Array.isArray(obj)) {
if (/^translate3d/.test(obj[0])) {
return handleMatrix(obj, ['translateX', 'translateY', 'translateZ']);
}
else if (/^translate\(/.test(obj[0])) {
return handleMatrix(obj, ['translateX', 'translateY']);
}
else if (/(^translate[XYZ])\(/.test(obj[0])) {
return `${obj[0]}${propToPx(obj[0].slice(0, -1), obj[1])})`;
}
else {
return obj;
}
}
return [obj];
}).join(' ');
}
function unifyMatrixEntry(prop, s) {
let res = toSignedNumber(s);
if (res[2] == undefined)
res[2] = /^(skew|rotate)[XYZ]/.test(prop.toString()) ? 'deg' : /^scale/.test(prop.toString()) ? '' : 'px';
return res;
}
function unifyMatrix(matrix) {
const res = {};
Object.keys(matrix).forEach(t => {
res[t] = unifyMatrixEntry(t, matrix[t]);
});
return res;
}
function buildTransformString({ translateX, translateY, translateZ, scaleX, scaleY, scaleZ, rotateX, rotateY, rotateZ, skewX, skewY }) {
return [
(translateX || translateY || translateZ) && `translate3d(${translateX !== null && translateX !== void 0 ? translateX : 0}, ${translateY !== null && translateY !== void 0 ? translateY : 0}, ${translateZ !== null && translateZ !== void 0 ? translateZ : 0})`,
rotateX && rotateX != '0deg' && `rotateX(${rotateX})`,
rotateY && rotateY != '0deg' && `rotateY(${rotateY})`,
rotateZ && rotateZ != '0deg' && `rotate(${rotateZ})`,
((scaleX || scaleY) && (scaleX != '1' || scaleY != '1')) && `scale(${scaleX !== null && scaleX !== void 0 ? scaleX : 1}, ${scaleY !== null && scaleY !== void 0 ? scaleY : 1})`,
scaleZ && scaleZ != '1' && `scaleZ(${scaleZ})`,
skewX && skewX != '0deg' && `skewX(${skewX})`,
skewY && skewY != '0deg' && `skewY(${skewY})`
].filter(Boolean).join(' ');
}
const viewportUnits = { wiw: 0, wih: 0, vmin: 0, vmax: 0 };
function updateViewportUnits() {
viewportUnits.wiw = window.innerWidth / 100;
viewportUnits.wih = window.innerHeight / 100;
viewportUnits.vmin = Math.min(viewportUnits.wiw, viewportUnits.wih);
viewportUnits.vmax = Math.max(viewportUnits.wiw, viewportUnits.wih);
}
const angles = {
'deg': 1,
'rad': 180 / Math.PI,
'grad': 9 / 10,
'turn': 360
};
class UnitConverter {
constructor(el) {
this.el = el;
this.css = getComputedStyle(el);
this.dcss = getComputedStyle(document.documentElement);
}
getLength(prop, unit) {
var _a, _b;
if (viewportUnits.wiw === 0) {
updateViewportUnits();
window.addEventListener('resize', updateViewportUnits);
}
let prc = 1;
switch (true) {
case /^(elw|\w+[xX])$/.test(prop):
prc = this.el.offsetWidth;
break;
case /^(elh|\w+[yY])$/.test(prop):
prc = this.el.offsetHeight;
break;
case /^((max|min|column)-)?width|(margin-|padding-)?(top|right|bottom|left)$/.test(prop):
this.par = (_a = this.par) !== null && _a !== void 0 ? _a : animatry.getParent(this.el);
prc = this.par.offsetWidth;
break;
case /^((max|min|column)-height|top|bottom)$/.test(prop):
this.par = (_b = this.par) !== null && _b !== void 0 ? _b : animatry.getParent(this.el);
prc = this.par.offsetHeight;
break;
case /^font-size$/.test(prop):
prc = parseFloat(this.dcss.fontSize);
break;
case /^line-height$/.test(prop):
prc = parseFloat(this.css.fontSize);
break;
}
prc /= 100;
switch (unit) {
case 'px': return 1;
case '%': return prc;
case 'rem': return parseFloat(this.dcss.fontSize);
case 'em': return parseFloat(this.css.fontSize);
case 'cm': return 37.8;
case 'mm': return 3.78;
case 'Q': return 0.945;
case 'in': return 96;
case 'pc': return 16;
case 'pt': return 1.333;
case 'ex': return parseFloat(this.css.fontSize) * 0.5;
case 'ch': return parseFloat(this.css.fontSize) * 0.6;
case 'vmin': return viewportUnits.vmin;
case 'vmax': return viewportUnits.vmax;
case 'vb': return /^(ltr|rtl)$/.test(this.css.direction) ? viewportUnits.wih : viewportUnits.wiw;
case 'vw':
case 'svw':
case 'lvw':
case 'dvw': return viewportUnits.wiw;
case 'vh':
case 'svh':
case 'lvh':
case 'dvh': return viewportUnits.wih;
default: return 0;
}
}
getAngle(unit) {
return angles[unit] || 0;
}
}
function lerpSingle(s, e, p) {
return /^[+-]?\d*\.?\d+$/.test(s) ? animatry.round((1 - p) * s + p * e, 4) : p > 0 ? e : s;
}
function lerpMulti(s, e, p) {
return s.map((v, i) => lerpSingle(v, e[i], p));
}
function unifyTransformations(uc, keys, from, to, css) {
const trans = [{}, {}, {}];
if (keys.length === 0)
return trans;
let matrixBefore = unifyMatrix(decomposeMatrix(css.transform));
const keyMap = {
x: 'translateX',
y: 'translateY',
z: 'translateZ',
rotate: 'rotateZ',
};
const handleScaleOrTranslate = (mappedKey, values, i) => {
const props = /^scale$/.test(mappedKey) ? ['scaleX', 'scaleY'] : ['translateX', 'translateY', 'translateZ'];
const parsedValues = mappedKey === 'scale' && values.length === 1 ? [values[0], values[0]] : values;
parsedValues.forEach((value, index) => {
var _a;
trans[i][props[index]] = combineSignedNumbers(uc, props[index], i === 0 ? matrixBefore[props[index]] : (_a = trans[1][props[index]]) !== null && _a !== void 0 ? _a : matrixBefore[props[index]], toSignedNumber(value));
});
};
[from, to].forEach((obj, i) => {
const transformBase = obj['transform'] ? unifyMatrix(decomposeMatrix(matrixToAbsolute(uc, obj['transform']))) : undefined;
if (transformBase || i === 1) {
trans[i + 1] = transformBase !== null && transformBase !== void 0 ? transformBase : structuredClone(trans[1]);
}
delete obj['transform'];
keys.forEach(key => {
var _a, _b;
if (obj[key] == null)
return;
let mappedKey = (_a = keyMap[key]) !== null && _a !== void 0 ? _a : key;
if (/^scale$|^translate$|^translate3d$/.test(mappedKey)) {
handleScaleOrTranslate(mappedKey, Array.isArray(obj[key]) ? obj[key] : obj[key].toString().split(' '), i + 1);
return;
}
if (!Array.isArray(obj[key]) && typeof obj[key] === 'number') {
const unit = /^(skew|rotate)[XYZ]/.test(mappedKey) ? 'deg' : /^scale/.test(mappedKey) ? '' : 'px';
obj[key] = `${obj[key]}${unit}`;
}
trans[i + 1][mappedKey] = combineSignedNumbers(uc, mappedKey, i == 0 ? matrixBefore[mappedKey] : (_b = trans[1][mappedKey]) !== null && _b !== void 0 ? _b : matrixBefore[mappedKey], toSignedNumber(obj[key]));
});
});
[...new Set([...Object.keys(trans[1]), ...Object.keys(trans[2])])].forEach((key) => {
[trans[0][key]] = convertSignedNumbers(uc, key, undefined, trans[1][key], matrixBefore[key]);
[trans[1][key], trans[2][key]] = convertSignedNumbers(uc, key, trans[1][key], trans[2][key], matrixBefore[key]);
});
return trans;
}
function readProperties(el, from, to) {
const css = getComputedStyle(el);
const uc = new UnitConverter(el);
const [tkeys, ckeys] = Array.from(new Set([...Object.keys(from), ...Object.keys(to)])).reduce((acc, key) => {
acc[/^([xyz]|transform|(translate([XYZ]|3d)?)|(rotat(e|ion))?[XYZ]?|scale[XYZ]?|skew[XYZ])$/i.test(key) ? 0 : 1].push(key);
return acc;
}, [[], []]);
const resTrans = unifyTransformations(uc, tkeys, from, to, css);
const resCss = unifyProperties(uc, ckeys, from, to);
[resTrans[0], resCss[0], resTrans[1], resCss[1], resTrans[2], resCss[2]].forEach(o => {
Object.keys(o).forEach(key => {
o[key] = o[key].filter((item) => typeof item !== 'boolean');
});
});
return [
[resTrans[0], resCss[0]],
[resTrans[1], resCss[1]],
[resTrans[2], resCss[2]]
];
}
function lerpProperties([state, from, to], p, options) {
const tfm = {};
const css = {};
if (animatry.round(p, 4) === (options.backwards ? 1 : 0) && !options.preRender) {
Object.keys(from[0]).forEach(key => {
tfm[key] = state[0][key].join('');
});
}
else {
Object.keys(from[0]).forEach(key => {
tfm[key] = lerpMulti(from[0][key], to[0][key], p).join('');
});
}
Object.keys(from[1]).forEach(key => {
const res = lerpMulti(from[1][key], to[1][key], p).join('');
if (!CSS.supports(animatry.camelToKebab(key), res) && p < 0)
return;
css[key] = res;
});
return [tfm, css];
}
function applyProperties(el, props) {
Object.keys(props[1]).forEach(k => {
if (k === 'autoHide') {
el.style.visibility = props[1]['opacity'] == 0 ? 'hidden' : 'visible';
}
else {
el.style[k] = props[1][k];
}
});
el.style.transform = buildTransformString(props[0]);
}
class Fragment {
constructor(element) {
this.element = element;
this.initialTransform = unifyMatrix(decomposeMatrix(getComputedStyle(element).transform));
Object.keys(this.initialTransform).forEach(key => {
this.initialTransform[key] = stringifySignedNumber(this.initialTransform[key]);
});
this.properties = [];
}
getProperties() {
return this.properties;
}
setProperties(properties) {
this.properties = [
{ ...this.initialTransform, ...this.properties[0], ...properties[0] },
{ ...this.properties[1], ...properties[1] }
];
}
getElement() {
return this.element;
}
}
class Renderer {
static addTween(element, tween) {
let fragment = Renderer.fragments.find((fragment) => fragment.getElement() === element);
if (!fragment) {
fragment = new Fragment(element);
Renderer.fragments.push(fragment);
}
if (!this.tweens.find((found) => found === tween)) {
this.tweens.push(tween);
}
return fragment;
}
static getTweens(elements) {
let found = [];
elements = animatry.select(elements);
elements.forEach(element => {
this.tweens.forEach(tween => {
if (tween.elements[0] == element) {
found.push(tween);
}
});
});
return found;
}
}
Renderer.fragments = [];
Renderer.tweens = [];
const _elementCenter = (element) => {
const rect = element.getBoundingClientRect();
return [rect.left + rect.width / 2, rect.top + rect.height / 2];
};
const _measureGrid = (elements) => {
return [new Set(elements.map(element => _elementCenter(element)[0])).size, new Set(elements.map(element => _elementCenter(element)[1])).size];
};
const _gridIndex = ([rows, columns], index) => {
const row = Math.floor(index / columns);
const column = index % columns;
const x = columns > 1 ? column / (columns - 1) : 0.5;
const y = rows > 1 ? row / (rows - 1) : 0.5;
return [x, y];
};
const _normalizeArray = (array) => {
const min = Math.min(...array);
const max = Math.max(...array);
return array.map(value => (value - min) / (max - min));
};
const _allPositionsEqual = (arr) => {
return arr.every(([first, second]) => first === arr[0][0] && second === arr[0][1]);
};
const _distributeArray = (elements, options) => {
if (options.each == 0)
return Array.from({ length: elements.length }, () => 0);
if ((Array.isArray(options.from) || options.axis || options.from == 'center' || options.from == 'right top' || options.from == 'left bottom') && options.layout == undefined) {
options.layout = 'distance';
}
switch (options.from) {
case 'start':
case 'left':
case 'top left':
case 'left top':
case undefined:
options.from = options.layout ? [0, 0] : 0;
break;
case 'center':
options.from = options.layout ? [0.5, 0.5] : (elements.length - 1) / 2;
break;
case 'end':
case 'right':
case 'bottom right':
case 'right bottom':
case -1:
options.from = options.layout ? [1, 1] : elements.length - 1;
break;
case 'top right':
case 'right top':
options.from = [1, 0];
break;
case 'bottom left':
case 'left bottom':
options.from = [0, 1];
break;
case 'random':
if (options.invert)
animatry.warn(`stagger invert has no effect when using random`);
if (options.axis)
animatry.warn(`stagger axis has no effect when using random`);
return _normalizeArray(Array.from({ length: elements.length }, () => Math.random())).map(value => (value * options.each * (elements.length - 1)));
}
let result = [];
if (options.layout) {
let elementCenters = [];
let { layout, axis = 'xy', from = [0.5, 0.5] } = options;
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
if (Array.isArray(layout) || layout === 'grid') {
layout = layout === 'grid' ? _measureGrid(elements) : layout;
const fromCoords = Array.isArray(from) ? from : _gridIndex(layout, from);
from = fromCoords;
[minX, maxX, minY, maxY] = [0, 1, 0, 1];
elementCenters = elements.map((_, i) => _gridIndex(layout, i));
}
else {
elementCenters = elements.map(_elementCenter);
elementCenters.forEach(([x, y]) => {
minX = Math.min(minX, x);
minY = Math.min(minY, y);
maxX = Math.max(maxX, x);
maxY = Math.max(maxY, y);
});
if (!Array.isArray(from)) {
const [x, y] = elementCenters[from];
options.from = [(x - minX) / (maxX - minX), (y - minY) / (maxY - minY)];
}
}
if (_allPositionsEqual(elementCenters)) {
animatry.warn(`Elements on the same location. Can't apply any stagger layouts.`);
result = _normalizeArray(elements.map((_, i) => i)).map(value => (value * options.each * (elements.length - 1)));
return result;
}
const [rangeX, rangeY] = [maxX - minX, maxY - minY];
const [startX, startY] = [minX + rangeX * options.from[0], minY + rangeY * options.from[1]];
const [dirX, dirY] = [axis.includes('x') ? 1 : 0, axis.includes('y') ? 1 : 0];
const computeTiming = (x, y) => {
const [distanceX, distanceY] = [x - startX, y - startY];
if (dirX && dirY)
return Math.sqrt(distanceX ** 2 + distanceY ** 2) / Math.sqrt(rangeX ** 2 + rangeY ** 2);
if (dirX)
return rangeX === 0 ? 0 : distanceX / rangeX;
if (dirY)
return rangeY === 0 ? 0 : distanceY / rangeY;
return 0;
};
const maxDistance = Math.max(...elementCenters.map(([x, y]) => computeTiming(x, y)));
result = elementCenters.map(([x, y]) => Math.abs(computeTiming(x, y) / (maxDistance || 1)));
}
else {
for (let i = 0; i < elements.length; i++) {
result.push(Math.abs(i - options.from));
}
}
result = _normalizeArray(result);
if (options.invert)
result = result.map(element => Math.abs(element - 1));
result = result.map(value => value * options.each * (elements.length - 1));
return result;
};
const stagger = (tween, elements, from, to) => {
var _a, _b;
elements = animatry.select(elements);
let options = tween.options.stagger;
if (typeof options === 'number')
options = { each: options };
options.each = (_a = options.each) !== null && _a !== void 0 ? _a : (options.duration ? (options.duration / (elements.length - 1)) : 0);
Object.keys(controllerFunctions()).forEach(func => {
to[func] = options[func] ? options[func] : () => { };
});
const timeline = new Timeline({ ease: (_b = options.ease) !== null && _b !== void 0 ? _b : Ease.none() });
const staggerArray = _distributeArray(elements, options);
elements.forEach((element, index) => {
var _a, _b, _c;
timeline.add(new Tween(element, from, Object.assign({}, to, {
at: Number.parseFloat(staggerArray[index].toFixed(4)),
duration: tween.options.duration,
ease: tween.options.ease,
alternateEase: tween.options.alternateEase,
repeat: (_a = tween.options.stagger.repeat) !== null && _a !== void 0 ? _a : 0,
iterationDelay: (_b = tween.options.stagger.iterationDelay) !== null && _b !== void 0 ? _b : 0,
alternate: (_c = tween.options.stagger.alternate) !== null && _c !== void 0 ? _c : false,
preRender: tween.options.preRender,
delay: 0,
timeScale: 1,
stagger: 0,
})));
});
tween.setDuration(timeline.getDuration());
tween.to = {};
return timeline;
};
class Tween extends Controller {
constructor(elements, from, to) {
let filterFrom = animatry.filterObjects(from, controllerSettings());
let filterTo = animatry.filterObjects(to, controllerSettings());
let options = { ...filterFrom[0], ...filterTo[0] };
super(options);
this.elements = elements ? animatry.select(elements) : [];
this.from = filterFrom[1];
this.to = filterTo[1];
if (this.elements.length == 0) {
animatry.warn(`Element '${elements}' not found`);
return;
}
if (this.elements.length > 1) {
this.timeline = stagger(this, this.elements, from, to);
this.elements = [];
}
else if (this.options.keyframes) {
this.timeline = keyframes(this, this.elements[0], from, to);
}
if (this.elements[0])
this.fragment = Renderer.addTween(this.elements[0], this);
if (this.options.preRender) {
if (this.elements[0])
this.render();
this.setTotalProgress(0);
}
if (this.timeline)
this.timeline.setParent(this);
}
render() {
this.properties = readProperties(this.elements[0], this.from, this.to);
}
setTotalProgress(totalProgress, events = true) {
totalProgress = animatry.round(animatry.clamp(totalProgress, 0, 1), 10);
super.setTotalProgress(totalProgress, events);
if (this.fragment) {
if (!this.properties && totalProgress >= 0 && totalProgress <= 1) {
this.render();
}
if (this.properties) {
this.fragment.setProperties(lerpProperties(this.properties, Controller.getEasedProgress(this, totalProgress), this.options));
applyProperties(this.elements[0], this.fragment.getProperties());
}
}
if (this.timeline) {
this.timeline.setTotalProgress(Controller.getProgress(this, totalProgress));
}
}
play() {
if (this.timeline)
this.timeline.play();
super.play();
}
reverse() {
if (this.timeline)
this.timeline.reverse();
super.reverse();
}
}
class Animatry {
constructor() {
this.pipeline = (...funcs) => {
return function (...args) {
let result = args;
for (const func of funcs) {
result = func(...result);
}
return result;
};
};
this.filterObjects = (object, filter) => {
const matched = {};
const unmatched = {};
Object.keys(object).forEach((key) => {
if (key in filter) {
matched[key] = object[key];
}
else {
unmatched[key] = object[key];
}
});
return [matched, unmatched];
};
this.random = (...args) => {
const isFunction = args[args.length - 1] === true;
const [min, max, step = 1] = args;
if (Array.isArray(min)) {
return () => min[Math.floor(Math.random() * min.length)];
}
return () => {
const range = max - min;
const randomValue = step === 0
? Math.random() * range + min
: Math.floor(Math.random() * (range / step)) * step + min;
return isFunction ? parseFloat(randomValue).toFixed(4) : randomValue;
};
};
this.shuffle = (array) => {
array = [...array];
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
return array;
};
this.clamp = (number, min = 0, max = 1) => {
return Math.min(max, Math.max(min, number));
};
this.round = (value, decimalPlaces = 0) => {
return Number(value.toFixed(decimalPlaces));
};
this.lerp = (start, end, progress) => {
return this.round((1 - progress) * start + progress * end, 4);
};
this.isEven = (n) => {
return n % 2;
};
this.isOdd = (n) => {
return !this.isEven(n);
};
this.pageReady = (callback) => {
if (document.readyState === 'complete') {
callback();
return;
}
window.addEventListener('load', () => {
document.fonts.ready.then(() => {
callback();
});
});
};
this.select = (input) => {
if (typeof input === 'string') {
const match = input.match(/\w+\[(\d+)\]/);
if (match) {
const index = parseInt(match[1], 10);
const elements = document.querySelectorAll(input.replace(`[${match[1]}]`, ''));
return [elements[index]];
}
return Array.from(document.querySelectorAll(input));
}
else if (input instanceof Element) {
return [input];
}
else if (input instanceof NodeList || input instanceof Array) {
return Array.from(input).flatMap(entry => this.select(entry));
}
else if (input instanceof Document) {
return [input.documentElement];
}
animatry.warn('Could not collect these elements:' + input);
return [];
};
this.selectCss = (property, value, parent, negate = false) => {
const elements = this.select(parent)[0].querySelectorAll('*');
const targets = Array.from(elements).filter((element) => {
const computed = window.getComputedStyle(element).getPropertyValue(property).trim();
if (negate) {
return computed !== value.trim();
}
else {
return computed === value.trim();
}
});
return targets;
};
this.getParent = (el) => {
const position = getComputedStyle(el).position;
if (position === 'absolute' || position === 'fixed') {
while (el && getComputedStyle(el).position !== 'relative' && el !== document.body) {
el = el.parentElement || document.body;
}
return el;
}
return el.parentElement || document.body;
};
this.camelToKebab = (s) => {
return s.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
};
this._p = 'background-color:rgba(0,0,0,0.5);padding:3px 7.5px;';
}
version() {
animatry.log(`v${'0.0.1'}`);
}
options(options) {
setGlobalOptions(options);
}
fromTo(elements, from = {}, to = {}) {
return new Tween(elements, Object.assign({ preRender: true }, from), to);
}
to(elements, to = {}) {
return new Tween(elements, {}, to);
}
from(elements, from = {}) {
return new Tween(elements, {}, Object.assign({ preRender: true }, from, { backwards: !from.backwards }));
}
timeline(options) {
return new Timeline(options);
}
set(elements, to = {}) {
return new Tween(elements, {}, Object.assign(to, { duration: 0 }));
}
use(plugin) {
if (typeof plugin !== 'function') {
animatry.warn(`A plugin has to be a function.`);
}
else {
plugin(this);
}
}
log(m) { console.log(`%cAnimatry%c ${m}`, this._p, ''); }
warn(m) { console.warn(`%cAnimatry%c ${m}`, this._p, ''); }
}
const animatry = new Animatry();
const select = (input) => {
if (typeof input === 'string') {
const match = input.match(/\w+\[(\d+)\]/);
if (match) {
const index = parseInt(match[1], 10);
const elements = document.querySelectorAll(input.replace(`[${match[1]}]`, ''));
return [elements[index]];
}
return Array.from(document.querySelectorAll(input));
}
else if (input instanceof Element) {
return [input];
}
else if (input instanceof NodeList || input instanceof Array) {
return Array.from(input).flatMap(entry => select(entry));
}
else if (input instanceof Document) {
return [input.documentElement];
}
warn('Could not collect these elements:' + input);
return [];
};
const _p = 'background-color:rgba(0,0,0,0.5);padding:3px 7.5px;';
const warn = (m) => { console.warn(`%cAnimatry%c ${m}`, _p, ''); };
function splitText(elements, options) {
elements = select(elements);
const splitChars = /char/.test(options);
const splitWords = /word/.test(options);
const splitLines = /line/.test(options);
const lines = [];
const words = [];
const chars = [];
elements.forEach((element) => {
element.style.minWidth = element.clientWidth + 'px';
const clone = element.cloneNode(true);
const processNode = (node) => {
var _a;
if (node.nodeType === Node.TEXT_NODE) {
const textContent = (_a = node.textContent) !== null && _a !== void 0 ? _a : '';
if (textContent.trim() === '')
return;
const wordsArray = textContent.split(/(\s+)/);
const container = document.createDocumentFragment();
wordsArray.forEach((wordString) => {
if (wordString.trim() !== '') {
const word = document.createElement('span');
word.classList.add('word');
word.style.display = 'inline-block';
if (splitChars) {
wordString.split('').forEach((charString) => {
const char = document.createElement('span');
char.classList.add('char');
char.style.display = 'inline-block';
char.textContent = charString;
word.appendChild(char);
chars.push(char);
});
}
else {
word.textContent = wordString;
}
container.appendChild(word);
words.push(word);
}
else {
container.appendChild(document.createTextNode(' '));
}
});
node.replaceWith(container);
}
else if (node.nodeType === Node.ELEMENT_NODE) {
Array.from(node.childNodes).forEach(processNode);
}
};
if (splitWords || splitChars || splitLines) {
processNode(clone);
}
const processLines = () => {
const nodes = Array.from(clone.childNodes);
element.innerHTML = '';
const lineFragments = [];
let currentHeight = 0;
function createLine(node) {
const lineFragment = document.createElement('div');
lineFragment.classList.add('line');
lineFragment.appendChild(node);
element.appendChild(lineFragment);
currentHeight = lineFragment.clientHeight;
lineFragments.push(lineFragment);
}
if (nodes.length > 0) {
createLine(nodes.shift());
}
while (nodes.length > 0) {
const lastLine = lineFragments[lineFragments.length - 1];
lastLine.appendChild(nodes.shift());
if (lastLine.clientHeight > currentHeight) {
createLine(lastLine.lastChild);
}
}
return lineFragments;
};
if (splitLines) {
lines.push(...processLines());
}
else {
element.replaceWith(clone);
}
});
return {
lines,
words,
chars,
};
}
export { animatry, splitText };