Optimize line metrics computation in App.tsx
This commit is contained in:
68
src/App.tsx
68
src/App.tsx
@@ -470,55 +470,53 @@ export default function App() {
|
|||||||
}
|
}
|
||||||
}, [defaultLineHeight, measureTick, showLineNumbersActive, sidebarCollapsed, historyOpen, textSize]);
|
}, [defaultLineHeight, measureTick, showLineNumbersActive, sidebarCollapsed, historyOpen, textSize]);
|
||||||
|
|
||||||
useEffect(() => {
|
const computeLineMetrics = useCallback(() => {
|
||||||
if (!showLineNumbersActive) return;
|
|
||||||
fenwickRef.current.reset(lineCount);
|
|
||||||
lineHeightsRef.current = new Array(lineCount).fill(0);
|
|
||||||
setLineNumbersVersion((version) => version + 1);
|
|
||||||
updateVisibleRange();
|
|
||||||
}, [defaultLineHeight, lineCount, measureTick, showLineNumbersActive, textSize, updateVisibleRange]);
|
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
|
||||||
if (!showLineNumbersActive || !defaultLineHeight) return;
|
if (!showLineNumbersActive || !defaultLineHeight) return;
|
||||||
const textarea = textareaRef.current;
|
const textarea = textareaRef.current;
|
||||||
const measureContainer = measureRef.current;
|
if (!textarea) return;
|
||||||
const measureLine = measureLineRef.current;
|
const styles = window.getComputedStyle(textarea);
|
||||||
if (!textarea || !measureContainer || !measureLine) return;
|
const paddingLeft = parseFloat(styles.paddingLeft) || 0;
|
||||||
measureContainer.style.width = `${textarea.clientWidth}px`;
|
const paddingRight = parseFloat(styles.paddingRight) || 0;
|
||||||
if (visibleRange.end < visibleRange.start) return;
|
const contentWidth = Math.max(1, textarea.clientWidth - paddingLeft - paddingRight);
|
||||||
let changed = false;
|
let canvas = measureCanvasRef.current;
|
||||||
for (let i = visibleRange.start; i <= visibleRange.end; i += 1) {
|
if (!canvas) {
|
||||||
const lineText = lines[i] ?? "";
|
canvas = document.createElement("canvas");
|
||||||
measureLine.textContent = lineText.length > 0 ? lineText : " ";
|
measureCanvasRef.current = canvas;
|
||||||
const height = Math.ceil(measureLine.getBoundingClientRect().height);
|
|
||||||
const prev = lineHeightsRef.current[i] || 0;
|
|
||||||
const base = prev || defaultLineHeight;
|
|
||||||
if (height > 0 && height !== prev) {
|
|
||||||
lineHeightsRef.current[i] = height;
|
|
||||||
fenwickRef.current.add(i, height - base);
|
|
||||||
changed = true;
|
|
||||||
}
|
}
|
||||||
|
const context = canvas.getContext("2d");
|
||||||
|
if (!context) return;
|
||||||
|
context.font = styles.font;
|
||||||
|
const charWidth = context.measureText("M").width || 1;
|
||||||
|
const columns = Math.max(1, Math.floor(contentWidth / charWidth));
|
||||||
|
const heights = new Array(lineCount);
|
||||||
|
const tops = new Array(lineCount + 1);
|
||||||
|
tops[0] = 0;
|
||||||
|
for (let i = 0; i < lineCount; i += 1) {
|
||||||
|
const length = lines[i]?.length ?? 0;
|
||||||
|
const wraps = Math.max(1, Math.ceil(length / columns));
|
||||||
|
const height = wraps * defaultLineHeight;
|
||||||
|
heights[i] = height;
|
||||||
|
tops[i + 1] = tops[i] + height;
|
||||||
}
|
}
|
||||||
if (changed) {
|
lineHeightsRef.current = heights;
|
||||||
|
lineTopsRef.current = tops;
|
||||||
setLineNumbersVersion((version) => version + 1);
|
setLineNumbersVersion((version) => version + 1);
|
||||||
scheduleUpdateVisibleRange();
|
updateVisibleRange();
|
||||||
}
|
|
||||||
if (lineNumbersRef.current) {
|
if (lineNumbersRef.current) {
|
||||||
lineNumbersRef.current.scrollTop = textarea.scrollTop;
|
lineNumbersRef.current.scrollTop = textarea.scrollTop;
|
||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
defaultLineHeight,
|
defaultLineHeight,
|
||||||
|
lineCount,
|
||||||
lines,
|
lines,
|
||||||
measureTick,
|
|
||||||
scheduleUpdateVisibleRange,
|
|
||||||
showLineNumbersActive,
|
showLineNumbersActive,
|
||||||
sidebarCollapsed,
|
updateVisibleRange
|
||||||
historyOpen,
|
|
||||||
textSize,
|
|
||||||
visibleRange.end,
|
|
||||||
visibleRange.start
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
computeLineMetrics();
|
||||||
|
}, [computeLineMetrics, measureTick, sidebarCollapsed, historyOpen, textSize]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (showLineNumbersActive && textareaRef.current && lineNumbersRef.current) {
|
if (showLineNumbersActive && textareaRef.current && lineNumbersRef.current) {
|
||||||
lineNumbersRef.current.scrollTop = textareaRef.current.scrollTop;
|
lineNumbersRef.current.scrollTop = textareaRef.current.scrollTop;
|
||||||
|
|||||||
Reference in New Issue
Block a user