diff --git a/frontend/src/GraphView.jsx b/frontend/src/GraphView.jsx
index 1d826de..5e35c6e 100644
--- a/frontend/src/GraphView.jsx
+++ b/frontend/src/GraphView.jsx
@@ -97,17 +97,18 @@ function GraphView({ servers, connections }) {
const current = d3.zoomTransform(svgEl);
const cx = svgEl.clientWidth / 2;
const cy = svgEl.clientHeight / 2;
- dbg('zoomBy click', { factor, current: { k: current.k, x: current.x, y: current.y }, center: { cx, cy } });
- const next = d3.zoomIdentity
- .translate(current.x, current.y)
- .scale(current.k)
- .translate(cx, cy)
- .scale(factor)
- .translate(-cx, -cy);
- d3.select(svgEl)
+ const minK = 0.3;
+ const maxK = 3;
+ let nextK = current.k * factor;
+ if (nextK < minK) nextK = minK;
+ if (nextK > maxK) nextK = maxK;
+ dbg('zoomBy click', { factor, current: { k: current.k, x: current.x, y: current.y }, nextK, center: { cx, cy } });
+ const svgSel = d3.select(svgEl);
+ svgSel.interrupt();
+ svgSel
.transition()
.duration(220)
- .call(zoomRef.current.transform, next)
+ .call(zoomRef.current.scaleTo, nextK, [cx, cy])
.on('end', () => {
const nt = d3.zoomTransform(svgEl);
dbg('zoomBy end', { next: { k: nt.k, x: nt.x, y: nt.y } });
@@ -459,17 +460,15 @@ function GraphView({ servers, connections }) {
fill="#fff"
stroke={countryColor}
/>
+ {/* Рендер флага через foreignObject как в HTML (надёжнее для Windows/Chrome) */}
+
+
+ {flag}
+
+
- {flag}
-
-