Smooth traffic chart paths and update version
This commit is contained in:
@@ -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>
|
||||
|
||||
+10
-10
@@ -1451,7 +1451,7 @@ p {
|
||||
animation: client-device-traffic-shift 560ms cubic-bezier(0.16, 1, 0.3, 1);
|
||||
}
|
||||
|
||||
.client-device-traffic-lines polyline,
|
||||
.client-device-traffic-lines path,
|
||||
.client-device-traffic-lines line {
|
||||
fill: none;
|
||||
stroke: color-mix(in oklch, var(--client-text) 72%, var(--client-muted));
|
||||
@@ -1465,32 +1465,32 @@ p {
|
||||
stroke: var(--client-accent);
|
||||
}
|
||||
|
||||
.client-device-traffic-lines line.is-new {
|
||||
.client-device-traffic-lines path.is-new {
|
||||
stroke-dasharray: 1;
|
||||
stroke-dashoffset: 1;
|
||||
animation: client-device-traffic-line-draw 620ms cubic-bezier(0.16, 1, 0.3, 1) forwards;
|
||||
}
|
||||
|
||||
.client-device-traffic-lines circle {
|
||||
fill: color-mix(in oklch, var(--client-text) 72%, var(--client-muted));
|
||||
vector-effect: non-scaling-stroke;
|
||||
.client-device-traffic-lines .is-point {
|
||||
stroke-width: 3;
|
||||
stroke-linecap: round;
|
||||
}
|
||||
|
||||
.client-device-traffic-cursor line {
|
||||
.client-device-traffic-cursor .is-guide {
|
||||
stroke: color-mix(in oklch, var(--client-text) 38%, transparent);
|
||||
stroke-width: 1;
|
||||
stroke-dasharray: 2 3;
|
||||
vector-effect: non-scaling-stroke;
|
||||
}
|
||||
|
||||
.client-device-traffic-cursor circle {
|
||||
fill: var(--client-bg);
|
||||
.client-device-traffic-cursor .is-point {
|
||||
stroke: color-mix(in oklch, var(--client-text) 82%, var(--client-muted));
|
||||
stroke-width: 1.5;
|
||||
stroke-width: 4;
|
||||
stroke-linecap: round;
|
||||
vector-effect: non-scaling-stroke;
|
||||
}
|
||||
|
||||
.client-device-traffic-cursor circle.is-proxy {
|
||||
.client-device-traffic-cursor .is-point.is-proxy {
|
||||
stroke: var(--client-accent);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user