mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-11-17 11:14:23 +01:00
fix: New highlight overdraws arrow
Signed-off-by: Mark Tolmacs <mark@lazycat.hu>
This commit is contained in:
@@ -90,7 +90,7 @@ const isPendingImageElement = (
|
|||||||
const shouldResetImageFilter = (
|
const shouldResetImageFilter = (
|
||||||
element: ExcalidrawElement,
|
element: ExcalidrawElement,
|
||||||
renderConfig: StaticCanvasRenderConfig,
|
renderConfig: StaticCanvasRenderConfig,
|
||||||
appState: StaticCanvasAppState,
|
appState: StaticCanvasAppState | InteractiveCanvasAppState,
|
||||||
) => {
|
) => {
|
||||||
return (
|
return (
|
||||||
appState.theme === THEME.DARK &&
|
appState.theme === THEME.DARK &&
|
||||||
@@ -217,7 +217,7 @@ const generateElementCanvas = (
|
|||||||
elementsMap: NonDeletedSceneElementsMap,
|
elementsMap: NonDeletedSceneElementsMap,
|
||||||
zoom: Zoom,
|
zoom: Zoom,
|
||||||
renderConfig: StaticCanvasRenderConfig,
|
renderConfig: StaticCanvasRenderConfig,
|
||||||
appState: StaticCanvasAppState,
|
appState: StaticCanvasAppState | InteractiveCanvasAppState,
|
||||||
): ExcalidrawElementWithCanvas | null => {
|
): ExcalidrawElementWithCanvas | null => {
|
||||||
const canvas = document.createElement("canvas");
|
const canvas = document.createElement("canvas");
|
||||||
const context = canvas.getContext("2d")!;
|
const context = canvas.getContext("2d")!;
|
||||||
@@ -549,7 +549,7 @@ const generateElementWithCanvas = (
|
|||||||
element: NonDeletedExcalidrawElement,
|
element: NonDeletedExcalidrawElement,
|
||||||
elementsMap: NonDeletedSceneElementsMap,
|
elementsMap: NonDeletedSceneElementsMap,
|
||||||
renderConfig: StaticCanvasRenderConfig,
|
renderConfig: StaticCanvasRenderConfig,
|
||||||
appState: StaticCanvasAppState,
|
appState: StaticCanvasAppState | InteractiveCanvasAppState,
|
||||||
) => {
|
) => {
|
||||||
const zoom: Zoom = renderConfig
|
const zoom: Zoom = renderConfig
|
||||||
? appState.zoom
|
? appState.zoom
|
||||||
@@ -602,141 +602,95 @@ const generateElementWithCanvas = (
|
|||||||
return prevElementWithCanvas;
|
return prevElementWithCanvas;
|
||||||
};
|
};
|
||||||
|
|
||||||
const drawElementHighlight = (
|
|
||||||
context: CanvasRenderingContext2D,
|
|
||||||
appState: StaticCanvasAppState,
|
|
||||||
) => {
|
|
||||||
if (appState.suggestedBinding) {
|
|
||||||
const cx =
|
|
||||||
(appState.suggestedBinding.x +
|
|
||||||
appState.suggestedBinding.width / 2 +
|
|
||||||
appState.scrollX) *
|
|
||||||
window.devicePixelRatio;
|
|
||||||
const cy =
|
|
||||||
(appState.suggestedBinding.y +
|
|
||||||
appState.suggestedBinding.height / 2 +
|
|
||||||
appState.scrollY) *
|
|
||||||
window.devicePixelRatio;
|
|
||||||
context.save();
|
|
||||||
|
|
||||||
context.translate(cx, cy);
|
|
||||||
context.rotate(appState.suggestedBinding.angle);
|
|
||||||
context.translate(-cx, -cy);
|
|
||||||
context.translate(
|
|
||||||
appState.scrollX + appState.suggestedBinding.x,
|
|
||||||
appState.scrollY + appState.suggestedBinding.y,
|
|
||||||
);
|
|
||||||
|
|
||||||
const drawable = ShapeCache.generateBindableElementHighlight(
|
|
||||||
appState.suggestedBinding,
|
|
||||||
appState,
|
|
||||||
);
|
|
||||||
rough.canvas(context.canvas).draw(drawable);
|
|
||||||
|
|
||||||
context.restore();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const drawElementFromCanvas = (
|
const drawElementFromCanvas = (
|
||||||
elementWithCanvas: ExcalidrawElementWithCanvas,
|
elementWithCanvas: ExcalidrawElementWithCanvas,
|
||||||
context: CanvasRenderingContext2D,
|
context: CanvasRenderingContext2D,
|
||||||
renderConfig: StaticCanvasRenderConfig,
|
renderConfig: StaticCanvasRenderConfig,
|
||||||
appState: StaticCanvasAppState,
|
appState: StaticCanvasAppState | InteractiveCanvasAppState,
|
||||||
allElementsMap: NonDeletedSceneElementsMap,
|
allElementsMap: NonDeletedSceneElementsMap,
|
||||||
) => {
|
) => {
|
||||||
const isHighlighted =
|
const element = elementWithCanvas.element;
|
||||||
appState.suggestedBinding?.id === elementWithCanvas.element.id;
|
const padding = getCanvasPadding(element);
|
||||||
if (
|
const zoom = elementWithCanvas.scale;
|
||||||
!isHighlighted ||
|
const [x1, y1, x2, y2] = getElementAbsoluteCoords(element, allElementsMap);
|
||||||
["image", "text"].includes(elementWithCanvas.element.type)
|
const cx = ((x1 + x2) / 2 + appState.scrollX) * window.devicePixelRatio;
|
||||||
) {
|
const cy = ((y1 + y2) / 2 + appState.scrollY) * window.devicePixelRatio;
|
||||||
const element = elementWithCanvas.element;
|
|
||||||
const padding = getCanvasPadding(element);
|
|
||||||
const zoom = elementWithCanvas.scale;
|
|
||||||
const [x1, y1, x2, y2] = getElementAbsoluteCoords(element, allElementsMap);
|
|
||||||
const cx = ((x1 + x2) / 2 + appState.scrollX) * window.devicePixelRatio;
|
|
||||||
const cy = ((y1 + y2) / 2 + appState.scrollY) * window.devicePixelRatio;
|
|
||||||
|
|
||||||
context.save();
|
context.save();
|
||||||
context.scale(1 / window.devicePixelRatio, 1 / window.devicePixelRatio);
|
context.scale(1 / window.devicePixelRatio, 1 / window.devicePixelRatio);
|
||||||
|
|
||||||
const boundTextElement = getBoundTextElement(element, allElementsMap);
|
const boundTextElement = getBoundTextElement(element, allElementsMap);
|
||||||
|
|
||||||
if (isArrowElement(element) && boundTextElement) {
|
if (isArrowElement(element) && boundTextElement) {
|
||||||
const offsetX =
|
const offsetX =
|
||||||
(elementWithCanvas.boundTextCanvas.width -
|
(elementWithCanvas.boundTextCanvas.width -
|
||||||
elementWithCanvas.canvas!.width) /
|
elementWithCanvas.canvas!.width) /
|
||||||
2;
|
2;
|
||||||
const offsetY =
|
const offsetY =
|
||||||
(elementWithCanvas.boundTextCanvas.height -
|
(elementWithCanvas.boundTextCanvas.height -
|
||||||
elementWithCanvas.canvas!.height) /
|
elementWithCanvas.canvas!.height) /
|
||||||
2;
|
2;
|
||||||
context.translate(cx, cy);
|
context.translate(cx, cy);
|
||||||
context.drawImage(
|
context.drawImage(
|
||||||
elementWithCanvas.boundTextCanvas,
|
elementWithCanvas.boundTextCanvas,
|
||||||
(-(x2 - x1) / 2) * window.devicePixelRatio - offsetX / zoom - padding,
|
(-(x2 - x1) / 2) * window.devicePixelRatio - offsetX / zoom - padding,
|
||||||
(-(y2 - y1) / 2) * window.devicePixelRatio - offsetY / zoom - padding,
|
(-(y2 - y1) / 2) * window.devicePixelRatio - offsetY / zoom - padding,
|
||||||
elementWithCanvas.boundTextCanvas.width / zoom,
|
elementWithCanvas.boundTextCanvas.width / zoom,
|
||||||
elementWithCanvas.boundTextCanvas.height / zoom,
|
elementWithCanvas.boundTextCanvas.height / zoom,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// we translate context to element center so that rotation and scale
|
||||||
|
// originates from the element center
|
||||||
|
context.translate(cx, cy);
|
||||||
|
|
||||||
|
context.rotate(element.angle);
|
||||||
|
|
||||||
|
if (
|
||||||
|
"scale" in elementWithCanvas.element &&
|
||||||
|
!isPendingImageElement(element, renderConfig)
|
||||||
|
) {
|
||||||
|
context.scale(
|
||||||
|
elementWithCanvas.element.scale[0],
|
||||||
|
elementWithCanvas.element.scale[1],
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
// we translate context to element center so that rotation and scale
|
|
||||||
// originates from the element center
|
|
||||||
context.translate(cx, cy);
|
|
||||||
|
|
||||||
context.rotate(element.angle);
|
|
||||||
|
|
||||||
if (
|
|
||||||
"scale" in elementWithCanvas.element &&
|
|
||||||
!isPendingImageElement(element, renderConfig)
|
|
||||||
) {
|
|
||||||
context.scale(
|
|
||||||
elementWithCanvas.element.scale[0],
|
|
||||||
elementWithCanvas.element.scale[1],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// revert afterwards we don't have account for it during drawing
|
|
||||||
context.translate(-cx, -cy);
|
|
||||||
|
|
||||||
context.drawImage(
|
|
||||||
elementWithCanvas.canvas!,
|
|
||||||
(x1 + appState.scrollX) * window.devicePixelRatio -
|
|
||||||
(padding * elementWithCanvas.scale) / elementWithCanvas.scale,
|
|
||||||
(y1 + appState.scrollY) * window.devicePixelRatio -
|
|
||||||
(padding * elementWithCanvas.scale) / elementWithCanvas.scale,
|
|
||||||
elementWithCanvas.canvas!.width / elementWithCanvas.scale,
|
|
||||||
elementWithCanvas.canvas!.height / elementWithCanvas.scale,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (
|
|
||||||
import.meta.env.VITE_APP_DEBUG_ENABLE_TEXT_CONTAINER_BOUNDING_BOX ===
|
|
||||||
"true" &&
|
|
||||||
hasBoundTextElement(element)
|
|
||||||
) {
|
|
||||||
const textElement = getBoundTextElement(
|
|
||||||
element,
|
|
||||||
allElementsMap,
|
|
||||||
) as ExcalidrawTextElementWithContainer;
|
|
||||||
const coords = getContainerCoords(element);
|
|
||||||
context.strokeStyle = "#c92a2a";
|
|
||||||
context.lineWidth = 3;
|
|
||||||
context.strokeRect(
|
|
||||||
(coords.x + appState.scrollX) * window.devicePixelRatio,
|
|
||||||
(coords.y + appState.scrollY) * window.devicePixelRatio,
|
|
||||||
getBoundTextMaxWidth(element, textElement) * window.devicePixelRatio,
|
|
||||||
getBoundTextMaxHeight(element, textElement) * window.devicePixelRatio,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
context.restore();
|
|
||||||
|
|
||||||
// Clear the nested element we appended to the DOM
|
// revert afterwards we don't have account for it during drawing
|
||||||
}
|
context.translate(-cx, -cy);
|
||||||
|
|
||||||
if (isHighlighted) {
|
context.drawImage(
|
||||||
drawElementHighlight(context, appState);
|
elementWithCanvas.canvas!,
|
||||||
|
(x1 + appState.scrollX) * window.devicePixelRatio -
|
||||||
|
(padding * elementWithCanvas.scale) / elementWithCanvas.scale,
|
||||||
|
(y1 + appState.scrollY) * window.devicePixelRatio -
|
||||||
|
(padding * elementWithCanvas.scale) / elementWithCanvas.scale,
|
||||||
|
elementWithCanvas.canvas!.width / elementWithCanvas.scale,
|
||||||
|
elementWithCanvas.canvas!.height / elementWithCanvas.scale,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (
|
||||||
|
import.meta.env.VITE_APP_DEBUG_ENABLE_TEXT_CONTAINER_BOUNDING_BOX ===
|
||||||
|
"true" &&
|
||||||
|
hasBoundTextElement(element)
|
||||||
|
) {
|
||||||
|
const textElement = getBoundTextElement(
|
||||||
|
element,
|
||||||
|
allElementsMap,
|
||||||
|
) as ExcalidrawTextElementWithContainer;
|
||||||
|
const coords = getContainerCoords(element);
|
||||||
|
context.strokeStyle = "#c92a2a";
|
||||||
|
context.lineWidth = 3;
|
||||||
|
context.strokeRect(
|
||||||
|
(coords.x + appState.scrollX) * window.devicePixelRatio,
|
||||||
|
(coords.y + appState.scrollY) * window.devicePixelRatio,
|
||||||
|
getBoundTextMaxWidth(element, textElement) * window.devicePixelRatio,
|
||||||
|
getBoundTextMaxHeight(element, textElement) * window.devicePixelRatio,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
context.restore();
|
||||||
|
|
||||||
|
// Clear the nested element we appended to the DOM
|
||||||
};
|
};
|
||||||
|
|
||||||
export const renderSelectionElement = (
|
export const renderSelectionElement = (
|
||||||
@@ -770,7 +724,7 @@ export const renderElement = (
|
|||||||
rc: RoughCanvas,
|
rc: RoughCanvas,
|
||||||
context: CanvasRenderingContext2D,
|
context: CanvasRenderingContext2D,
|
||||||
renderConfig: StaticCanvasRenderConfig,
|
renderConfig: StaticCanvasRenderConfig,
|
||||||
appState: StaticCanvasAppState,
|
appState: StaticCanvasAppState | InteractiveCanvasAppState,
|
||||||
) => {
|
) => {
|
||||||
const reduceAlphaForSelection =
|
const reduceAlphaForSelection =
|
||||||
appState.openDialog?.name === "elementLinkSelector" &&
|
appState.openDialog?.name === "elementLinkSelector" &&
|
||||||
@@ -789,11 +743,6 @@ export const renderElement = (
|
|||||||
case "magicframe":
|
case "magicframe":
|
||||||
case "frame": {
|
case "frame": {
|
||||||
if (appState.frameRendering.enabled && appState.frameRendering.outline) {
|
if (appState.frameRendering.enabled && appState.frameRendering.outline) {
|
||||||
const isHighlighted = element.id === appState.suggestedBinding?.id;
|
|
||||||
const {
|
|
||||||
options: { stroke: highlightStroke },
|
|
||||||
} = ShapeCache.generateBindableElementHighlight(element, appState);
|
|
||||||
|
|
||||||
context.save();
|
context.save();
|
||||||
context.translate(
|
context.translate(
|
||||||
element.x + appState.scrollX,
|
element.x + appState.scrollX,
|
||||||
@@ -802,17 +751,12 @@ export const renderElement = (
|
|||||||
context.fillStyle = "rgba(0, 0, 200, 0.04)";
|
context.fillStyle = "rgba(0, 0, 200, 0.04)";
|
||||||
|
|
||||||
context.lineWidth = FRAME_STYLE.strokeWidth / appState.zoom.value;
|
context.lineWidth = FRAME_STYLE.strokeWidth / appState.zoom.value;
|
||||||
context.strokeStyle = isHighlighted
|
context.strokeStyle = FRAME_STYLE.strokeColor;
|
||||||
? highlightStroke
|
|
||||||
: FRAME_STYLE.strokeColor;
|
|
||||||
|
|
||||||
// TODO change later to only affect AI frames
|
// TODO change later to only affect AI frames
|
||||||
if (isMagicFrameElement(element)) {
|
if (isMagicFrameElement(element)) {
|
||||||
context.strokeStyle = isHighlighted
|
context.strokeStyle =
|
||||||
? highlightStroke
|
appState.theme === THEME.LIGHT ? "#7affd7" : "#1d8264";
|
||||||
: appState.theme === THEME.LIGHT
|
|
||||||
? "#7affd7"
|
|
||||||
: "#1d8264";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FRAME_STYLE.radius && context.roundRect) {
|
if (FRAME_STYLE.radius && context.roundRect) {
|
||||||
|
|||||||
@@ -917,12 +917,11 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this.lastPointerMoveCoords) {
|
if (
|
||||||
invariant(
|
this.lastPointerMoveCoords &&
|
||||||
this.state.selectedLinearElement,
|
this.state.selectedLinearElement?.selectedPointsIndices &&
|
||||||
"Selected element is missing",
|
this.state.selectedLinearElement?.selectedPointsIndices.length
|
||||||
);
|
) {
|
||||||
|
|
||||||
const { x, y } = this.lastPointerMoveCoords;
|
const { x, y } = this.lastPointerMoveCoords;
|
||||||
const event =
|
const event =
|
||||||
this.lastPointerMoveEvent ?? this.lastPointerDownEvent?.nativeEvent;
|
this.lastPointerMoveEvent ?? this.lastPointerDownEvent?.nativeEvent;
|
||||||
|
|||||||
@@ -215,6 +215,10 @@ const getRelevantAppStateProps = (
|
|||||||
croppingElementId: appState.croppingElementId,
|
croppingElementId: appState.croppingElementId,
|
||||||
searchMatches: appState.searchMatches,
|
searchMatches: appState.searchMatches,
|
||||||
activeLockedId: appState.activeLockedId,
|
activeLockedId: appState.activeLockedId,
|
||||||
|
hoveredElementIds: appState.hoveredElementIds,
|
||||||
|
frameRendering: appState.frameRendering,
|
||||||
|
shouldCacheIgnoreZoom: appState.shouldCacheIgnoreZoom,
|
||||||
|
exportScale: appState.exportScale,
|
||||||
});
|
});
|
||||||
|
|
||||||
const areEqual = (
|
const areEqual = (
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import rough from "roughjs/bin/rough";
|
||||||
import {
|
import {
|
||||||
pointFrom,
|
pointFrom,
|
||||||
pointsEqual,
|
pointsEqual,
|
||||||
@@ -16,7 +17,11 @@ import {
|
|||||||
throttleRAF,
|
throttleRAF,
|
||||||
} from "@excalidraw/common";
|
} from "@excalidraw/common";
|
||||||
|
|
||||||
import { LinearElementEditor } from "@excalidraw/element";
|
import {
|
||||||
|
LinearElementEditor,
|
||||||
|
renderElement,
|
||||||
|
ShapeCache,
|
||||||
|
} from "@excalidraw/element";
|
||||||
import {
|
import {
|
||||||
getOmitSidesForDevice,
|
getOmitSidesForDevice,
|
||||||
getTransformHandles,
|
getTransformHandles,
|
||||||
@@ -50,6 +55,7 @@ import type {
|
|||||||
|
|
||||||
import type {
|
import type {
|
||||||
ElementsMap,
|
ElementsMap,
|
||||||
|
ExcalidrawBindableElement,
|
||||||
ExcalidrawElement,
|
ExcalidrawElement,
|
||||||
ExcalidrawFrameLikeElement,
|
ExcalidrawFrameLikeElement,
|
||||||
ExcalidrawImageElement,
|
ExcalidrawImageElement,
|
||||||
@@ -57,6 +63,7 @@ import type {
|
|||||||
ExcalidrawTextElement,
|
ExcalidrawTextElement,
|
||||||
GroupId,
|
GroupId,
|
||||||
NonDeleted,
|
NonDeleted,
|
||||||
|
NonDeletedSceneElementsMap,
|
||||||
} from "@excalidraw/element/types";
|
} from "@excalidraw/element/types";
|
||||||
|
|
||||||
import { renderSnaps } from "../renderer/renderSnaps";
|
import { renderSnaps } from "../renderer/renderSnaps";
|
||||||
@@ -66,6 +73,7 @@ import {
|
|||||||
SCROLLBAR_COLOR,
|
SCROLLBAR_COLOR,
|
||||||
SCROLLBAR_WIDTH,
|
SCROLLBAR_WIDTH,
|
||||||
} from "../scene/scrollbars";
|
} from "../scene/scrollbars";
|
||||||
|
|
||||||
import { type InteractiveCanvasAppState } from "../types";
|
import { type InteractiveCanvasAppState } from "../types";
|
||||||
|
|
||||||
import { getClientColor, renderRemoteCursors } from "../clients";
|
import { getClientColor, renderRemoteCursors } from "../clients";
|
||||||
@@ -178,6 +186,126 @@ const renderSingleLinearPoint = <Point extends GlobalPoint | LocalPoint>(
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const renderBindingHighlightForBindableElement = (
|
||||||
|
context: CanvasRenderingContext2D,
|
||||||
|
element: ExcalidrawBindableElement,
|
||||||
|
elementsMap: RenderableElementsMap,
|
||||||
|
allElementsMap: NonDeletedSceneElementsMap,
|
||||||
|
appState: InteractiveCanvasAppState,
|
||||||
|
) => {
|
||||||
|
switch (element.type) {
|
||||||
|
case "magicframe":
|
||||||
|
case "frame":
|
||||||
|
{
|
||||||
|
const {
|
||||||
|
options: { stroke: highlightStroke },
|
||||||
|
} = ShapeCache.generateBindableElementHighlight(element, appState);
|
||||||
|
|
||||||
|
context.save();
|
||||||
|
context.translate(
|
||||||
|
element.x + appState.scrollX,
|
||||||
|
element.y + appState.scrollY,
|
||||||
|
);
|
||||||
|
|
||||||
|
context.lineWidth = FRAME_STYLE.strokeWidth / appState.zoom.value;
|
||||||
|
context.strokeStyle = highlightStroke;
|
||||||
|
|
||||||
|
if (FRAME_STYLE.radius && context.roundRect) {
|
||||||
|
context.beginPath();
|
||||||
|
context.roundRect(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
element.width,
|
||||||
|
element.height,
|
||||||
|
FRAME_STYLE.radius / appState.zoom.value,
|
||||||
|
);
|
||||||
|
context.stroke();
|
||||||
|
context.closePath();
|
||||||
|
} else {
|
||||||
|
context.strokeRect(0, 0, element.width, element.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
context.restore();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "image":
|
||||||
|
case "text":
|
||||||
|
{
|
||||||
|
const {
|
||||||
|
options: { stroke: highlightStroke },
|
||||||
|
} = ShapeCache.generateBindableElementHighlight(element, appState);
|
||||||
|
|
||||||
|
context.save();
|
||||||
|
context.translate(
|
||||||
|
element.x + appState.scrollX,
|
||||||
|
element.y + appState.scrollY,
|
||||||
|
);
|
||||||
|
|
||||||
|
context.lineWidth = FRAME_STYLE.strokeWidth / appState.zoom.value;
|
||||||
|
context.strokeStyle = highlightStroke;
|
||||||
|
|
||||||
|
context.strokeRect(0, 0, element.width, element.height);
|
||||||
|
context.restore();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
const cx =
|
||||||
|
(element.x + element.width / 2 + appState.scrollX) *
|
||||||
|
window.devicePixelRatio;
|
||||||
|
const cy =
|
||||||
|
(element.y + element.height / 2 + appState.scrollY) *
|
||||||
|
window.devicePixelRatio;
|
||||||
|
context.save();
|
||||||
|
|
||||||
|
context.translate(cx, cy);
|
||||||
|
context.rotate(element.angle);
|
||||||
|
context.translate(-cx, -cy);
|
||||||
|
context.translate(
|
||||||
|
appState.scrollX + element.x,
|
||||||
|
appState.scrollY + element.y,
|
||||||
|
);
|
||||||
|
|
||||||
|
const drawable = ShapeCache.generateBindableElementHighlight(
|
||||||
|
element,
|
||||||
|
appState,
|
||||||
|
);
|
||||||
|
drawable.options.fill = "transparent";
|
||||||
|
rough.canvas(context.canvas).draw(drawable);
|
||||||
|
|
||||||
|
context.restore();
|
||||||
|
|
||||||
|
// Overdraw the arrow if exists (if there is a suggestedBinding it's an arrow)
|
||||||
|
if (appState.selectedLinearElement) {
|
||||||
|
const arrow = LinearElementEditor.getElement(
|
||||||
|
appState.selectedLinearElement.elementId,
|
||||||
|
allElementsMap,
|
||||||
|
);
|
||||||
|
|
||||||
|
invariant(arrow, "arrow not found during highlight element binding");
|
||||||
|
|
||||||
|
renderElement(
|
||||||
|
arrow,
|
||||||
|
elementsMap,
|
||||||
|
allElementsMap,
|
||||||
|
rough.canvas(context.canvas),
|
||||||
|
context,
|
||||||
|
{
|
||||||
|
canvasBackgroundColor: "transparent",
|
||||||
|
imageCache: new Map(),
|
||||||
|
renderGrid: false,
|
||||||
|
isExporting: false,
|
||||||
|
embedsValidationStatus: new Map(),
|
||||||
|
elementsPendingErasure: new Set(),
|
||||||
|
pendingFlowchartNodes: null,
|
||||||
|
},
|
||||||
|
appState,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
type ElementSelectionBorder = {
|
type ElementSelectionBorder = {
|
||||||
angle: number;
|
angle: number;
|
||||||
x1: number;
|
x1: number;
|
||||||
@@ -707,6 +835,16 @@ const _renderInteractiveScene = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (appState.isBindingEnabled && appState.suggestedBinding) {
|
||||||
|
renderBindingHighlightForBindableElement(
|
||||||
|
context,
|
||||||
|
appState.suggestedBinding,
|
||||||
|
elementsMap,
|
||||||
|
allElementsMap,
|
||||||
|
appState,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (appState.frameToHighlight) {
|
if (appState.frameToHighlight) {
|
||||||
renderFrameHighlight(
|
renderFrameHighlight(
|
||||||
context,
|
context,
|
||||||
|
|||||||
@@ -234,6 +234,11 @@ export type InteractiveCanvasAppState = Readonly<
|
|||||||
// Search matches
|
// Search matches
|
||||||
searchMatches: AppState["searchMatches"];
|
searchMatches: AppState["searchMatches"];
|
||||||
activeLockedId: AppState["activeLockedId"];
|
activeLockedId: AppState["activeLockedId"];
|
||||||
|
// Non-used but needed in binding highlight arrow overdraw
|
||||||
|
hoveredElementIds: AppState["hoveredElementIds"];
|
||||||
|
frameRendering: AppState["frameRendering"];
|
||||||
|
shouldCacheIgnoreZoom: AppState["shouldCacheIgnoreZoom"];
|
||||||
|
exportScale: AppState["exportScale"];
|
||||||
}
|
}
|
||||||
>;
|
>;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user