Skip to content

Commit 3cad505

Browse files
committed
비율 튕김 이슈 해결
1 parent 293d572 commit 3cad505

6 files changed

Lines changed: 223 additions & 103 deletions

File tree

src/components/ide/animations/BinaryTreeAnimation.jsx

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import * as d3 from 'd3';
44

55
const BinaryTreeAnimation = ({ data, currentStep, theme }) => {
66
const svgRef = useRef(null);
7+
const gRef = useRef(null);
78
const [zoomLevel, setZoomLevel] = useState(100);
89
const zoomBehaviorRef = useRef(null);
910
const currentTransformRef = useRef(d3.zoomIdentity);
@@ -103,28 +104,38 @@ const BinaryTreeAnimation = ({ data, currentStep, theme }) => {
103104
}, [treeState]);
104105

105106
useEffect(() => {
106-
const svg = d3.select(svgRef.current);
107-
svg.selectAll('*').remove();
107+
const svgElement = svgRef.current;
108+
if (!svgElement) return;
108109

109-
if (treeLayout.nodes.length === 0) return;
110+
const svg = d3.select(svgElement);
110111

111-
const svgElement = svgRef.current;
112-
const width = svgElement?.clientWidth || 800;
113-
const height = svgElement?.clientHeight || 600;
112+
if (!gRef.current) {
113+
gRef.current = svg.append('g');
114+
}
114115

115-
const g = svg.append('g');
116+
const g = gRef.current;
116117

117-
const zoom = d3.zoom()
118-
.scaleExtent([0.3, 2])
119-
.on('zoom', (event) => {
120-
g.attr('transform', event.transform);
121-
currentTransformRef.current = event.transform;
122-
setZoomLevel(Math.round(event.transform.k * 100));
123-
});
118+
if (!zoomBehaviorRef.current) {
119+
const zoom = d3.zoom()
120+
.scaleExtent([0.3, 2])
121+
.on('zoom', (event) => {
122+
g.attr('transform', event.transform);
123+
currentTransformRef.current = event.transform;
124+
setZoomLevel(Math.round(event.transform.k * 100));
125+
});
126+
127+
zoomBehaviorRef.current = zoom;
128+
svg.call(zoom);
129+
}
130+
131+
g.attr('transform', currentTransformRef.current);
132+
g.selectAll('*').remove();
124133

125-
zoomBehaviorRef.current = zoom;
126-
svg.call(zoom);
127-
svg.call(zoom.transform, currentTransformRef.current);
134+
if (zoomBehaviorRef.current) {
135+
svg.call(zoomBehaviorRef.current.transform, currentTransformRef.current);
136+
}
137+
138+
if (treeLayout.nodes.length === 0) return;
128139

129140
g.selectAll('line')
130141
.data(treeLayout.links)
@@ -189,6 +200,14 @@ const BinaryTreeAnimation = ({ data, currentStep, theme }) => {
189200

190201
}, [treeLayout, highlightInfo]);
191202

203+
useEffect(() => () => {
204+
const svg = d3.select(svgRef.current);
205+
svg.selectAll('*').remove();
206+
gRef.current = null;
207+
zoomBehaviorRef.current = null;
208+
currentTransformRef.current = d3.zoomIdentity;
209+
}, []);
210+
192211
const handleZoomIn = () => {
193212
const svg = d3.select(svgRef.current);
194213
svg.transition().call(zoomBehaviorRef.current.scaleBy, 1.3);

src/components/ide/animations/GraphAnimation.jsx

Lines changed: 40 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import * as d3 from 'd3';
44

55
const GraphAnimation = ({ data, currentStep, theme }) => {
66
const svgRef = useRef(null);
7+
const gRef = useRef(null);
78
const [zoomLevel, setZoomLevel] = useState(100);
89
const zoomBehaviorRef = useRef(null);
910
const currentTransformRef = useRef(d3.zoomIdentity);
@@ -54,28 +55,42 @@ const GraphAnimation = ({ data, currentStep, theme }) => {
5455
}, [data, currentStep]);
5556

5657
useEffect(() => {
57-
const svg = d3.select(svgRef.current);
58-
svg.selectAll('*').remove();
58+
const svgElement = svgRef.current;
59+
if (!svgElement) return;
5960

60-
if (graphState.vertexCount === 0) return;
61+
const svg = d3.select(svgElement);
6162

62-
const svgElement = svgRef.current;
63-
const width = svgElement?.clientWidth || 800;
64-
const height = svgElement?.clientHeight || 600;
63+
if (!gRef.current) {
64+
gRef.current = svg.append('g');
65+
}
66+
67+
const g = gRef.current;
6568

66-
const g = svg.append('g');
69+
if (!zoomBehaviorRef.current) {
70+
const zoom = d3.zoom()
71+
.scaleExtent([0.5, 3])
72+
.on('zoom', (event) => {
73+
g.attr('transform', event.transform);
74+
currentTransformRef.current = event.transform;
75+
setZoomLevel(Math.round(event.transform.k * 100));
76+
});
6777

68-
const zoom = d3.zoom()
69-
.scaleExtent([0.5, 3])
70-
.on('zoom', (event) => {
71-
g.attr('transform', event.transform);
72-
currentTransformRef.current = event.transform;
73-
setZoomLevel(Math.round(event.transform.k * 100));
74-
});
78+
zoomBehaviorRef.current = zoom;
79+
svg.call(zoom);
80+
}
7581

76-
zoomBehaviorRef.current = zoom;
77-
svg.call(zoom);
78-
svg.call(zoom.transform, currentTransformRef.current);
82+
g.attr('transform', currentTransformRef.current);
83+
g.selectAll('*').remove();
84+
85+
if (zoomBehaviorRef.current) {
86+
svg.call(zoomBehaviorRef.current.transform, currentTransformRef.current);
87+
}
88+
89+
if (graphState.vertexCount === 0) return;
90+
91+
const svgElementRef = svgRef.current;
92+
const width = svgElementRef?.clientWidth || 800;
93+
const height = svgElementRef?.clientHeight || 600;
7994

8095
// 정적 원형 레이아웃 계산
8196
const centerX = width / 2;
@@ -138,6 +153,14 @@ const GraphAnimation = ({ data, currentStep, theme }) => {
138153

139154
}, [graphState, highlightedEdge]);
140155

156+
useEffect(() => () => {
157+
const svg = d3.select(svgRef.current);
158+
svg.selectAll('*').remove();
159+
gRef.current = null;
160+
zoomBehaviorRef.current = null;
161+
currentTransformRef.current = d3.zoomIdentity;
162+
}, []);
163+
141164
const handleZoomIn = () => {
142165
const svg = d3.select(svgRef.current);
143166
svg.transition().call(zoomBehaviorRef.current.scaleBy, 1.3);

src/components/ide/animations/HeapAnimation.jsx

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import * as d3 from 'd3';
44

55
const HeapAnimation = ({ data, currentStep, theme }) => {
66
const svgRef = useRef(null);
7+
const gRef = useRef(null);
78
const [zoomLevel, setZoomLevel] = useState(100);
89
const zoomBehaviorRef = useRef(null);
910
const currentTransformRef = useRef(d3.zoomIdentity);
@@ -59,27 +60,41 @@ const HeapAnimation = ({ data, currentStep, theme }) => {
5960
}, [data, currentStep]);
6061

6162
useEffect(() => {
62-
const svg = d3.select(svgRef.current);
63-
svg.selectAll('*').remove();
63+
const svgElement = svgRef.current;
64+
if (!svgElement) return;
6465

65-
if (heapState.nodes.length === 0) return;
66+
const svg = d3.select(svgElement);
67+
68+
if (!gRef.current) {
69+
gRef.current = svg.append('g');
70+
}
6671

67-
const width = svgRef.current?.clientWidth || 800;
68-
const height = svgRef.current?.clientHeight || 600;
72+
const g = gRef.current;
6973

70-
const g = svg.append('g');
74+
if (!zoomBehaviorRef.current) {
75+
const zoom = d3.zoom()
76+
.scaleExtent([0.5, 3])
77+
.on('zoom', (event) => {
78+
g.attr('transform', event.transform);
79+
currentTransformRef.current = event.transform;
80+
setZoomLevel(Math.round(event.transform.k * 100));
81+
});
7182

72-
const zoom = d3.zoom()
73-
.scaleExtent([0.5, 3])
74-
.on('zoom', (event) => {
75-
g.attr('transform', event.transform);
76-
currentTransformRef.current = event.transform;
77-
setZoomLevel(Math.round(event.transform.k * 100));
78-
});
83+
zoomBehaviorRef.current = zoom;
84+
svg.call(zoom);
85+
}
86+
87+
g.attr('transform', currentTransformRef.current);
88+
g.selectAll('*').remove();
7989

80-
zoomBehaviorRef.current = zoom;
81-
svg.call(zoom);
82-
svg.call(zoom.transform, currentTransformRef.current);
90+
if (zoomBehaviorRef.current) {
91+
svg.call(zoomBehaviorRef.current.transform, currentTransformRef.current);
92+
}
93+
94+
if (heapState.nodes.length === 0) return;
95+
96+
const width = svgElement?.clientWidth || 800;
97+
const height = svgElement?.clientHeight || 600;
8398

8499
const treeData = buildHeapTree(heapState.nodes);
85100
const root = d3.hierarchy(treeData);
@@ -152,6 +167,14 @@ const HeapAnimation = ({ data, currentStep, theme }) => {
152167

153168
}, [heapState, highlightInfo, theme]);
154169

170+
useEffect(() => () => {
171+
const svg = d3.select(svgRef.current);
172+
svg.selectAll('*').remove();
173+
gRef.current = null;
174+
zoomBehaviorRef.current = null;
175+
currentTransformRef.current = d3.zoomIdentity;
176+
}, []);
177+
155178
const handleZoomIn = () => {
156179
const svg = d3.select(svgRef.current);
157180
svg.transition().call(zoomBehaviorRef.current.scaleBy, 1.3);

src/components/ide/animations/LinkedListAnimation.jsx

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -48,24 +48,39 @@ const LinkedListAnimation = ({ data, currentStep, theme }) => {
4848

4949
// D3 Zoom 설정
5050
useEffect(() => {
51-
if (!containerRef.current || listState.nodes.length === 0) return;
52-
53-
const container = d3.select(containerRef.current);
54-
55-
const zoom = d3.zoom()
56-
.scaleExtent([0.5, 3])
57-
.on('zoom', (event) => {
58-
container.select('.zoom-group').attr('transform', event.transform);
59-
currentTransformRef.current = event.transform;
60-
setZoomLevel(Math.round(event.transform.k * 100));
61-
});
51+
const svgElement = containerRef.current;
52+
if (!svgElement) return;
53+
54+
const container = d3.select(svgElement);
55+
56+
if (!zoomBehaviorRef.current) {
57+
const zoom = d3.zoom()
58+
.scaleExtent([0.5, 3])
59+
.on('zoom', (event) => {
60+
container.select('.zoom-group').attr('transform', event.transform);
61+
currentTransformRef.current = event.transform;
62+
setZoomLevel(Math.round(event.transform.k * 100));
63+
});
64+
65+
zoomBehaviorRef.current = zoom;
66+
container.call(zoom);
67+
}
6268

63-
zoomBehaviorRef.current = zoom;
64-
container.call(zoom);
65-
container.call(zoom.transform, currentTransformRef.current);
69+
container.select('.zoom-group').attr('transform', currentTransformRef.current);
6670

71+
if (zoomBehaviorRef.current) {
72+
container.call(zoomBehaviorRef.current.transform, currentTransformRef.current);
73+
}
6774
}, [listState.nodes.length]);
6875

76+
useEffect(() => () => {
77+
if (!containerRef.current) return;
78+
const container = d3.select(containerRef.current);
79+
container.on('.zoom', null);
80+
zoomBehaviorRef.current = null;
81+
currentTransformRef.current = d3.zoomIdentity;
82+
}, []);
83+
6984
const handleZoomIn = () => {
7085
if (!containerRef.current) return;
7186
const container = d3.select(containerRef.current);

src/components/ide/animations/RecursionAnimation.jsx

Lines changed: 40 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ function buildRecursionTree(events) {
5959

6060
const RecursionAnimation = ({ data, currentStep, theme }) => {
6161
const svgRef = useRef(null);
62+
const gRef = useRef(null);
6263
const [zoomLevel, setZoomLevel] = useState(100);
6364
const zoomBehaviorRef = useRef(null); // Zoom 동작 저장
6465
const currentTransformRef = useRef(d3.zoomIdentity); // 현재 Transform 저장
@@ -88,36 +89,46 @@ const RecursionAnimation = ({ data, currentStep, theme }) => {
8889
}, [data, currentStep]);
8990

9091
useEffect(() => {
91-
const svg = d3.select(svgRef.current);
92-
svg.selectAll('*').remove();
92+
const svgElement = svgRef.current;
93+
if (!svgElement) return;
9394

94-
const { root } = memoizedTreeData;
95-
const { activeStack, completedSet } = currentExecutionState;
95+
const svg = d3.select(svgElement);
9696

97-
if (!root) {
98-
return;
97+
if (!gRef.current) {
98+
gRef.current = svg.append('g');
9999
}
100100

101-
const width = svgRef.current?.clientWidth || 800;
102-
const height = svgRef.current?.clientHeight || 600;
101+
const g = gRef.current;
103102

104-
const g = svg.append('g');
103+
if (!zoomBehaviorRef.current) {
104+
const zoom = d3.zoom()
105+
.scaleExtent([0.3, 3])
106+
.on('zoom', (event) => {
107+
g.attr('transform', event.transform);
108+
currentTransformRef.current = event.transform; // Transform 저장
109+
setZoomLevel(Math.round(event.transform.k * 100));
110+
});
105111

106-
// Zoom behavior 설정
107-
const zoom = d3.zoom()
108-
.scaleExtent([0.3, 3])
109-
.on('zoom', (event) => {
110-
g.attr('transform', event.transform);
111-
currentTransformRef.current = event.transform; // Transform 저장
112-
setZoomLevel(Math.round(event.transform.k * 100));
113-
});
112+
zoomBehaviorRef.current = zoom;
113+
svg.call(zoom);
114+
}
114115

115-
zoomBehaviorRef.current = zoom;
116+
g.attr('transform', currentTransformRef.current);
117+
g.selectAll('*').remove();
116118

117-
svg.call(zoom);
119+
if (zoomBehaviorRef.current) {
120+
svg.call(zoomBehaviorRef.current.transform, currentTransformRef.current);
121+
}
118122

119-
// 저장된 Transform 복원
120-
svg.call(zoom.transform, currentTransformRef.current);
123+
const { root } = memoizedTreeData;
124+
const { activeStack, completedSet } = currentExecutionState;
125+
126+
if (!root) {
127+
return;
128+
}
129+
130+
const width = svgElement?.clientWidth || 800;
131+
const height = svgElement?.clientHeight || 600;
121132

122133
const hierarchy = d3.hierarchy(root, d => d.children);
123134
const treeLayout = d3.tree().size([width - 150, height - 200]);
@@ -200,6 +211,14 @@ const RecursionAnimation = ({ data, currentStep, theme }) => {
200211

201212
}, [memoizedTreeData, currentExecutionState, theme]);
202213

214+
useEffect(() => () => {
215+
const svg = d3.select(svgRef.current);
216+
svg.selectAll('*').remove();
217+
gRef.current = null;
218+
zoomBehaviorRef.current = null;
219+
currentTransformRef.current = d3.zoomIdentity;
220+
}, []);
221+
203222
const handleZoom = (scale) => {
204223
const svg = d3.select(svgRef.current);
205224
svg.transition().call(zoomBehaviorRef.current.scaleBy, scale);

0 commit comments

Comments
 (0)