@@ -59,6 +59,7 @@ function buildRecursionTree(events) {
5959
6060const 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