Skip to content

Commit

Permalink
excalidraw PR excalidraw#8267 fix labeled arrows cache
Browse files Browse the repository at this point in the history
  • Loading branch information
zsviczian committed Jul 22, 2024
1 parent d04c461 commit cb47c74
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 73 deletions.
1 change: 1 addition & 0 deletions packages/excalidraw/components/canvases/StaticCanvas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ const getRelevantAppStateProps = (
selectedElementIds: appState.selectedElementIds,
frameToHighlight: appState.frameToHighlight,
editingGroupId: appState.editingGroupId,
isRotating: appState.isRotating,
});

const areEqual = (
Expand Down
2 changes: 1 addition & 1 deletion packages/excalidraw/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@zsviczian/excalidraw",
"version": "0.17.1-obsidian-31",
"version": "0.17.1-obsidian-32",
"main": "main.js",
"types": "types/excalidraw/index.d.ts",
"files": [
Expand Down
153 changes: 81 additions & 72 deletions packages/excalidraw/renderer/renderElement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ export interface ExcalidrawElementWithCanvas {
canvasOffsetY: number;
boundTextElementVersion: number | null;
containingFrameOpacity: number;
boundTextCanvas: HTMLCanvasElement;
}

const cappedElementCanvasSize = (
Expand Down Expand Up @@ -184,7 +185,7 @@ const cappedElementCanvasSize = (

const generateElementCanvas = (
element: NonDeletedExcalidrawElement,
elementsMap: RenderableElementsMap,
elementsMap: NonDeletedSceneElementsMap,
zoom: Zoom,
renderConfig: StaticCanvasRenderConfig,
appState: StaticCanvasAppState,
Expand Down Expand Up @@ -237,7 +238,67 @@ const generateElementCanvas = (

drawElementOnCanvas(element, rc, context, renderConfig, appState);
context.restore();
const boundTextElement = getBoundTextElement(element, elementsMap);
const boundTextCanvas = document.createElement("canvas");
const boundTextCanvasContext = boundTextCanvas.getContext("2d")!;
if (isArrowElement(element) && boundTextElement) {
const [x1, y1, x2, y2] = getElementAbsoluteCoords(element, elementsMap);
// Take max dimensions of arrow canvas so that when canvas is rotated
// the arrow doesn't get clipped
const maxDim = Math.max(distance(x1, x2), distance(y1, y2));
boundTextCanvas.width =
maxDim * window.devicePixelRatio * scale + padding * scale * 10;
boundTextCanvas.height =
maxDim * window.devicePixelRatio * scale + padding * scale * 10;
boundTextCanvasContext.translate(
boundTextCanvas.width / 2,
boundTextCanvas.height / 2,
);
boundTextCanvasContext.rotate(element.angle);
boundTextCanvasContext.drawImage(
canvas!,
-canvas.width / 2,
-canvas.height / 2,
canvas.width,
canvas.height,
);

const [, , , , boundTextCx, boundTextCy] = getElementAbsoluteCoords(
boundTextElement,
elementsMap,
);

boundTextCanvasContext.rotate(-element.angle);
const offsetX = (boundTextCanvas.width - canvas!.width) / 2;
const offsetY = (boundTextCanvas.height - canvas!.height) / 2;
const shiftX =
boundTextCanvas.width / 2 -
(boundTextCx - x1) * window.devicePixelRatio * scale -
offsetX -
padding * scale;

const shiftY =
boundTextCanvas.height / 2 -
(boundTextCy - y1) * window.devicePixelRatio * scale -
offsetY -
padding * scale;
boundTextCanvasContext.translate(-shiftX, -shiftY);
// Clear the bound text area
boundTextCanvasContext.clearRect(
-(boundTextElement.width / 2 + BOUND_TEXT_PADDING) *
window.devicePixelRatio *
scale,
-(boundTextElement.height / 2 + BOUND_TEXT_PADDING) *
window.devicePixelRatio *
scale,
(boundTextElement.width + BOUND_TEXT_PADDING * 2) *
window.devicePixelRatio *
scale,
(boundTextElement.height + BOUND_TEXT_PADDING * 2) *
window.devicePixelRatio *
scale,
);
}
return {
element,
canvas,
Expand All @@ -250,6 +311,7 @@ const generateElementCanvas = (
getBoundTextElement(element, elementsMap)?.version || null,
containingFrameOpacity:
getContainingFrame(element, elementsMap)?.opacity || 100,
boundTextCanvas,
};
};

Expand Down Expand Up @@ -435,7 +497,7 @@ export const elementWithCanvasCache = new WeakMap<

const generateElementWithCanvas = (
element: NonDeletedExcalidrawElement,
elementsMap: RenderableElementsMap,
elementsMap: NonDeletedSceneElementsMap,
renderConfig: StaticCanvasRenderConfig,
appState: StaticCanvasAppState,
) => {
Expand All @@ -445,8 +507,8 @@ const generateElementWithCanvas = (
prevElementWithCanvas &&
prevElementWithCanvas.zoomValue !== zoom.value &&
!appState?.shouldCacheIgnoreZoom;
const boundTextElementVersion =
getBoundTextElement(element, elementsMap)?.version || null;
const boundTextElement = getBoundTextElement(element, elementsMap);
const boundTextElementVersion = boundTextElement?.version || null;

const containingFrameOpacity =
getContainingFrame(element, elementsMap)?.opacity || 100;
Expand All @@ -456,7 +518,8 @@ const generateElementWithCanvas = (
shouldRegenerateBecauseZoom ||
prevElementWithCanvas.theme !== appState.theme ||
prevElementWithCanvas.boundTextElementVersion !== boundTextElementVersion ||
prevElementWithCanvas.containingFrameOpacity !== containingFrameOpacity
prevElementWithCanvas.containingFrameOpacity !== containingFrameOpacity ||
(isArrowElement(element) && boundTextElement && appState.isRotating)
) {
const elementWithCanvas = generateElementCanvas(
element,
Expand Down Expand Up @@ -493,75 +556,21 @@ const drawElementFromCanvas = (
const boundTextElement = getBoundTextElement(element, allElementsMap);

if (isArrowElement(element) && boundTextElement) {
const tempCanvas = document.createElement("canvas");
const tempCanvasContext = tempCanvas.getContext("2d")!;

// Take max dimensions of arrow canvas so that when canvas is rotated
// the arrow doesn't get clipped
const maxDim = Math.max(distance(x1, x2), distance(y1, y2));
tempCanvas.width =
maxDim * window.devicePixelRatio * zoom +
padding * elementWithCanvas.scale * 10;
tempCanvas.height =
maxDim * window.devicePixelRatio * zoom +
padding * elementWithCanvas.scale * 10;
const offsetX = (tempCanvas.width - elementWithCanvas.canvas!.width) / 2;
const offsetY = (tempCanvas.height - elementWithCanvas.canvas!.height) / 2;

tempCanvasContext.translate(tempCanvas.width / 2, tempCanvas.height / 2);
tempCanvasContext.rotate(element.angle);

tempCanvasContext.drawImage(
elementWithCanvas.canvas!,
-elementWithCanvas.canvas.width / 2,
-elementWithCanvas.canvas.height / 2,
elementWithCanvas.canvas.width,
elementWithCanvas.canvas.height,
);

const [, , , , boundTextCx, boundTextCy] = getElementAbsoluteCoords(
boundTextElement,
allElementsMap,
);

tempCanvasContext.rotate(-element.angle);

// Shift the canvas to the center of the bound text element
const shiftX =
tempCanvas.width / 2 -
(boundTextCx - x1) * window.devicePixelRatio * zoom -
offsetX -
padding * zoom;

const shiftY =
tempCanvas.height / 2 -
(boundTextCy - y1) * window.devicePixelRatio * zoom -
offsetY -
padding * zoom;
tempCanvasContext.translate(-shiftX, -shiftY);
// Clear the bound text area
tempCanvasContext.clearRect(
-(boundTextElement.width / 2 + BOUND_TEXT_PADDING) *
window.devicePixelRatio *
zoom,
-(boundTextElement.height / 2 + BOUND_TEXT_PADDING) *
window.devicePixelRatio *
zoom,
(boundTextElement.width + BOUND_TEXT_PADDING * 2) *
window.devicePixelRatio *
zoom,
(boundTextElement.height + BOUND_TEXT_PADDING * 2) *
window.devicePixelRatio *
zoom,
);

const offsetX =
(elementWithCanvas.boundTextCanvas.width -
elementWithCanvas.canvas!.width) /
2;
const offsetY =
(elementWithCanvas.boundTextCanvas.height -
elementWithCanvas.canvas!.height) /
2;
context.translate(cx, cy);
context.drawImage(
tempCanvas,
elementWithCanvas.boundTextCanvas,
(-(x2 - x1) / 2) * window.devicePixelRatio - offsetX / zoom - padding,
(-(y2 - y1) / 2) * window.devicePixelRatio - offsetY / zoom - padding,
tempCanvas.width / zoom,
tempCanvas.height / zoom,
elementWithCanvas.boundTextCanvas.width / zoom,
elementWithCanvas.boundTextCanvas.height / zoom,
);
} else {
// we translate context to element center so that rotation and scale
Expand Down Expand Up @@ -724,7 +733,7 @@ export const renderElement = (
} else {
const elementWithCanvas = generateElementWithCanvas(
element,
elementsMap,
allElementsMap,
renderConfig,
appState,
);
Expand Down Expand Up @@ -862,7 +871,7 @@ export const renderElement = (
} else {
const elementWithCanvas = generateElementWithCanvas(
element,
elementsMap,
allElementsMap,
renderConfig,
appState,
);
Expand Down
1 change: 1 addition & 0 deletions packages/excalidraw/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ export type StaticCanvasAppState = Readonly<
selectedElementsAreBeingDragged: AppState["selectedElementsAreBeingDragged"];
gridSize: AppState["gridSize"];
frameRendering: AppState["frameRendering"];
isRotating: AppState["isRotating"];
linkOpacity: AppState["linkOpacity"]; //zsviczian
gridColor: AppState["gridColor"]; //zsviczian
frameColor: AppState["frameColor"]; //zsviczian
Expand Down

0 comments on commit cb47c74

Please sign in to comment.