1
0
Fork 0
mirror of https://github.com/muerwre/vault-frontend.git synced 2025-04-24 20:36:40 +07:00

made curves on basic graphs

This commit is contained in:
Fedor Katurov 2022-03-30 16:04:01 +07:00
parent ac533df3a3
commit c0f8766ec6
3 changed files with 140 additions and 16 deletions

View file

@ -1,40 +1,93 @@
import React, { VFC } from 'react';
import React, { useMemo, VFC } from 'react';
import { lighten } from 'color2k';
import { makeBezierCurve, PathPoint } from '~/utils/dom/makeBezierCurve';
import { SVGProps } from '~/utils/types';
interface BasicCurveChartProps extends SVGProps {
items: number[];
gap?: number;
fullscreen?: boolean;
}
const gap = 5;
const BasicCurveChart: VFC<BasicCurveChartProps> = ({
stroke = '#007962',
items = [],
fullscreen = true,
...props
}) => {
const max = Math.max(...items) + 5;
const max = Math.max(...items);
const height = props.height ? parseFloat(props.height.toString()) : 100;
const width = props.width ? parseFloat(props.width.toString()) : 100;
const borderGap = fullscreen ? 0 : gap;
const d = items.reduce<string[]>(
(acc, val, index) => [
...acc,
index === 0
? `M 5 ${height - (val / max) * height}`
: `L ${(width / (items.length - 1)) * index} ${height - (val / max) * height}`,
],
[]
const points = useMemo(
() =>
items.reduce<PathPoint[]>(
(acc, val, index) => [
...acc,
index === 0
? { x: borderGap, y: height - (val / max) * (height - gap * 2) - gap }
: {
x: ((width - borderGap) / (items.length - 1)) * index,
y: height - (val / max) * (height - gap * 2) - gap,
},
],
[]
),
[height, width, items, gap]
);
if (!points.length) {
return null;
}
return (
<svg {...props} width={width} height={height} viewBox={`0 0 ${width} ${height}`}>
<svg
{...props}
width="100%"
height="100%"
viewBox={`0 0 ${width} ${height}`}
preserveAspectRatio="none"
>
<defs>
<filter id="f1" x="0" y="0">
<filter id="f1" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur in="SourceGraphic" stdDeviation="2" />
</filter>
</defs>
<path d={d.join(' ')} fill="none" x={0} y={0} stroke={stroke} filter="url(#f1)" />
<path d={d.join(' ')} fill="none" x={0} y={0} stroke={stroke} />
<path
d={makeBezierCurve(points)}
fill="none"
x={0}
y={0}
stroke={stroke}
strokeWidth={2}
strokeLinecap="round"
filter="url(#f1)"
/>
<path
d={makeBezierCurve(points)}
fill="none"
x={0}
y={0}
stroke={stroke}
strokeWidth={2}
strokeLinecap="round"
/>
<path
d={makeBezierCurve(points)}
fill="none"
x={0}
y={0}
stroke={lighten(stroke, 0.1)}
strokeWidth={1}
strokeLinecap="round"
/>
</svg>
);
};

View file

@ -6,6 +6,8 @@ import { BorisSidebar } from '~/components/boris/BorisSidebar';
import { Superpower } from '~/components/boris/Superpower';
import { BasicCurveChart } from '~/components/charts/BasicCurveChart';
import { Card } from '~/components/containers/Card';
import { Filler } from '~/components/containers/Filler';
import { Grid } from '~/components/containers/Grid';
import { Group } from '~/components/containers/Group';
import { Padder } from '~/components/containers/Padder';
import { Sticky } from '~/components/containers/Sticky';
@ -66,10 +68,19 @@ const BorisLayout: FC<IProps> = ({ title, setIsBetaTester, isTester, stats, isLo
<Group>
<h4>Количество нод за год</h4>
<BasicCurveChart items={stats.backend.nodes.by_month} width={200} />
<Grid horizontal>
<Card style={{ padding: 0 }}>
<BasicCurveChart items={stats.backend.nodes.by_month} width={200} />
</Card>
<Card style={{ padding: 0 }}>
<BasicCurveChart items={stats.backend.comments.by_month} width={200} />
</Card>
<Filler />
</Grid>
<h4>Количество комментов за год</h4>
<BasicCurveChart items={stats.backend.comments.by_month} width={200} />
</Group>
</Group>
</Padder>

View file

@ -0,0 +1,60 @@
// size of the tangent
const t = 1 / 5;
export interface PathPoint {
x: number;
y: number;
}
const controlPoints = (p: PathPoint[]) => {
const pc: PathPoint[][] = [];
for (let i = 1; i < p.length - 1; i++) {
const dx = p[i - 1].x - p[i + 1].x; // difference x
const dy = p[i - 1].y - p[i + 1].y; // difference y
// the first control point
const x1 = p[i].x - dx * t;
const y1 = p[i].y - dy * t;
const o1 = {
x: x1,
y: y1,
};
// the second control point
const x2 = p[i].x + dx * t;
const y2 = p[i].y + dy * t;
const o2 = {
x: x2,
y: y2,
};
// building the control points array
pc[i] = [];
pc[i].push(o1);
pc[i].push(o2);
}
return pc;
};
export const makeBezierCurve = (p: PathPoint[]) => {
const pc = controlPoints(p); // the control points array
let d = `M${p[0].x},${p[0].y} Q${pc[1][1].x},${pc[1][1].y}, ${p[1].x},${p[1].y}`;
if (p.length > 2) {
// central curves are cubic Bezier
for (let i = 1; i < p.length - 2; i++) {
d += `C${pc[i][0].x}, ${pc[i][0].y}, ${pc[i + 1][1].x}, ${pc[i + 1][1].y}, ${p[i + 1].x},${
p[i + 1].y
}`;
}
// the first & the last curve are quadratic Bezier
const n = p.length - 1;
d += `Q${pc[n - 1][0].x}, ${pc[n - 1][0].y}, ${p[n].x}, ${p[n].y}`;
}
return d;
};