Tldraw/packages/tldraw/src/lib/ui/components/DebugPanel.tsx

129 wiersze
3.4 KiB
TypeScript

import { debugFlags, track, useEditor, useValue, Vec } from '@tldraw/editor'
import { memo, useEffect, useRef, useState } from 'react'
import { useTldrawUiComponents } from '../context/components'
/** @internal */
export const DebugPanel = memo(function DebugPanel() {
const { DebugMenu } = useTldrawUiComponents()
return (
<div className="tlui-debug-panel">
<CurrentState />
<FPS />
{DebugMenu && <DebugMenu />}
</div>
)
})
function useTick(isEnabled = true) {
const [_, setTick] = useState(0)
const editor = useEditor()
useEffect(() => {
if (!isEnabled) return
const update = () => setTick((tick) => tick + 1)
editor.on('tick', update)
return () => {
editor.off('tick', update)
}
}, [editor, isEnabled])
}
const CurrentState = track(function CurrentState() {
useTick()
const editor = useEditor()
const path = editor.getPath()
const hoverShape = editor.getHoveredShape()
const selectedShape = editor.getOnlySelectedShape()
const shape = path === 'select.idle' || !path.includes('select.') ? hoverShape : selectedShape
const shapeInfo =
shape && path.includes('select.')
? ` / ${shape.type || ''}${
'geo' in shape.props ? ' / ' + shape.props.geo : ''
} / [${Vec.ToFixed(editor.getPointInShapeSpace(shape, editor.inputs.currentPagePoint), 0)}]`
: ''
const ruler =
path.startsWith('select.') && !path.includes('.idle')
? ` / [${Vec.ToFixed(editor.inputs.originPagePoint, 0)}] → [${Vec.ToFixed(
editor.inputs.currentPagePoint,
0
)}] = ${Vec.Dist(editor.inputs.originPagePoint, editor.inputs.currentPagePoint).toFixed(0)}`
: ''
return <div className="tlui-debug-panel__current-state">{`${path}${shapeInfo}${ruler}`}</div>
})
function FPS() {
const showFps = useValue('show_fps', () => debugFlags.showFps.get(), [debugFlags])
const fpsRef = useRef<HTMLDivElement>(null)
useEffect(() => {
if (!showFps) return
const TICK_LENGTH = 250
let maxKnownFps = 0
let cancelled = false
let start = performance.now()
let currentTickLength = 0
let framesInCurrentTick = 0
let isSlow = false
// A "tick" is the amount of time between renders. Even though
// we'll loop on every frame, we will only paint when the time
// since the last paint is greater than the tick length.
// When we paint, we'll calculate the FPS based on the number
// of frames that we've seen since the last time we rendered,
// and the actual time since the last render.
function loop() {
if (cancelled) return
// Count the frame
framesInCurrentTick++
// Check if we should render
currentTickLength = performance.now() - start
if (currentTickLength > TICK_LENGTH) {
// Calculate the FPS and paint it
const fps = Math.round(
framesInCurrentTick * (TICK_LENGTH / currentTickLength) * (1000 / TICK_LENGTH)
)
if (fps > maxKnownFps) {
maxKnownFps = fps
}
const slowFps = maxKnownFps * 0.75
if ((fps < slowFps && !isSlow) || (fps >= slowFps && isSlow)) {
isSlow = !isSlow
}
fpsRef.current!.innerHTML = `FPS ${fps.toString()}`
fpsRef.current!.className =
`tlui-debug-panel__fps` + (isSlow ? ` tlui-debug-panel__fps__slow` : ``)
// Reset the values
currentTickLength -= TICK_LENGTH
framesInCurrentTick = 0
start = performance.now()
}
requestAnimationFrame(loop)
}
loop()
return () => {
cancelled = true
}
}, [showFps])
if (!showFps) return null
return <div ref={fpsRef} />
}