fix(editor): simplify and fix midpoint highlighting (#10832)
This commit is contained in:
@@ -265,6 +265,7 @@ const getRelevantAppStateProps = (
|
|||||||
frameRendering: appState.frameRendering,
|
frameRendering: appState.frameRendering,
|
||||||
shouldCacheIgnoreZoom: appState.shouldCacheIgnoreZoom,
|
shouldCacheIgnoreZoom: appState.shouldCacheIgnoreZoom,
|
||||||
exportScale: appState.exportScale,
|
exportScale: appState.exportScale,
|
||||||
|
currentItemArrowType: appState.currentItemArrowType,
|
||||||
});
|
});
|
||||||
|
|
||||||
const areEqual = (
|
const areEqual = (
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
type Radians,
|
type Radians,
|
||||||
bezierEquation,
|
bezierEquation,
|
||||||
pointRotateRads,
|
pointRotateRads,
|
||||||
|
pointDistance,
|
||||||
} from "@excalidraw/math";
|
} from "@excalidraw/math";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -37,14 +38,9 @@ import {
|
|||||||
isImageElement,
|
isImageElement,
|
||||||
isLinearElement,
|
isLinearElement,
|
||||||
isLineElement,
|
isLineElement,
|
||||||
|
maxBindingDistance_simple,
|
||||||
isTextElement,
|
isTextElement,
|
||||||
LinearElementEditor,
|
LinearElementEditor,
|
||||||
headingForPoint,
|
|
||||||
compareHeading,
|
|
||||||
HEADING_RIGHT,
|
|
||||||
HEADING_DOWN,
|
|
||||||
HEADING_LEFT,
|
|
||||||
HEADING_UP,
|
|
||||||
} from "@excalidraw/element";
|
} from "@excalidraw/element";
|
||||||
|
|
||||||
import { renderSelectionElement } from "@excalidraw/element";
|
import { renderSelectionElement } from "@excalidraw/element";
|
||||||
@@ -419,9 +415,8 @@ const renderBindingHighlightForBindableElement_simple = (
|
|||||||
const arrow =
|
const arrow =
|
||||||
linearElement?.elementId &&
|
linearElement?.elementId &&
|
||||||
LinearElementEditor.getElement(linearElement?.elementId, elementsMap);
|
LinearElementEditor.getElement(linearElement?.elementId, elementsMap);
|
||||||
const insideBindable =
|
const cursorIsInsideBindable =
|
||||||
pointerCoords &&
|
pointerCoords &&
|
||||||
arrow &&
|
|
||||||
hitElementItself({
|
hitElementItself({
|
||||||
point: pointerCoords,
|
point: pointerCoords,
|
||||||
element: suggestedBinding.element,
|
element: suggestedBinding.element,
|
||||||
@@ -430,14 +425,17 @@ const renderBindingHighlightForBindableElement_simple = (
|
|||||||
overrideShouldTestInside: true,
|
overrideShouldTestInside: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!insideBindable || isElbowArrow(arrow)) {
|
const isElbow =
|
||||||
|
(arrow && isElbowArrow(arrow)) ||
|
||||||
|
(appState.activeTool.type === "arrow" &&
|
||||||
|
appState.currentItemArrowType === "elbow");
|
||||||
|
|
||||||
|
if (!cursorIsInsideBindable || isElbow) {
|
||||||
context.save();
|
context.save();
|
||||||
context.translate(suggestedBinding.element.x, suggestedBinding.element.y);
|
|
||||||
|
|
||||||
const midpointRadius = 5 / appState.zoom.value;
|
|
||||||
const center = elementCenterPoint(suggestedBinding.element, elementsMap);
|
const center = elementCenterPoint(suggestedBinding.element, elementsMap);
|
||||||
|
|
||||||
let midpoints: LocalPoint[];
|
let midpoints: GlobalPoint[];
|
||||||
if (suggestedBinding.element.type === "diamond") {
|
if (suggestedBinding.element.type === "diamond") {
|
||||||
const center = elementCenterPoint(
|
const center = elementCenterPoint(
|
||||||
suggestedBinding.element,
|
suggestedBinding.element,
|
||||||
@@ -452,10 +450,7 @@ const renderBindingHighlightForBindableElement_simple = (
|
|||||||
suggestedBinding.element.angle,
|
suggestedBinding.element.angle,
|
||||||
);
|
);
|
||||||
|
|
||||||
return pointFrom<LocalPoint>(
|
return pointFrom<GlobalPoint>(rotatedPoint[0], rotatedPoint[1]);
|
||||||
rotatedPoint[0] - suggestedBinding.element.x,
|
|
||||||
rotatedPoint[1] - suggestedBinding.element.y,
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@@ -481,47 +476,53 @@ const renderBindingHighlightForBindableElement_simple = (
|
|||||||
center,
|
center,
|
||||||
suggestedBinding.element.angle,
|
suggestedBinding.element.angle,
|
||||||
);
|
);
|
||||||
return pointFrom<LocalPoint>(
|
return pointFrom<GlobalPoint>(rotatedPoint[0], rotatedPoint[1]);
|
||||||
rotatedPoint[0] - suggestedBinding.element.x,
|
|
||||||
rotatedPoint[1] - suggestedBinding.element.y,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const highlightedPoint =
|
|
||||||
suggestedBinding.midPoint &&
|
const hoveredMidpoint =
|
||||||
pointFrom<LocalPoint>(
|
pointerCoords &&
|
||||||
suggestedBinding.midPoint[0] - suggestedBinding.element.x,
|
midpoints.reduce(
|
||||||
suggestedBinding.midPoint[1] - suggestedBinding.element.y,
|
(
|
||||||
|
closestIdx: {
|
||||||
|
idx: number;
|
||||||
|
distance: number;
|
||||||
|
},
|
||||||
|
point,
|
||||||
|
idx,
|
||||||
|
) => {
|
||||||
|
const distance = pointDistance(point, pointerCoords);
|
||||||
|
if (idx === -1 || distance < closestIdx.distance) {
|
||||||
|
return { idx, distance };
|
||||||
|
}
|
||||||
|
return closestIdx;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
idx: -1,
|
||||||
|
distance: Infinity,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const target = [HEADING_RIGHT, HEADING_DOWN, HEADING_LEFT, HEADING_UP];
|
const midpointRadius = 4 / appState.zoom.value;
|
||||||
|
const highlightThreshold =
|
||||||
|
maxBindingDistance_simple(appState.zoom) +
|
||||||
|
suggestedBinding.element.strokeWidth / 2;
|
||||||
|
|
||||||
midpoints.forEach((midpoint, idx) => {
|
midpoints.forEach((midpoint, idx) => {
|
||||||
const isHighlighted =
|
const isHighlighted =
|
||||||
highlightedPoint &&
|
(!cursorIsInsideBindable || isElbow) &&
|
||||||
compareHeading(
|
hoveredMidpoint?.idx === idx &&
|
||||||
headingForPoint(
|
hoveredMidpoint.distance <= highlightThreshold;
|
||||||
pointRotateRads(
|
|
||||||
pointFrom<GlobalPoint>(
|
|
||||||
highlightedPoint[0] + suggestedBinding.element.x,
|
|
||||||
highlightedPoint[1] + suggestedBinding.element.y,
|
|
||||||
),
|
|
||||||
center,
|
|
||||||
suggestedBinding.element.angle as Radians,
|
|
||||||
),
|
|
||||||
center,
|
|
||||||
),
|
|
||||||
target[idx],
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!isHighlighted) {
|
// also render midpoint if cursor close but not highlighted
|
||||||
context.fillStyle =
|
// (for elbows, always show all points)
|
||||||
appState.theme === THEME.DARK
|
const isShown =
|
||||||
? `rgba(0, 0, 0, 0.5)`
|
!isHighlighted &&
|
||||||
: `rgba(65, 65, 65, 0.4)`;
|
(isElbow ||
|
||||||
context.beginPath();
|
(idx === hoveredMidpoint?.idx &&
|
||||||
context.arc(midpoint[0], midpoint[1], midpointRadius, 0, 2 * Math.PI);
|
hoveredMidpoint.distance <= highlightThreshold * 2));
|
||||||
context.fill();
|
|
||||||
} else {
|
if (isHighlighted) {
|
||||||
context.fillStyle =
|
context.fillStyle =
|
||||||
appState.theme === THEME.DARK
|
appState.theme === THEME.DARK
|
||||||
? `rgba(3, 93, 161, 1)`
|
? `rgba(3, 93, 161, 1)`
|
||||||
@@ -530,6 +531,14 @@ const renderBindingHighlightForBindableElement_simple = (
|
|||||||
context.beginPath();
|
context.beginPath();
|
||||||
context.arc(midpoint[0], midpoint[1], midpointRadius, 0, 2 * Math.PI);
|
context.arc(midpoint[0], midpoint[1], midpointRadius, 0, 2 * Math.PI);
|
||||||
context.fill();
|
context.fill();
|
||||||
|
} else if (isShown) {
|
||||||
|
context.fillStyle =
|
||||||
|
appState.theme === THEME.DARK
|
||||||
|
? `rgba(0, 0, 0, 0.8)`
|
||||||
|
: `rgba(65, 65, 65, 0.5)`;
|
||||||
|
context.beginPath();
|
||||||
|
context.arc(midpoint[0], midpoint[1], midpointRadius, 0, 2 * Math.PI);
|
||||||
|
context.fill();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -244,6 +244,7 @@ export type InteractiveCanvasAppState = Readonly<
|
|||||||
frameRendering: AppState["frameRendering"];
|
frameRendering: AppState["frameRendering"];
|
||||||
shouldCacheIgnoreZoom: AppState["shouldCacheIgnoreZoom"];
|
shouldCacheIgnoreZoom: AppState["shouldCacheIgnoreZoom"];
|
||||||
exportScale: AppState["exportScale"];
|
exportScale: AppState["exportScale"];
|
||||||
|
currentItemArrowType: AppState["currentItemArrowType"];
|
||||||
}
|
}
|
||||||
>;
|
>;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user