feat: traffic line graph

This commit is contained in:
GyDi 2022-02-19 17:02:24 +08:00
parent 7e4506c860
commit 457655b416
No known key found for this signature in database
GPG Key ID: 1C95E0D3467B3084
2 changed files with 154 additions and 2 deletions

View File

@ -6,10 +6,12 @@ import { getInfomation } from "../../services/api";
import { ApiType } from "../../services/types"; import { ApiType } from "../../services/types";
import { atomClashPort } from "../../states/setting"; import { atomClashPort } from "../../states/setting";
import parseTraffic from "../../utils/parse-traffic"; import parseTraffic from "../../utils/parse-traffic";
import useTrafficGraph from "./use-traffic-graph";
const LayoutTraffic = () => { const LayoutTraffic = () => {
const portValue = useRecoilValue(atomClashPort); const portValue = useRecoilValue(atomClashPort);
const [traffic, setTraffic] = useState({ up: 0, down: 0 }); const [traffic, setTraffic] = useState({ up: 0, down: 0 });
const { canvasRef, appendData, toggleStyle } = useTrafficGraph();
useEffect(() => { useEffect(() => {
let ws: WebSocket | null = null; let ws: WebSocket | null = null;
@ -19,7 +21,9 @@ const LayoutTraffic = () => {
ws = new WebSocket(`ws://${server}/traffic?token=${secret}`); ws = new WebSocket(`ws://${server}/traffic?token=${secret}`);
ws.addEventListener("message", (event) => { ws.addEventListener("message", (event) => {
setTraffic(JSON.parse(event.data) as ApiType.TrafficItem); const data = JSON.parse(event.data) as ApiType.TrafficItem;
appendData(data);
setTraffic(data);
}); });
}); });
@ -44,7 +48,12 @@ const LayoutTraffic = () => {
}; };
return ( return (
<Box width="110px"> <Box data-windrag width="110px" position="relative" onClick={toggleStyle}>
<canvas
ref={canvasRef}
style={{ width: "100%", height: 60, marginBottom: 6 }}
/>
<Box mb={1.5} display="flex" alignItems="center" whiteSpace="nowrap"> <Box mb={1.5} display="flex" alignItems="center" whiteSpace="nowrap">
<ArrowUpward <ArrowUpward
fontSize="small" fontSize="small"

View File

@ -0,0 +1,143 @@
import { useRef } from "react";
const minPoint = 10;
const maxPoint = 36;
const refLineAlpha = 0.5;
const refLineWidth = 2;
const refLineColor = "#ccc";
const upLineAlpha = 0.6;
const upLineWidth = 4;
const upLineColor = "#9c27b0";
const downLineAlpha = 1;
const downLineWidth = 4;
const downLineColor = "#5b5c9d";
/**
* draw the traffic graph
*/
export default function useTrafficGraph() {
type TrafficData = { up: number; down: number };
const listRef = useRef<TrafficData[]>([]);
const styleRef = useRef(true);
const canvasRef = useRef<HTMLCanvasElement>(null!);
const drawGraph = () => {
const canvas = canvasRef.current!;
const context = canvas.getContext("2d")!;
const width = canvas.width;
const height = canvas.height;
const l1 = height * 0.2;
const l2 = height * 0.6;
const dl = height * 0.4;
context.clearRect(0, 0, width, height);
// Reference lines
context.beginPath();
context.globalAlpha = refLineAlpha;
context.lineWidth = refLineWidth;
context.strokeStyle = refLineColor;
context.moveTo(0, l1);
context.lineTo(width, l1);
context.moveTo(0, l2);
context.lineTo(width, l2);
context.stroke();
context.closePath();
const countY = (value: number) => {
let v = value;
if (v < 1024) v = (v / 1024) * dl;
else if (v < 1048576) v = dl + (v / 1048576) * dl;
else v = 2 * dl + (v / 10485760) * l1;
return height - v;
};
const drawBezier = (list: number[]) => {
const len = list.length;
const size = Math.min(Math.max(len, minPoint), maxPoint);
const axis = width / size;
let lx = 0;
let ly = height;
let llx = 0;
let lly = height;
list.forEach((val, index) => {
const x = (axis * index) | 0;
const y = countY(val);
const s = 0.25;
if (index === 0) context.moveTo(x, y);
else {
let nx = (axis * (index + 1)) | 0;
let ny = index < len - 1 ? countY(list[index + 1]) | 0 : 0;
const ax = (lx + (x - llx) * s) | 0;
const ay = (ly + (y - lly) * s) | 0;
const bx = (x - (nx - lx) * s) | 0;
const by = (y - (ny - ly) * s) | 0;
context.bezierCurveTo(ax, ay, bx, by, x, y);
}
llx = lx;
lly = ly;
lx = x;
ly = y;
});
};
const drawLine = (list: number[]) => {
const len = list.length;
const size = Math.min(Math.max(len, minPoint), maxPoint);
const axis = width / size;
list.forEach((val, index) => {
const x = (axis * index) | 0;
const y = countY(val);
if (index === 0) context.moveTo(x, y);
else context.lineTo(x, y);
});
};
const listUp = listRef.current.map((v) => v.up);
const listDown = listRef.current.map((v) => v.down);
const lineStyle = styleRef.current;
context.beginPath();
context.globalAlpha = upLineAlpha;
context.lineWidth = upLineWidth;
context.strokeStyle = upLineColor;
lineStyle ? drawLine(listUp) : drawBezier(listUp);
context.stroke();
context.closePath();
context.beginPath();
context.globalAlpha = downLineAlpha;
context.lineWidth = downLineWidth;
context.strokeStyle = downLineColor;
lineStyle ? drawLine(listDown) : drawBezier(listDown);
context.stroke();
context.closePath();
};
const appendData = (data: TrafficData) => {
const list = listRef.current;
if (list.length > maxPoint) list.shift();
list.push(data);
drawGraph();
};
const toggleStyle = () => {
styleRef.current = !styleRef.current;
drawGraph();
};
return {
canvasRef,
appendData,
toggleStyle,
};
}