just-animate
Version:
_Making Animation Simple_
166 lines (165 loc) • 6.1 kB
JavaScript
import { replaceWithRefs } from '../core/references';
import { _, CONFIG } from '../utils/constants';
import { all, find, push, list, pushDistinct, sortBy } from '../utils/lists';
import { flr, max } from '../utils/math';
import { resolveProperty } from '../utils/resolve-property';
import { isObject, isNumber, isArrayLike, isOwner, isDefined } from '../utils/inspect';
import { plugins } from '../core/plugins';
import { calculateConfigs } from './calc-configs';
const propKeyframeSort = sortBy('time');
export const insert = (model, options, ctx) => {
all(options, opts => {
if (opts.to === _) {
throw new Error('missing duration');
}
opts = replaceWithRefs(model.refs, opts, true);
all(opts.targets, (target, i, ilen) => {
const config = addPropertyKeyframes(model, target, i, ilen, opts);
ctx.dirty(config);
});
});
calculateConfigs(model);
ctx.trigger(CONFIG);
};
function addPropertyKeyframes(model, target, index, ilen, opts) {
const defaultEasing = 'ease';
const delay = resolveProperty(opts.delay, target, index, ilen) || 0;
const config = find(model.configs, c => c.target === target) ||
push(model.configs, {
from: max(opts.from + delay, 0),
to: max(opts.to + delay, 0),
easing: opts.easing || defaultEasing,
duration: opts.to - opts.from,
endDelay: resolveProperty(opts.endDelay, target, index, ilen) || 0,
stagger: opts.stagger || 0,
target,
targetLength: ilen,
propNames: [],
keyframes: []
});
const staggerMs = (opts.stagger && opts.stagger * (index + 1)) || 0;
const delayMs = resolveProperty(opts.delay, config, index, config.targetLength) || 0;
const from = max(staggerMs + delayMs + opts.from, 0);
const duration = opts.to - opts.from;
const easing = opts.easing || defaultEasing;
for (var pluginName in plugins) {
if (isOwner(opts, pluginName)) {
const props = opts[pluginName];
for (var name in props) {
var propVal = props[name];
if (isOwner(props, name) && isDefined(propVal)) {
addProperty(config, pluginName, index, name, propVal, duration, from, easing);
}
}
}
}
config.keyframes.sort(propKeyframeSort);
return config;
}
function addProperty(config, plugin, index, name, val, duration, from, defaultEasing) {
let defaultInterpolator;
let values;
const isValueObject = !isArrayLike(val) && isObject(val);
if (isValueObject) {
const objVal = val;
if (objVal.easing) {
defaultEasing = objVal.easing;
}
if (objVal.interpolate) {
defaultInterpolator = objVal.interpolate;
}
values = list(objVal.value);
}
else {
values = list(val);
}
const keyframes = values.map((v, i, vals) => {
const valOrObj = resolveProperty(v, config.target, index, config.targetLength);
const valObj = valOrObj;
const isObj2 = isObject(valOrObj);
const value = isObj2 ? valObj.value : valOrObj;
const offset = isObj2 && isNumber(valObj.offset)
?
valObj.offset
: i === vals.length - 1
?
1
: i === 0
?
0
: _;
const interpolate = (valObj && valObj.interpolate) || defaultInterpolator;
const easing = (valObj && valObj.easing) || defaultEasing;
return { offset, value, easing, interpolate };
});
inferOffsets(keyframes);
all(keyframes, keyframe => {
const { offset, value } = keyframe;
const time = flr(duration * offset + from);
const frame = find(config.keyframes, k => k.prop === name && k.time === time) ||
push(config.keyframes, {
plugin,
easing: keyframe.easing,
index,
prop: name,
time,
value,
interpolate: keyframe.interpolate
});
frame.value = value;
});
find(config.keyframes, k => k.prop === name && k.time === from) ||
push(config.keyframes, {
plugin,
easing: defaultEasing,
index,
prop: name,
time: from,
value: _,
interpolate: defaultInterpolator
});
var to = from + duration;
find(config.keyframes, k => k.prop === name && k.time === to, true) ||
push(config.keyframes, {
plugin,
easing: defaultEasing,
index,
prop: name,
time: to,
value: _,
interpolate: defaultInterpolator
});
pushDistinct(config.propNames, name);
}
function inferOffsets(keyframes) {
if (!keyframes.length) {
return;
}
const first = find(keyframes, k => k.offset === 0) || keyframes[0];
if (!isDefined(first.offset)) {
first.offset = 0;
}
const last = find(keyframes, k => k.offset === 1, true) ||
keyframes[keyframes.length - 1];
if (keyframes.length > 1 && !isDefined(last.offset)) {
last.offset = 1;
}
for (let i = 1, ilen = keyframes.length; i < ilen; i++) {
const target = keyframes[i];
if (!isDefined(target.offset)) {
for (let j = i + 1; j < ilen; j++) {
const endTime = keyframes[j].offset;
if (isDefined(endTime)) {
const startTime = keyframes[i - 1].offset;
const timeDelta = endTime - startTime;
const deltaLength = j - i + 1;
for (let k = 1; k < deltaLength; k++) {
keyframes[k - 1 + i].offset = (k / j) * timeDelta + startTime;
}
i = j;
break;
}
}
}
}
}