Skip to content

Commit 15f131f

Browse files
committed
more performance improvements
1 parent d95900c commit 15f131f

File tree

1 file changed

+68
-84
lines changed

1 file changed

+68
-84
lines changed

src/frontend/components/TrackMap/TrackCanvas.tsx

Lines changed: 68 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useEffect, useMemo, useRef } from 'react';
1+
import { useEffect, useMemo, useRef, useState } from 'react';
22
import { Driver } from '@irdashies/types';
33
import tracks from './tracks/tracks.json';
44
import { getColor, getTailwindStyle } from '@irdashies/utils/colors';
@@ -44,7 +44,7 @@ const TRACK_DRAWING_HEIGHT = 1080;
4444

4545
export const TrackCanvas = ({ trackId, drivers }: TrackProps) => {
4646
const canvasRef = useRef<HTMLCanvasElement>(null);
47-
const animationFrameIdRef = useRef<number | null>(null);
47+
const [canvasSize, setCanvasSize] = useState({ width: 0, height: 0 });
4848

4949
const trackDrawing = (tracks as unknown as TrackDrawing[])[trackId];
5050
const shouldShow = shouldShowTrack(trackId, trackDrawing);
@@ -158,6 +158,8 @@ export const TrackCanvas = ({ trackId, drivers }: TrackProps) => {
158158
ctx.setTransform(1, 0, 0, 1, 0, 0); // Reset transform
159159
ctx.scale(dpr, dpr); // Apply DPR scaling
160160
}
161+
// Update state to trigger redraw
162+
setCanvasSize({ width: rect.width, height: rect.height });
161163
};
162164

163165
// Initial resize
@@ -182,98 +184,80 @@ export const TrackCanvas = ({ trackId, drivers }: TrackProps) => {
182184
const ctx = canvas?.getContext('2d');
183185
if (!canvas || !ctx || !path2DObjects) return;
184186

185-
const draw = () => {
186-
const rect = canvas.getBoundingClientRect();
187-
188-
// Clear the entire canvas
189-
ctx.clearRect(0, 0, canvas.width, canvas.height);
190-
191-
// Calculate scale to fit the 1920x1080 track into the current canvas size
192-
const scaleX = rect.width / TRACK_DRAWING_WIDTH;
193-
const scaleY = rect.height / TRACK_DRAWING_HEIGHT;
194-
const scale = Math.min(scaleX, scaleY); // Maintain aspect ratio
195-
196-
// Calculate centering offset
197-
const offsetX = (rect.width - TRACK_DRAWING_WIDTH * scale) / 2;
198-
const offsetY = (rect.height - TRACK_DRAWING_HEIGHT * scale) / 2;
187+
const rect = canvas.getBoundingClientRect();
199188

200-
// Save context state
201-
ctx.save();
189+
// Clear the entire canvas
190+
ctx.clearRect(0, 0, canvas.width, canvas.height);
202191

203-
// Apply scaling and centering
204-
ctx.translate(offsetX, offsetY);
205-
ctx.scale(scale, scale);
192+
// Calculate scale to fit the 1920x1080 track into the current canvas size
193+
const scaleX = rect.width / TRACK_DRAWING_WIDTH;
194+
const scaleY = rect.height / TRACK_DRAWING_HEIGHT;
195+
const scale = Math.min(scaleX, scaleY); // Maintain aspect ratio
206196

207-
// Shadow
208-
ctx.shadowColor = 'black';
209-
ctx.shadowBlur = 2;
210-
ctx.shadowOffsetX = 1;
211-
ctx.shadowOffsetY = 1;
197+
// Calculate centering offset
198+
const offsetX = (rect.width - TRACK_DRAWING_WIDTH * scale) / 2;
199+
const offsetY = (rect.height - TRACK_DRAWING_HEIGHT * scale) / 2;
212200

213-
// Draw track
214-
if (path2DObjects.inside) {
215-
ctx.strokeStyle = 'white';
216-
ctx.lineWidth = 20;
217-
ctx.stroke(path2DObjects.inside);
218-
}
201+
// Save context state
202+
ctx.save();
219203

220-
// Draw start/finish line
221-
if (path2DObjects.startFinish) {
222-
ctx.lineWidth = 10;
223-
ctx.strokeStyle = getColor('red');
224-
ctx.stroke(path2DObjects.startFinish);
225-
}
204+
// Apply scaling and centering
205+
ctx.translate(offsetX, offsetY);
206+
ctx.scale(scale, scale);
226207

227-
// Draw turn numbers
228-
if (ENABLE_TURNS) {
229-
trackDrawing.turns?.forEach((turn) => {
230-
if (!turn.content || !turn.x || !turn.y) return;
231-
ctx.textAlign = 'center';
232-
ctx.textBaseline = 'middle';
233-
ctx.fillStyle = 'white';
234-
ctx.font = '2rem sans-serif';
235-
ctx.fillText(turn.content, turn.x, turn.y);
236-
});
237-
}
208+
// Shadow
209+
ctx.shadowColor = 'black';
210+
ctx.shadowBlur = 2;
211+
ctx.shadowOffsetX = 1;
212+
ctx.shadowOffsetY = 1;
238213

239-
// Draw drivers
240-
Object.values(calculatePositions)
241-
.sort((a, b) => Number(a.isPlayer) - Number(b.isPlayer)) // draws player last to be on top
242-
.forEach(({ driver, position }) => {
243-
const color = driverColors[driver.CarIdx];
244-
if (!color) return;
245-
246-
ctx.fillStyle = color.fill;
247-
ctx.beginPath();
248-
ctx.arc(position.x, position.y, 40, 0, 2 * Math.PI);
249-
ctx.fill();
250-
ctx.textAlign = 'center';
251-
ctx.textBaseline = 'middle';
252-
ctx.fillStyle = color.text;
253-
ctx.font = '2rem sans-serif';
254-
ctx.fillText(driver.CarNumber, position.x, position.y);
255-
});
256-
257-
// Restore context state
258-
ctx.restore();
259-
};
214+
// Draw track
215+
if (path2DObjects.inside) {
216+
ctx.strokeStyle = 'white';
217+
ctx.lineWidth = 20;
218+
ctx.stroke(path2DObjects.inside);
219+
}
260220

261-
// Simple animation loop - just redraw when needed
262-
const animate = () => {
263-
draw();
264-
animationFrameIdRef.current = requestAnimationFrame(animate);
265-
};
221+
// Draw start/finish line
222+
if (path2DObjects.startFinish) {
223+
ctx.lineWidth = 10;
224+
ctx.strokeStyle = getColor('red');
225+
ctx.stroke(path2DObjects.startFinish);
226+
}
266227

267-
// Start animation
268-
animate();
228+
// Draw turn numbers
229+
if (ENABLE_TURNS) {
230+
trackDrawing.turns?.forEach((turn) => {
231+
if (!turn.content || !turn.x || !turn.y) return;
232+
ctx.textAlign = 'center';
233+
ctx.textBaseline = 'middle';
234+
ctx.fillStyle = 'white';
235+
ctx.font = '2rem sans-serif';
236+
ctx.fillText(turn.content, turn.x, turn.y);
237+
});
238+
}
269239

270-
// Cleanup on component unmount
271-
return () => {
272-
if (animationFrameIdRef.current) {
273-
cancelAnimationFrame(animationFrameIdRef.current);
274-
}
275-
};
276-
}, [calculatePositions, path2DObjects, trackDrawing?.turns, driverColors]);
240+
// Draw drivers
241+
Object.values(calculatePositions)
242+
.sort((a, b) => Number(a.isPlayer) - Number(b.isPlayer)) // draws player last to be on top
243+
.forEach(({ driver, position }) => {
244+
const color = driverColors[driver.CarIdx];
245+
if (!color) return;
246+
247+
ctx.fillStyle = color.fill;
248+
ctx.beginPath();
249+
ctx.arc(position.x, position.y, 40, 0, 2 * Math.PI);
250+
ctx.fill();
251+
ctx.textAlign = 'center';
252+
ctx.textBaseline = 'middle';
253+
ctx.fillStyle = color.text;
254+
ctx.font = '2rem sans-serif';
255+
ctx.fillText(driver.CarNumber, position.x, position.y);
256+
});
257+
258+
// Restore context state
259+
ctx.restore();
260+
}, [calculatePositions, path2DObjects, trackDrawing?.turns, driverColors, canvasSize]);
277261

278262
// Development/Storybook mode - show debug info and canvas
279263
if (import.meta.env?.DEV || import.meta.env?.MODE === 'storybook') {

0 commit comments

Comments
 (0)