Smooth traffic chart paths and update version
Build and Deploy Gateway / build-and-push (push) Successful in 13s
Build and Deploy Gateway / deploy (push) Successful in 6s

This commit is contained in:
2026-08-07 23:02:18 +03:00
parent b084d7e42c
commit 39f3467f9b
4 changed files with 47 additions and 43 deletions
+29 -28
View File
@@ -45,6 +45,15 @@ function chartTime(value) {
});
}
function smoothTrafficPath(points, valueKey) {
if (!points.length) return '';
return points.slice(1).reduce((path, point, index) => {
const previous = points[index];
const midX = (previous.x + point.x) / 2;
return `${path} C ${midX},${previous[valueKey]} ${midX},${point[valueKey]} ${point.x},${point[valueKey]}`;
}, `M ${points[0].x},${points[0][valueKey]}`);
}
function TrafficChart({ samples, scale, capacity, routeLabel, pinned }) {
const [hovered, setHovered] = useState(null);
const previousPoints = useRef([]);
@@ -132,52 +141,44 @@ function TrafficChart({ samples, scale, capacity, routeLabel, pinned }) {
<line x1="0" x2="100" y1="100" y2="100" />
</g>}
<g className="client-device-traffic-lines" style={{ '--sample-count': Math.max(1, capacity) }}>
{previous.length > 0 && <polyline className="is-gateway" points={previous.map(({ x, gatewayY }) => `${x},${gatewayY}`).join(' ')}>
{previous.length > 0 && <path className="is-gateway" d={smoothTrafficPath(previous, 'gatewayY')}>
{animateScale && <animate
key={`gateway-${scale}`}
attributeName="points"
from={scaleFrom.slice(0, -1).map(({ x, gatewayY }) => `${x},${gatewayY}`).join(' ')}
to={previous.map(({ x, gatewayY }) => `${x},${gatewayY}`).join(' ')}
attributeName="d"
from={smoothTrafficPath(scaleFrom.slice(0, -1), 'gatewayY')}
to={smoothTrafficPath(previous, 'gatewayY')}
dur="520ms"
calcMode="spline"
keyTimes="0;1"
keySplines="0.16 1 0.3 1"
fill="freeze"
/>}
</polyline>}
{hasProxy && previous.length > 0 && <polyline className="is-proxy" points={previous.map(({ x, proxyY }) => `${x},${proxyY}`).join(' ')}>
</path>}
{hasProxy && previous.length > 0 && <path className="is-proxy" d={smoothTrafficPath(previous, 'proxyY')}>
{animateScale && <animate
key={`proxy-${scale}`}
attributeName="points"
from={scaleFrom.slice(0, -1).map(({ x, proxyY }) => `${x},${proxyY}`).join(' ')}
to={previous.map(({ x, proxyY }) => `${x},${proxyY}`).join(' ')}
attributeName="d"
from={smoothTrafficPath(scaleFrom.slice(0, -1), 'proxyY')}
to={smoothTrafficPath(previous, 'proxyY')}
dur="520ms"
calcMode="spline"
keyTimes="0;1"
keySplines="0.16 1 0.3 1"
fill="freeze"
/>}
</polyline>}
{penultimate && newest && <line className="is-gateway is-new" pathLength="1" x1={penultimate.x} y1={penultimate.gatewayY} x2={newest.x} y2={newest.gatewayY}>
{animateScale && <>
<animate key={`gateway-y1-${scale}`} attributeName="y1" from={scaleFrom.at(-2).gatewayY} to={penultimate.gatewayY} dur="520ms" fill="freeze" />
<animate key={`gateway-y2-${scale}`} attributeName="y2" from={scaleFrom.at(-1).gatewayY} to={newest.gatewayY} dur="520ms" fill="freeze" />
</>}
</line>}
{hasProxy && penultimate && newest && <line className="is-proxy is-new" pathLength="1" x1={penultimate.x} y1={penultimate.proxyY} x2={newest.x} y2={newest.proxyY}>
{animateScale && <>
<animate key={`proxy-y1-${scale}`} attributeName="y1" from={scaleFrom.at(-2).proxyY} to={penultimate.proxyY} dur="520ms" fill="freeze" />
<animate key={`proxy-y2-${scale}`} attributeName="y2" from={scaleFrom.at(-1).proxyY} to={newest.proxyY} dur="520ms" fill="freeze" />
</>}
</line>}
{!penultimate && newest && <circle className="is-gateway is-new" cx={newest.x} cy={newest.gatewayY} r="1.5">
{animateScale && <animate key={`gateway-cy-${scale}`} attributeName="cy" from={scaleFrom[0].gatewayY} to={newest.gatewayY} dur="520ms" fill="freeze" />}
</circle>}
</path>}
{penultimate && newest && <path className="is-gateway is-new" pathLength="1" d={smoothTrafficPath([penultimate, newest], 'gatewayY')}>
{animateScale && <animate key={`gateway-new-${scale}`} attributeName="d" from={smoothTrafficPath(scaleFrom.slice(-2), 'gatewayY')} to={smoothTrafficPath([penultimate, newest], 'gatewayY')} dur="520ms" fill="freeze" />}
</path>}
{hasProxy && penultimate && newest && <path className="is-proxy is-new" pathLength="1" d={smoothTrafficPath([penultimate, newest], 'proxyY')}>
{animateScale && <animate key={`proxy-new-${scale}`} attributeName="d" from={smoothTrafficPath(scaleFrom.slice(-2), 'proxyY')} to={smoothTrafficPath([penultimate, newest], 'proxyY')} dur="520ms" fill="freeze" />}
</path>}
{!penultimate && newest && <line className="is-gateway is-point" x1={newest.x} x2={newest.x} y1={newest.gatewayY} y2={newest.gatewayY} />}
</g>
{hovered && <g className="client-device-traffic-cursor">
<line x1={hovered.x} x2={hovered.x} y1={TRAFFIC_CHART_HEADROOM} y2="100" />
<circle className="is-gateway" cx={hovered.x} cy={hovered.gatewayY} r="2" />
{hovered.proxy > 0n && <circle className="is-proxy" cx={hovered.x} cy={hovered.proxyY} r="2" />}
<line className="is-guide" x1={hovered.x} x2={hovered.x} y1={TRAFFIC_CHART_HEADROOM} y2="100" />
<line className="is-point is-gateway" x1={hovered.x} x2={hovered.x} y1={hovered.gatewayY} y2={hovered.gatewayY} />
{hovered.proxy > 0n && <line className="is-point is-proxy" x1={hovered.x} x2={hovered.x} y1={hovered.proxyY} y2={hovered.proxyY} />}
</g>}
</svg>
</span>