🚀 improve svg generation
This commit is contained in:
@@ -24,7 +24,7 @@ try {
|
||||
} catch (err) {}
|
||||
|
||||
for (const [key, grid] of Object.entries(grids))
|
||||
it(`should generate ${key} gif`, () => {
|
||||
it(`should generate ${key} svg`, () => {
|
||||
const chain = [snake, ...getBestRoute(grid, snake)!];
|
||||
|
||||
const gif = createSvg(grid, chain, drawOptions, gifOptions);
|
||||
|
||||
@@ -27,13 +27,14 @@ export const createGrid = (
|
||||
) => {
|
||||
const svgElements: string[] = [];
|
||||
const styles = [
|
||||
`rect.c{
|
||||
`.c{
|
||||
shape-rendering: geometricPrecision;
|
||||
outline: 1px solid ${colorBorder};
|
||||
outline-offset: -1px;
|
||||
rx: ${sizeBorderRadius};
|
||||
ry: ${sizeBorderRadius};
|
||||
fill:${colorEmpty}
|
||||
fill:${colorEmpty};
|
||||
stroke-width: 1px;
|
||||
stroke: ${colorBorder};
|
||||
animation: none ${duration}ms linear infinite;
|
||||
}`,
|
||||
];
|
||||
|
||||
@@ -55,14 +56,13 @@ export const createGrid = (
|
||||
`${percent(t + 0.0001)}%,100%{fill:${colorEmpty}}` +
|
||||
"}",
|
||||
|
||||
`#${id}{fill:${fill};animation: ${animationName} linear ${duration}ms infinite}`
|
||||
`.c.${id}{fill:${fill};animation-name: ${animationName}}`
|
||||
);
|
||||
}
|
||||
|
||||
svgElements.push(
|
||||
h("rect", {
|
||||
id,
|
||||
class: "c",
|
||||
class: ["c", id].filter(Boolean).join(" "),
|
||||
x: x * s + m,
|
||||
y: y * s + m,
|
||||
width: d,
|
||||
|
||||
@@ -25,26 +25,20 @@ export type Options = {
|
||||
cells?: Point[];
|
||||
};
|
||||
|
||||
const createCells = ({ width, height }: Grid) =>
|
||||
const getCellsFromGrid = ({ width, height }: Grid) =>
|
||||
Array.from({ length: width }, (_, x) =>
|
||||
Array.from({ length: height }, (_, y) => ({ x, y }))
|
||||
).flat();
|
||||
|
||||
export const createSvg = (
|
||||
const createLivingCells = (
|
||||
grid0: Grid,
|
||||
chain: Snake[],
|
||||
drawOptions: Options,
|
||||
gifOptions: { frameDuration: number }
|
||||
drawOptions: Options
|
||||
) => {
|
||||
const width = (grid0.width + 2) * drawOptions.sizeCell;
|
||||
const height = (grid0.height + 5) * drawOptions.sizeCell;
|
||||
|
||||
const duration = gifOptions.frameDuration * chain.length;
|
||||
|
||||
const cells: (Point & {
|
||||
t: number | null;
|
||||
color: Color | Empty;
|
||||
})[] = (drawOptions.cells ?? createCells(grid0)).map(({ x, y }) => ({
|
||||
})[] = (drawOptions.cells ?? getCellsFromGrid(grid0)).map(({ x, y }) => ({
|
||||
x,
|
||||
y,
|
||||
t: null,
|
||||
@@ -64,13 +58,29 @@ export const createSvg = (
|
||||
}
|
||||
}
|
||||
|
||||
return cells;
|
||||
};
|
||||
|
||||
export const createSvg = (
|
||||
grid: Grid,
|
||||
chain: Snake[],
|
||||
drawOptions: Options,
|
||||
gifOptions: { frameDuration: number }
|
||||
) => {
|
||||
const width = (grid.width + 2) * drawOptions.sizeCell;
|
||||
const height = (grid.height + 5) * drawOptions.sizeCell;
|
||||
|
||||
const duration = gifOptions.frameDuration * chain.length;
|
||||
|
||||
const cells = createLivingCells(grid, chain, drawOptions);
|
||||
|
||||
const elements = [
|
||||
createGrid(cells, drawOptions, duration),
|
||||
createStack(
|
||||
cells,
|
||||
drawOptions,
|
||||
grid0.width * drawOptions.sizeCell,
|
||||
(grid0.height + 2) * drawOptions.sizeCell,
|
||||
grid.width * drawOptions.sizeCell,
|
||||
(grid.height + 2) * drawOptions.sizeCell,
|
||||
duration
|
||||
),
|
||||
createSnake(chain, drawOptions, duration),
|
||||
@@ -83,21 +93,32 @@ export const createSvg = (
|
||||
height,
|
||||
].join(" ");
|
||||
|
||||
return [
|
||||
const style = elements
|
||||
.map((e) => e.styles)
|
||||
.flat()
|
||||
.join("");
|
||||
|
||||
const svg = [
|
||||
`<svg
|
||||
${toAttribute({
|
||||
viewBox,
|
||||
width,
|
||||
height,
|
||||
xmlns: "http://www.w3.org/2000/svg",
|
||||
})}
|
||||
>`,
|
||||
${toAttribute({
|
||||
viewBox,
|
||||
width,
|
||||
height,
|
||||
xmlns: "http://www.w3.org/2000/svg",
|
||||
})}
|
||||
>`,
|
||||
|
||||
"<style>",
|
||||
...elements.map((e) => e.styles).flat(),
|
||||
optimizeCss(style),
|
||||
"</style>",
|
||||
|
||||
...elements.map((e) => e.svgElements).flat(),
|
||||
|
||||
"</svg>",
|
||||
].join("\n");
|
||||
].join("");
|
||||
|
||||
return optimizeSvg(svg);
|
||||
};
|
||||
|
||||
const optimizeCss = (css: string) => css;
|
||||
const optimizeSvg = (svg: string) => svg;
|
||||
|
||||
@@ -42,17 +42,16 @@ export const createSnake = (
|
||||
|
||||
const m = (sizeCell - s) / 2;
|
||||
|
||||
const r = Math.min(4.5, (4 * s) / sizeDot).toFixed(2);
|
||||
const r = Math.min(4.5, (4 * s) / sizeDot);
|
||||
|
||||
return h("rect", {
|
||||
class: "s",
|
||||
id: `s${i}`,
|
||||
x: m,
|
||||
y: m,
|
||||
width: s,
|
||||
height: s,
|
||||
rx: r,
|
||||
ry: r,
|
||||
class: `s s${i}`,
|
||||
x: m.toFixed(1),
|
||||
y: m.toFixed(1),
|
||||
width: s.toFixed(1),
|
||||
height: s.toFixed(1),
|
||||
rx: r.toFixed(1),
|
||||
ry: r.toFixed(1),
|
||||
});
|
||||
});
|
||||
|
||||
@@ -60,9 +59,10 @@ export const createSnake = (
|
||||
`transform:translate(${x * sizeCell}px,${y * sizeCell}px)`;
|
||||
|
||||
const styles = [
|
||||
`rect.s{
|
||||
shape-rendering: geometricPrecision;
|
||||
`.s{
|
||||
shape-rendering:geometricPrecision;
|
||||
fill:${colorSnake};
|
||||
animation: none linear ${duration}ms infinite
|
||||
}`,
|
||||
|
||||
...snakeParts.map((positions, i) => {
|
||||
@@ -78,10 +78,7 @@ export const createSnake = (
|
||||
.join("") +
|
||||
"}",
|
||||
|
||||
`#${id}{` +
|
||||
`${transform(positions[0])};` +
|
||||
`animation: ${animationName} linear ${duration}ms infinite` +
|
||||
"}",
|
||||
`.s.${id}{${transform(positions[0])};animation-name: ${animationName}}`,
|
||||
];
|
||||
}),
|
||||
].flat();
|
||||
|
||||
@@ -16,7 +16,12 @@ export const createStack = (
|
||||
duration: number
|
||||
) => {
|
||||
const svgElements: string[] = [];
|
||||
const styles = [];
|
||||
const styles = [
|
||||
`.u{
|
||||
animation: none linear ${duration}ms infinite;
|
||||
fill:transparent;
|
||||
}`,
|
||||
];
|
||||
|
||||
const stack = cells
|
||||
.slice()
|
||||
@@ -26,14 +31,20 @@ export const createStack = (
|
||||
const m = width / stack.length;
|
||||
let i = 0;
|
||||
for (const { color, t } of stack) {
|
||||
const x = ((i * width) / stack.length).toFixed(2);
|
||||
const id = "t" + (i++).toString(36);
|
||||
const x = (i * width) / stack.length;
|
||||
const id = "u" + (i++).toString(36);
|
||||
const animationName = "a" + id;
|
||||
// @ts-ignore
|
||||
const fill = colorDots[color];
|
||||
|
||||
svgElements.push(
|
||||
h("rect", { id, height: sizeDot, width: (m + 0.6).toFixed(2), x, y })
|
||||
h("rect", {
|
||||
class: `u ${id}`,
|
||||
height: sizeDot,
|
||||
width: (m + 0.6).toFixed(1),
|
||||
x: x.toFixed(1),
|
||||
y,
|
||||
})
|
||||
);
|
||||
styles.push(
|
||||
`@keyframes ${animationName} {` +
|
||||
@@ -41,7 +52,7 @@ export const createStack = (
|
||||
`${percent(t + 0.0001)}%,100%{fill:${fill}}` +
|
||||
"}",
|
||||
|
||||
`#${id}{fill:transparent;animation: ${animationName} linear ${duration}ms infinite}`
|
||||
`.u.${id}{animation-name:${animationName}}`
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user