metamorphosis
Version:
A css variable management library that helps create and organize variables into easily configurable themes.
131 lines (130 loc) • 3.99 kB
JavaScript
import { isBlank } from "@alanscodelog/utils/isBlank";
import { Base } from "./Base.js";
import { getTotalSteps } from "./internal.js";
import { lerp, defaultKeyNamer } from "./utils.js";
const getStepPercent = (percent, startPercent, endPercent) => {
let stopPercent = percent - startPercent;
let stopPercentTotal = endPercent - startPercent;
if (stopPercent < 0) stopPercent = 0;
if (stopPercentTotal === 0) {
stopPercent = 1;
stopPercentTotal = 1;
}
return stopPercent / stopPercentTotal;
};
class InterpolatedVars extends Base {
name;
unit;
values;
ready = false;
value = [];
interpolated = {};
options = {
roundTo: 3,
exclude: [],
keyLimit: 10,
keyName: defaultKeyNamer,
interpolator: lerp,
separator: "-",
steps: 10
};
constructor(name, unit, values, options = {}) {
super();
if (isBlank(name)) throw new Error("Name cannot be blank.");
this.name = name;
this.unit = unit;
this.set(values);
this.setOpts(options);
this.ready = true;
this.notify();
}
setOpts(value) {
this.options = { ...this.options, ...value };
if (this.ready) {
this.notify();
}
}
set(value) {
const hasStops = Array.isArray(value[0]);
if (this.ready) {
for (const val of this.values) {
const v = hasStops ? val[1] : val;
v?.removeDep(this);
}
}
if (hasStops && value.find((entry) => entry[0] > 1) !== void 0) {
throw new Error("Stop Entry percentage must be expressed in a value from 0 to 1.");
}
this.values = hasStops ? [...value].sort((a, b) => a[0] - b[0]) : value;
for (const val of this.values) {
const v = hasStops ? val[1] : val;
v.addDep(this);
}
if (this.ready) {
this.notify();
}
}
notify() {
this.recompute();
this._notify();
}
recompute() {
const valRes = [];
const interpolatedRes = {};
const steps = this.options.steps;
const totalSteps = getTotalSteps(steps);
const { values, name } = this;
const hasStops = Array.isArray(values[0]);
const lastStopIndex = values.length - 1;
const nonStopStepPercent = lastStopIndex === 0 ? 0 : 1 / lastStopIndex;
const state = {};
let stopIndex = -1;
let nextStopIndex = -1;
let startPercent = -1;
let endPercent = -1;
for (let i = 0; i < totalSteps; i++) {
let percent = Array.isArray(steps) ? steps[i] : i / (steps - 1);
let startVal, endVal;
if (hasStops) {
while ((stopIndex < 0 || endPercent < percent) && stopIndex < values.length - 1) {
stopIndex++;
startPercent = values[stopIndex][0];
nextStopIndex = Math.min(stopIndex + 1, lastStopIndex);
endPercent = values[nextStopIndex][0];
}
startVal = values[stopIndex][1];
endVal = values[nextStopIndex][1];
percent = getStepPercent(percent, startPercent, endPercent);
} else {
const startValIndex = Math.floor(percent * lastStopIndex);
const endValIndex = Math.min(startValIndex + 1, lastStopIndex);
startPercent = startValIndex * nonStopStepPercent;
endPercent = endValIndex * nonStopStepPercent;
percent = getStepPercent(percent, startPercent, endPercent);
startVal = values[startValIndex];
endVal = values[endValIndex];
}
const keyName = this.options.keyName({ i, steps, totalSteps, name: this.name, keyLimit: this.options.keyLimit, separator: this.options.separator });
const val = this.options.interpolator({
start: startVal,
end: endVal,
name,
percent,
state,
step: i,
keyName,
totalSteps,
steps,
exclude: this.options.exclude,
roundTo: this.options.roundTo
});
valRes.push(val);
interpolatedRes[keyName] = this.unit(val);
}
this.value = valRes;
this.interpolated = interpolatedRes;
}
}
export {
InterpolatedVars
};