mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-04-14 16:40:58 -04:00
fix: introduce baseline to fix the layout shift when switching to text editor
This commit is contained in:
parent
83383977f5
commit
fc80fd15dc
3 changed files with 85 additions and 4 deletions
|
@ -289,6 +289,43 @@ export const measureText = (
|
||||||
return { width, height };
|
return { width, height };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const measureBaseline = (
|
||||||
|
text: string,
|
||||||
|
font: FontString,
|
||||||
|
lineHeight: ExcalidrawTextElement["lineHeight"],
|
||||||
|
wrapInContainer?: boolean,
|
||||||
|
) => {
|
||||||
|
const container = document.createElement("div");
|
||||||
|
container.style.position = "absolute";
|
||||||
|
container.style.whiteSpace = "pre";
|
||||||
|
container.style.font = font;
|
||||||
|
container.style.minHeight = "1em";
|
||||||
|
|
||||||
|
if (wrapInContainer) {
|
||||||
|
container.style.overflow = "hidden";
|
||||||
|
container.style.wordBreak = "break-word";
|
||||||
|
container.style.whiteSpace = "pre-wrap";
|
||||||
|
}
|
||||||
|
|
||||||
|
//@ts-ignore
|
||||||
|
container.style.lineHeight = lineHeight;
|
||||||
|
|
||||||
|
container.innerText = text;
|
||||||
|
|
||||||
|
// Baseline is important for positioning text on canvas
|
||||||
|
document.body.appendChild(container);
|
||||||
|
|
||||||
|
const span = document.createElement("span");
|
||||||
|
span.style.display = "inline-block";
|
||||||
|
span.style.overflow = "hidden";
|
||||||
|
span.style.width = "1px";
|
||||||
|
span.style.height = "1px";
|
||||||
|
container.appendChild(span);
|
||||||
|
const baseline = span.offsetTop + span.offsetHeight;
|
||||||
|
//document.body.removeChild(container);
|
||||||
|
return baseline;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* To get unitless line-height (if unknown) we can calculate it by dividing
|
* To get unitless line-height (if unknown) we can calculate it by dividing
|
||||||
* height-per-line by fontSize.
|
* height-per-line by fontSize.
|
||||||
|
|
|
@ -34,6 +34,7 @@ import {
|
||||||
wrapText,
|
wrapText,
|
||||||
getMaxContainerHeight,
|
getMaxContainerHeight,
|
||||||
getMaxContainerWidth,
|
getMaxContainerWidth,
|
||||||
|
measureBaseline,
|
||||||
} from "./textElement";
|
} from "./textElement";
|
||||||
import {
|
import {
|
||||||
actionDecreaseFontSize,
|
actionDecreaseFontSize,
|
||||||
|
@ -269,6 +270,17 @@ export const textWysiwyg = ({
|
||||||
} else {
|
} else {
|
||||||
textElementWidth += 0.5;
|
textElementWidth += 0.5;
|
||||||
}
|
}
|
||||||
|
const baseline = measureBaseline(
|
||||||
|
updatedTextElement.text,
|
||||||
|
getFontString(updatedTextElement),
|
||||||
|
updatedTextElement.lineHeight,
|
||||||
|
!!container,
|
||||||
|
);
|
||||||
|
|
||||||
|
const offset =
|
||||||
|
(updatedTextElement.height - baseline - 10) * appState.zoom.value;
|
||||||
|
|
||||||
|
const top = viewportY + offset;
|
||||||
// Make sure text editor height doesn't go beyond viewport
|
// Make sure text editor height doesn't go beyond viewport
|
||||||
const editorMaxHeight =
|
const editorMaxHeight =
|
||||||
(appState.height - viewportY) / appState.zoom.value;
|
(appState.height - viewportY) / appState.zoom.value;
|
||||||
|
@ -279,7 +291,7 @@ export const textWysiwyg = ({
|
||||||
width: `${textElementWidth}px`,
|
width: `${textElementWidth}px`,
|
||||||
height: `${textElementHeight}px`,
|
height: `${textElementHeight}px`,
|
||||||
left: `${viewportX}px`,
|
left: `${viewportX}px`,
|
||||||
top: `${viewportY}px`,
|
top: `${top}px`,
|
||||||
transform: getTransform(
|
transform: getTransform(
|
||||||
textElementWidth,
|
textElementWidth,
|
||||||
textElementHeight,
|
textElementHeight,
|
||||||
|
|
|
@ -200,6 +200,16 @@ const drawImagePlaceholder = (
|
||||||
size,
|
size,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
//@ts-ignore
|
||||||
|
const drawLine = (x, y, width, height, stroke, context) => {
|
||||||
|
context.lineWidth = "2";
|
||||||
|
context.strokeStyle = stroke;
|
||||||
|
context.beginPath();
|
||||||
|
context.moveTo(x, y);
|
||||||
|
context.lineTo(x + width, y);
|
||||||
|
context.closePath();
|
||||||
|
context.stroke();
|
||||||
|
};
|
||||||
|
|
||||||
const drawElementOnCanvas = (
|
const drawElementOnCanvas = (
|
||||||
element: NonDeletedExcalidrawElement,
|
element: NonDeletedExcalidrawElement,
|
||||||
|
@ -274,6 +284,29 @@ const drawElementOnCanvas = (
|
||||||
context.canvas.setAttribute("dir", rtl ? "rtl" : "ltr");
|
context.canvas.setAttribute("dir", rtl ? "rtl" : "ltr");
|
||||||
context.save();
|
context.save();
|
||||||
context.font = getFontString(element);
|
context.font = getFontString(element);
|
||||||
|
context.textBaseline = "alphabetic";
|
||||||
|
const metrics = context.measureText(element.text);
|
||||||
|
|
||||||
|
// drawLine(0, 0, metrics.width, element.height, "green", context);
|
||||||
|
|
||||||
|
// drawLine(
|
||||||
|
// 0,
|
||||||
|
// -metrics.actualBoundingBoxAscent,
|
||||||
|
// metrics.width,
|
||||||
|
// element.height,
|
||||||
|
// "magenta",
|
||||||
|
// context,
|
||||||
|
// );
|
||||||
|
|
||||||
|
// drawLine(
|
||||||
|
// 0,
|
||||||
|
// metrics.actualBoundingBoxDescent,
|
||||||
|
// metrics.width,
|
||||||
|
// element.height,
|
||||||
|
// "magenta",
|
||||||
|
// context,
|
||||||
|
// );
|
||||||
|
|
||||||
context.fillStyle = element.strokeColor;
|
context.fillStyle = element.strokeColor;
|
||||||
context.textAlign = element.textAlign as CanvasTextAlign;
|
context.textAlign = element.textAlign as CanvasTextAlign;
|
||||||
|
|
||||||
|
@ -286,18 +319,17 @@ const drawElementOnCanvas = (
|
||||||
: element.textAlign === "right"
|
: element.textAlign === "right"
|
||||||
? element.width
|
? element.width
|
||||||
: 0;
|
: 0;
|
||||||
context.textBaseline = "bottom";
|
|
||||||
|
|
||||||
const lineHeightPx = getLineHeightInPx(
|
const lineHeightPx = getLineHeightInPx(
|
||||||
element.fontSize,
|
element.fontSize,
|
||||||
element.lineHeight,
|
element.lineHeight,
|
||||||
);
|
);
|
||||||
|
const verticalOffset = 10;
|
||||||
|
|
||||||
for (let index = 0; index < lines.length; index++) {
|
for (let index = 0; index < lines.length; index++) {
|
||||||
context.fillText(
|
context.fillText(
|
||||||
lines[index],
|
lines[index],
|
||||||
horizontalOffset,
|
horizontalOffset,
|
||||||
(index + 1) * lineHeightPx,
|
(index + 1) * lineHeightPx - verticalOffset,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
context.restore();
|
context.restore();
|
||||||
|
|
Loading…
Add table
Reference in a new issue