Files
snk/packages/svg-creator/stack.ts
2025-02-20 19:58:59 +07:00

85 lines
1.9 KiB
TypeScript

import type { Color, Empty } from "@snk/types/grid";
import { createAnimation } from "./css-utils";
import { h } from "./xml-utils";
export type Options = {
sizeDot: number;
};
export const createStack = (
cells: { t: number | null; color: Color | Empty }[],
{ sizeDot }: Options,
width: number,
y: number,
duration: number,
) => {
const svgElements: string[] = [];
const styles = [
`.u{
transform-origin: 0 0;
transform: scale(0,1);
animation: none linear ${duration}ms infinite;
}`,
];
const stack = cells
.slice()
.filter((a) => a.t !== null)
.sort((a, b) => a.t! - b.t!) as any[];
const blocks: { color: Color; ts: number[] }[] = [];
stack.forEach(({ color, t }) => {
const latest = blocks[blocks.length - 1];
if (latest?.color === color) latest.ts.push(t);
else blocks.push({ color, ts: [t] });
});
const m = width / stack.length;
let i = 0;
let nx = 0;
for (const { color, ts } of blocks) {
const id = "u" + (i++).toString(36);
const animationName = id;
const x = (nx * m).toFixed(1);
nx += ts.length;
svgElements.push(
h("rect", {
class: `u ${id}`,
height: sizeDot,
width: (ts.length * m + 0.6).toFixed(1),
x,
y,
}),
);
styles.push(
createAnimation(
animationName,
[
...ts
.map((t, i, { length }) => [
{ scale: i / length, t: t - 0.0001 },
{ scale: (i + 1) / length, t: t + 0.0001 },
])
.flat(),
{ scale: 1, t: 1 },
].map(({ scale, t }) => ({
t,
style: `transform:scale(${scale.toFixed(3)},1)`,
})),
),
`.u.${id} {
fill: var(--c${color});
animation-name: ${animationName};
transform-origin: ${x}px 0
}
`,
);
}
return { svgElements, styles };
};