/
Example.tsx
112 lines (98 loc) 路 3.44 KB
/
Example.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
import React from 'react';
import { Group } from '@vx/group';
import letterFrequency, { LetterFrequency } from '@vx/mock-data/lib/mocks/letterFrequency';
import { scaleLinear } from '@vx/scale';
import { Point } from '@vx/point';
import { Line, LineRadial } from '@vx/shape';
const orange = '#ff9933';
export const pumpkin = '#f5810c';
const silver = '#d9d9d9';
export const bg = '#FAF7E9';
const degrees = 360;
const data = letterFrequency.slice(2, 12);
const y = (d: LetterFrequency) => d.frequency;
const genAngles = (length: number) =>
[...new Array(length + 1)].map((_, i) => ({
angle: i * (degrees / length),
}));
const genPoints = (length: number, radius: number) => {
const step = (Math.PI * 2) / length;
return [...new Array(length)].map((_, i) => ({
x: radius * Math.sin(i * step),
y: radius * Math.cos(i * step),
}));
};
function genPolygonPoints<Datum>(
dataArray: Datum[],
scale: (n: number) => number,
getValue: (d: Datum) => number,
) {
const step = (Math.PI * 2) / dataArray.length;
const points: { x: number; y: number }[] = new Array(dataArray.length).fill({ x: 0, y: 0 });
const pointString: string = new Array(dataArray.length + 1).fill('').reduce((res, _, i) => {
if (i > dataArray.length) return res;
const xVal = scale(getValue(dataArray[i - 1])) * Math.sin(i * step);
const yVal = scale(getValue(dataArray[i - 1])) * Math.cos(i * step);
points[i - 1] = { x: xVal, y: yVal };
res += `${xVal},${yVal} `;
return res;
});
return { points, pointString };
}
const defaultMargin = { top: 40, left: 80, right: 80, bottom: 80 };
type Props = {
width: number;
height: number;
margin?: { top: number; right: number; bottom: number; left: number };
levels?: number;
};
export default function Example({ width, height, levels = 5, margin = defaultMargin }: Props) {
const xMax = width - margin.left - margin.right;
const yMax = height - margin.top - margin.bottom;
const radius = Math.min(xMax, yMax) / 2;
const radialScale = scaleLinear<number>({
range: [0, Math.PI * 2],
domain: [degrees, 0],
});
const yScale = scaleLinear<number>({
range: [0, radius],
domain: [0, Math.max(...data.map(y))],
});
const webs = genAngles(data.length);
const points = genPoints(data.length, radius);
const polygonPoints = genPolygonPoints(data, yScale, y);
const zeroPoint = new Point({ x: 0, y: 0 });
return width < 10 ? null : (
<svg width={width} height={height}>
<rect fill={bg} width={width} height={height} rx={14} />
<Group top={height / 2 - margin.top} left={width / 2}>
{[...new Array(levels)].map((_, i) => (
<LineRadial
key={`web-${i}`}
data={webs}
angle={d => radialScale(d.angle)}
radius={((i + 1) * radius) / levels}
fill="none"
stroke={silver}
strokeWidth={2}
strokeOpacity={0.8}
strokeLinecap="round"
/>
))}
{[...new Array(data.length)].map((_, i) => (
<Line key={`radar-line-${i}`} from={zeroPoint} to={points[i]} stroke={silver} />
))}
<polygon
points={polygonPoints.pointString}
fill={orange}
fillOpacity={0.3}
stroke={orange}
strokeWidth={1}
/>
{polygonPoints.points.map((point, i) => (
<circle key={`radar-point-${i}`} cx={point.x} cy={point.y} r={4} fill={pumpkin} />
))}
</Group>
</svg>
);
}