85 lines
1.9 KiB
TypeScript
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 };
|
|
};
|