diff --git a/src/components/layout/layout-traffic.tsx b/src/components/layout/layout-traffic.tsx index e2da330..8e16f35 100644 --- a/src/components/layout/layout-traffic.tsx +++ b/src/components/layout/layout-traffic.tsx @@ -6,10 +6,12 @@ import { getInfomation } from "../../services/api"; import { ApiType } from "../../services/types"; import { atomClashPort } from "../../states/setting"; import parseTraffic from "../../utils/parse-traffic"; +import useTrafficGraph from "./use-traffic-graph"; const LayoutTraffic = () => { const portValue = useRecoilValue(atomClashPort); const [traffic, setTraffic] = useState({ up: 0, down: 0 }); + const { canvasRef, appendData, toggleStyle } = useTrafficGraph(); useEffect(() => { let ws: WebSocket | null = null; @@ -19,7 +21,9 @@ const LayoutTraffic = () => { ws = new WebSocket(`ws://${server}/traffic?token=${secret}`); 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 ( - + + + ([]); + const styleRef = useRef(true); + const canvasRef = useRef(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, + }; +}