mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-09-17 22:40:54 +02:00
Binding highlight refactor
This commit is contained in:
@@ -1,6 +1,5 @@
|
|||||||
import { THEME, THEME_FILTER } from "@excalidraw/common";
|
import { THEME, THEME_FILTER } from "@excalidraw/common";
|
||||||
|
|
||||||
import { FIXED_BINDING_DISTANCE } from "@excalidraw/element";
|
|
||||||
import { getDiamondPoints } from "@excalidraw/element";
|
import { getDiamondPoints } from "@excalidraw/element";
|
||||||
import { elementCenterPoint, getCornerRadius } from "@excalidraw/element";
|
import { elementCenterPoint, getCornerRadius } from "@excalidraw/element";
|
||||||
|
|
||||||
@@ -130,7 +129,6 @@ export const drawHighlightForRectWithRotation = (
|
|||||||
context: CanvasRenderingContext2D,
|
context: CanvasRenderingContext2D,
|
||||||
element: ExcalidrawRectanguloidElement,
|
element: ExcalidrawRectanguloidElement,
|
||||||
elementsMap: ElementsMap,
|
elementsMap: ElementsMap,
|
||||||
padding: number,
|
|
||||||
) => {
|
) => {
|
||||||
const [x, y] = pointRotateRads(
|
const [x, y] = pointRotateRads(
|
||||||
pointFrom<GlobalPoint>(element.x, element.y),
|
pointFrom<GlobalPoint>(element.x, element.y),
|
||||||
@@ -157,25 +155,25 @@ export const drawHighlightForRectWithRotation = (
|
|||||||
pointFrom(0, 0 + radius),
|
pointFrom(0, 0 + radius),
|
||||||
pointFrom(0, 0),
|
pointFrom(0, 0),
|
||||||
pointFrom(0 + radius, 0),
|
pointFrom(0 + radius, 0),
|
||||||
padding,
|
0,
|
||||||
);
|
);
|
||||||
const topRightApprox = offsetPointsForQuadraticBezier(
|
const topRightApprox = offsetPointsForQuadraticBezier(
|
||||||
pointFrom(element.width - radius, 0),
|
pointFrom(element.width - radius, 0),
|
||||||
pointFrom(element.width, 0),
|
pointFrom(element.width, 0),
|
||||||
pointFrom(element.width, radius),
|
pointFrom(element.width, radius),
|
||||||
padding,
|
0,
|
||||||
);
|
);
|
||||||
const bottomRightApprox = offsetPointsForQuadraticBezier(
|
const bottomRightApprox = offsetPointsForQuadraticBezier(
|
||||||
pointFrom(element.width, element.height - radius),
|
pointFrom(element.width, element.height - radius),
|
||||||
pointFrom(element.width, element.height),
|
pointFrom(element.width, element.height),
|
||||||
pointFrom(element.width - radius, element.height),
|
pointFrom(element.width - radius, element.height),
|
||||||
padding,
|
0,
|
||||||
);
|
);
|
||||||
const bottomLeftApprox = offsetPointsForQuadraticBezier(
|
const bottomLeftApprox = offsetPointsForQuadraticBezier(
|
||||||
pointFrom(radius, element.height),
|
pointFrom(radius, element.height),
|
||||||
pointFrom(0, element.height),
|
pointFrom(0, element.height),
|
||||||
pointFrom(0, element.height - radius),
|
pointFrom(0, element.height - radius),
|
||||||
padding,
|
0,
|
||||||
);
|
);
|
||||||
|
|
||||||
context.moveTo(
|
context.moveTo(
|
||||||
@@ -192,51 +190,8 @@ export const drawHighlightForRectWithRotation = (
|
|||||||
drawCatmullRomQuadraticApprox(context, topLeftApprox);
|
drawCatmullRomQuadraticApprox(context, topLeftApprox);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Counter-clockwise for the cutout in the middle. We need to have an "inverse
|
|
||||||
// mask" on a filled shape for the diamond highlight, because stroking creates
|
|
||||||
// sharp inset edges on line joins < 90 degrees.
|
|
||||||
{
|
|
||||||
const topLeftApprox = offsetPointsForQuadraticBezier(
|
|
||||||
pointFrom(0 + radius, 0),
|
|
||||||
pointFrom(0, 0),
|
|
||||||
pointFrom(0, 0 + radius),
|
|
||||||
-FIXED_BINDING_DISTANCE,
|
|
||||||
);
|
|
||||||
const topRightApprox = offsetPointsForQuadraticBezier(
|
|
||||||
pointFrom(element.width, radius),
|
|
||||||
pointFrom(element.width, 0),
|
|
||||||
pointFrom(element.width - radius, 0),
|
|
||||||
-FIXED_BINDING_DISTANCE,
|
|
||||||
);
|
|
||||||
const bottomRightApprox = offsetPointsForQuadraticBezier(
|
|
||||||
pointFrom(element.width - radius, element.height),
|
|
||||||
pointFrom(element.width, element.height),
|
|
||||||
pointFrom(element.width, element.height - radius),
|
|
||||||
-FIXED_BINDING_DISTANCE,
|
|
||||||
);
|
|
||||||
const bottomLeftApprox = offsetPointsForQuadraticBezier(
|
|
||||||
pointFrom(0, element.height - radius),
|
|
||||||
pointFrom(0, element.height),
|
|
||||||
pointFrom(radius, element.height),
|
|
||||||
-FIXED_BINDING_DISTANCE,
|
|
||||||
);
|
|
||||||
|
|
||||||
context.moveTo(
|
|
||||||
topLeftApprox[topLeftApprox.length - 1][0],
|
|
||||||
topLeftApprox[topLeftApprox.length - 1][1],
|
|
||||||
);
|
|
||||||
context.lineTo(bottomLeftApprox[0][0], bottomLeftApprox[0][1]);
|
|
||||||
drawCatmullRomQuadraticApprox(context, bottomLeftApprox);
|
|
||||||
context.lineTo(bottomRightApprox[0][0], bottomRightApprox[0][1]);
|
|
||||||
drawCatmullRomQuadraticApprox(context, bottomRightApprox);
|
|
||||||
context.lineTo(topRightApprox[0][0], topRightApprox[0][1]);
|
|
||||||
drawCatmullRomQuadraticApprox(context, topRightApprox);
|
|
||||||
context.lineTo(topLeftApprox[0][0], topLeftApprox[0][1]);
|
|
||||||
drawCatmullRomQuadraticApprox(context, topLeftApprox);
|
|
||||||
}
|
|
||||||
|
|
||||||
context.closePath();
|
context.closePath();
|
||||||
context.fill();
|
context.stroke();
|
||||||
|
|
||||||
context.restore();
|
context.restore();
|
||||||
};
|
};
|
||||||
@@ -286,7 +241,6 @@ export const strokeRectWithRotation = (
|
|||||||
|
|
||||||
export const drawHighlightForDiamondWithRotation = (
|
export const drawHighlightForDiamondWithRotation = (
|
||||||
context: CanvasRenderingContext2D,
|
context: CanvasRenderingContext2D,
|
||||||
padding: number,
|
|
||||||
element: ExcalidrawDiamondElement,
|
element: ExcalidrawDiamondElement,
|
||||||
elementsMap: ElementsMap,
|
elementsMap: ElementsMap,
|
||||||
) => {
|
) => {
|
||||||
@@ -317,7 +271,7 @@ export const drawHighlightForDiamondWithRotation = (
|
|||||||
pointFrom(topX, topY),
|
pointFrom(topX, topY),
|
||||||
pointFrom(topX + verticalRadius, topY + horizontalRadius),
|
pointFrom(topX + verticalRadius, topY + horizontalRadius),
|
||||||
),
|
),
|
||||||
padding,
|
0,
|
||||||
);
|
);
|
||||||
const rightApprox = curveOffsetPoints(
|
const rightApprox = curveOffsetPoints(
|
||||||
curve(
|
curve(
|
||||||
@@ -326,7 +280,7 @@ export const drawHighlightForDiamondWithRotation = (
|
|||||||
pointFrom(rightX, rightY),
|
pointFrom(rightX, rightY),
|
||||||
pointFrom(rightX - verticalRadius, rightY + horizontalRadius),
|
pointFrom(rightX - verticalRadius, rightY + horizontalRadius),
|
||||||
),
|
),
|
||||||
padding,
|
0,
|
||||||
);
|
);
|
||||||
const bottomApprox = curveOffsetPoints(
|
const bottomApprox = curveOffsetPoints(
|
||||||
curve(
|
curve(
|
||||||
@@ -335,7 +289,7 @@ export const drawHighlightForDiamondWithRotation = (
|
|||||||
pointFrom(bottomX, bottomY),
|
pointFrom(bottomX, bottomY),
|
||||||
pointFrom(bottomX - verticalRadius, bottomY - horizontalRadius),
|
pointFrom(bottomX - verticalRadius, bottomY - horizontalRadius),
|
||||||
),
|
),
|
||||||
padding,
|
0,
|
||||||
);
|
);
|
||||||
const leftApprox = curveOffsetPoints(
|
const leftApprox = curveOffsetPoints(
|
||||||
curve(
|
curve(
|
||||||
@@ -344,7 +298,7 @@ export const drawHighlightForDiamondWithRotation = (
|
|||||||
pointFrom(leftX, leftY),
|
pointFrom(leftX, leftY),
|
||||||
pointFrom(leftX + verticalRadius, leftY - horizontalRadius),
|
pointFrom(leftX + verticalRadius, leftY - horizontalRadius),
|
||||||
),
|
),
|
||||||
padding,
|
0,
|
||||||
);
|
);
|
||||||
|
|
||||||
context.moveTo(
|
context.moveTo(
|
||||||
@@ -361,69 +315,7 @@ export const drawHighlightForDiamondWithRotation = (
|
|||||||
drawCatmullRomCubicApprox(context, topApprox);
|
drawCatmullRomCubicApprox(context, topApprox);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Counter-clockwise for the cutout in the middle. We need to have an "inverse
|
|
||||||
// mask" on a filled shape for the diamond highlight, because stroking creates
|
|
||||||
// sharp inset edges on line joins < 90 degrees.
|
|
||||||
{
|
|
||||||
const [topX, topY, rightX, rightY, bottomX, bottomY, leftX, leftY] =
|
|
||||||
getDiamondPoints(element);
|
|
||||||
const verticalRadius = element.roundness
|
|
||||||
? getCornerRadius(Math.abs(topX - leftX), element)
|
|
||||||
: (topX - leftX) * 0.01;
|
|
||||||
const horizontalRadius = element.roundness
|
|
||||||
? getCornerRadius(Math.abs(rightY - topY), element)
|
|
||||||
: (rightY - topY) * 0.01;
|
|
||||||
const topApprox = curveOffsetPoints(
|
|
||||||
curve(
|
|
||||||
pointFrom(topX + verticalRadius, topY + horizontalRadius),
|
|
||||||
pointFrom(topX, topY),
|
|
||||||
pointFrom(topX, topY),
|
|
||||||
pointFrom(topX - verticalRadius, topY + horizontalRadius),
|
|
||||||
),
|
|
||||||
-FIXED_BINDING_DISTANCE,
|
|
||||||
);
|
|
||||||
const rightApprox = curveOffsetPoints(
|
|
||||||
curve(
|
|
||||||
pointFrom(rightX - verticalRadius, rightY + horizontalRadius),
|
|
||||||
pointFrom(rightX, rightY),
|
|
||||||
pointFrom(rightX, rightY),
|
|
||||||
pointFrom(rightX - verticalRadius, rightY - horizontalRadius),
|
|
||||||
),
|
|
||||||
-FIXED_BINDING_DISTANCE,
|
|
||||||
);
|
|
||||||
const bottomApprox = curveOffsetPoints(
|
|
||||||
curve(
|
|
||||||
pointFrom(bottomX - verticalRadius, bottomY - horizontalRadius),
|
|
||||||
pointFrom(bottomX, bottomY),
|
|
||||||
pointFrom(bottomX, bottomY),
|
|
||||||
pointFrom(bottomX + verticalRadius, bottomY - horizontalRadius),
|
|
||||||
),
|
|
||||||
-FIXED_BINDING_DISTANCE,
|
|
||||||
);
|
|
||||||
const leftApprox = curveOffsetPoints(
|
|
||||||
curve(
|
|
||||||
pointFrom(leftX + verticalRadius, leftY - horizontalRadius),
|
|
||||||
pointFrom(leftX, leftY),
|
|
||||||
pointFrom(leftX, leftY),
|
|
||||||
pointFrom(leftX + verticalRadius, leftY + horizontalRadius),
|
|
||||||
),
|
|
||||||
-FIXED_BINDING_DISTANCE,
|
|
||||||
);
|
|
||||||
|
|
||||||
context.moveTo(
|
|
||||||
topApprox[topApprox.length - 1][0],
|
|
||||||
topApprox[topApprox.length - 1][1],
|
|
||||||
);
|
|
||||||
context.lineTo(leftApprox[1][0], leftApprox[1][1]);
|
|
||||||
drawCatmullRomCubicApprox(context, leftApprox);
|
|
||||||
context.lineTo(bottomApprox[1][0], bottomApprox[1][1]);
|
|
||||||
drawCatmullRomCubicApprox(context, bottomApprox);
|
|
||||||
context.lineTo(rightApprox[1][0], rightApprox[1][1]);
|
|
||||||
drawCatmullRomCubicApprox(context, rightApprox);
|
|
||||||
context.lineTo(topApprox[1][0], topApprox[1][1]);
|
|
||||||
drawCatmullRomCubicApprox(context, topApprox);
|
|
||||||
}
|
|
||||||
context.closePath();
|
context.closePath();
|
||||||
context.fill();
|
context.stroke();
|
||||||
context.restore();
|
context.restore();
|
||||||
};
|
};
|
||||||
|
@@ -16,7 +16,6 @@ import {
|
|||||||
throttleRAF,
|
throttleRAF,
|
||||||
} from "@excalidraw/common";
|
} from "@excalidraw/common";
|
||||||
|
|
||||||
import { FIXED_BINDING_DISTANCE } from "@excalidraw/element";
|
|
||||||
import { LinearElementEditor } from "@excalidraw/element";
|
import { LinearElementEditor } from "@excalidraw/element";
|
||||||
import {
|
import {
|
||||||
getOmitSidesForDevice,
|
getOmitSidesForDevice,
|
||||||
@@ -192,11 +191,11 @@ const renderBindingHighlightForBindableElement = (
|
|||||||
context: CanvasRenderingContext2D,
|
context: CanvasRenderingContext2D,
|
||||||
element: ExcalidrawBindableElement,
|
element: ExcalidrawBindableElement,
|
||||||
elementsMap: ElementsMap,
|
elementsMap: ElementsMap,
|
||||||
zoom: InteractiveCanvasAppState["zoom"],
|
|
||||||
) => {
|
) => {
|
||||||
const padding = 5;
|
context.lineWidth = 1;
|
||||||
|
context.strokeStyle = element.strokeColor;
|
||||||
context.fillStyle = "rgba(0,0,0,.05)";
|
context.shadowColor = element.strokeColor;
|
||||||
|
context.shadowBlur = 10;
|
||||||
|
|
||||||
switch (element.type) {
|
switch (element.type) {
|
||||||
case "rectangle":
|
case "rectangle":
|
||||||
@@ -206,28 +205,20 @@ const renderBindingHighlightForBindableElement = (
|
|||||||
case "embeddable":
|
case "embeddable":
|
||||||
case "frame":
|
case "frame":
|
||||||
case "magicframe":
|
case "magicframe":
|
||||||
drawHighlightForRectWithRotation(context, element, elementsMap, padding);
|
drawHighlightForRectWithRotation(context, element, elementsMap);
|
||||||
break;
|
break;
|
||||||
case "diamond":
|
case "diamond":
|
||||||
drawHighlightForDiamondWithRotation(
|
drawHighlightForDiamondWithRotation(context, element, elementsMap);
|
||||||
context,
|
|
||||||
padding,
|
|
||||||
element,
|
|
||||||
elementsMap,
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
case "ellipse": {
|
case "ellipse": {
|
||||||
const [x1, y1, x2, y2] = getElementAbsoluteCoords(element, elementsMap);
|
const [x1, y1, x2, y2] = getElementAbsoluteCoords(element, elementsMap);
|
||||||
const width = x2 - x1;
|
const width = x2 - x1;
|
||||||
const height = y2 - y1;
|
const height = y2 - y1;
|
||||||
|
|
||||||
context.strokeStyle = "rgba(0,0,0,.05)";
|
|
||||||
context.lineWidth = padding - FIXED_BINDING_DISTANCE;
|
|
||||||
|
|
||||||
strokeEllipseWithRotation(
|
strokeEllipseWithRotation(
|
||||||
context,
|
context,
|
||||||
width + padding + FIXED_BINDING_DISTANCE,
|
width,
|
||||||
height + padding + FIXED_BINDING_DISTANCE,
|
height,
|
||||||
x1 + width / 2,
|
x1 + width / 2,
|
||||||
y1 + height / 2,
|
y1 + height / 2,
|
||||||
element.angle,
|
element.angle,
|
||||||
@@ -241,7 +232,6 @@ const renderBindingHighlightForSuggestedPointBinding = (
|
|||||||
context: CanvasRenderingContext2D,
|
context: CanvasRenderingContext2D,
|
||||||
suggestedBinding: SuggestedPointBinding,
|
suggestedBinding: SuggestedPointBinding,
|
||||||
elementsMap: ElementsMap,
|
elementsMap: ElementsMap,
|
||||||
zoom: InteractiveCanvasAppState["zoom"],
|
|
||||||
) => {
|
) => {
|
||||||
const [element, startOrEnd] = suggestedBinding;
|
const [element, startOrEnd] = suggestedBinding;
|
||||||
|
|
||||||
@@ -343,7 +333,7 @@ const renderBindingHighlight = (
|
|||||||
|
|
||||||
context.save();
|
context.save();
|
||||||
context.translate(appState.scrollX, appState.scrollY);
|
context.translate(appState.scrollX, appState.scrollY);
|
||||||
renderHighlight(context, suggestedBinding as any, elementsMap, appState.zoom);
|
renderHighlight(context, suggestedBinding as any, elementsMap);
|
||||||
|
|
||||||
context.restore();
|
context.restore();
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user