mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-11-19 20:24:49 +01:00
feat: Binding highlight band re-added
This commit is contained in:
@@ -104,10 +104,33 @@ export type BindingStrategy =
|
|||||||
|
|
||||||
export const FIXED_BINDING_DISTANCE = 5;
|
export const FIXED_BINDING_DISTANCE = 5;
|
||||||
|
|
||||||
|
export const BINDING_HIGHLIGHT_THICKNESS = 10;
|
||||||
|
|
||||||
export const getFixedBindingDistance = (
|
export const getFixedBindingDistance = (
|
||||||
element: ExcalidrawBindableElement,
|
element: ExcalidrawBindableElement,
|
||||||
): number => FIXED_BINDING_DISTANCE + element.strokeWidth / 2;
|
): number => FIXED_BINDING_DISTANCE + element.strokeWidth / 2;
|
||||||
|
|
||||||
|
export const maxBindingGap_simple = (
|
||||||
|
element: ExcalidrawElement,
|
||||||
|
elementWidth: number,
|
||||||
|
elementHeight: number,
|
||||||
|
zoom?: AppState["zoom"],
|
||||||
|
): number => {
|
||||||
|
const zoomValue = zoom?.value && zoom.value < 1 ? zoom.value : 1;
|
||||||
|
|
||||||
|
// Aligns diamonds with rectangles
|
||||||
|
const shapeRatio = element.type === "diamond" ? 1 / Math.sqrt(2) : 1;
|
||||||
|
const smallerDimension = shapeRatio * Math.min(elementWidth, elementHeight);
|
||||||
|
|
||||||
|
return Math.max(
|
||||||
|
16,
|
||||||
|
// bigger bindable boundary for bigger elements
|
||||||
|
Math.min(0.25 * smallerDimension, 32),
|
||||||
|
// keep in sync with the zoomed highlight
|
||||||
|
BINDING_HIGHLIGHT_THICKNESS / zoomValue + FIXED_BINDING_DISTANCE,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export const shouldEnableBindingForPointerEvent = (
|
export const shouldEnableBindingForPointerEvent = (
|
||||||
event: React.PointerEvent<HTMLElement>,
|
event: React.PointerEvent<HTMLElement>,
|
||||||
) => {
|
) => {
|
||||||
@@ -626,7 +649,7 @@ const getBindingStrategyForDraggingBindingElementEndpoints_simple = (
|
|||||||
globalPoint,
|
globalPoint,
|
||||||
elements,
|
elements,
|
||||||
elementsMap,
|
elementsMap,
|
||||||
(e) => 100, // TODO: Zoom-level
|
(e) => maxBindingGap_simple(e, e.width, e.height, appState.zoom),
|
||||||
);
|
);
|
||||||
const current: BindingStrategy = hit
|
const current: BindingStrategy = hit
|
||||||
? isPointInElement(globalPoint, hit, elementsMap)
|
? isPointInElement(globalPoint, hit, elementsMap)
|
||||||
|
|||||||
@@ -2126,7 +2126,11 @@ const pointDraggingUpdates = (
|
|||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (startIsDragged) {
|
if (
|
||||||
|
startIsDragged &&
|
||||||
|
(updates.startBinding.mode === "orbit" ||
|
||||||
|
!getFeatureFlag("COMPLEX_BINDINGS"))
|
||||||
|
) {
|
||||||
updates.suggestedBinding = start.element;
|
updates.suggestedBinding = start.element;
|
||||||
}
|
}
|
||||||
} else if (startIsDragged) {
|
} else if (startIsDragged) {
|
||||||
@@ -2148,7 +2152,11 @@ const pointDraggingUpdates = (
|
|||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (endIsDragged && updates.endBinding.mode === "orbit") {
|
if (
|
||||||
|
endIsDragged &&
|
||||||
|
(updates.endBinding.mode === "orbit" ||
|
||||||
|
!getFeatureFlag("COMPLEX_BINDINGS"))
|
||||||
|
) {
|
||||||
updates.suggestedBinding = end.element;
|
updates.suggestedBinding = end.element;
|
||||||
}
|
}
|
||||||
} else if (endIsDragged) {
|
} else if (endIsDragged) {
|
||||||
|
|||||||
@@ -1,5 +1,26 @@
|
|||||||
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 { elementCenterPoint, getCornerRadius } from "@excalidraw/element";
|
||||||
|
|
||||||
|
import {
|
||||||
|
curve,
|
||||||
|
curveCatmullRomCubicApproxPoints,
|
||||||
|
curveCatmullRomQuadraticApproxPoints,
|
||||||
|
curveOffsetPoints,
|
||||||
|
type GlobalPoint,
|
||||||
|
offsetPointsForQuadraticBezier,
|
||||||
|
pointFrom,
|
||||||
|
pointRotateRads,
|
||||||
|
} from "@excalidraw/math";
|
||||||
|
|
||||||
|
import type {
|
||||||
|
ElementsMap,
|
||||||
|
ExcalidrawDiamondElement,
|
||||||
|
ExcalidrawRectanguloidElement,
|
||||||
|
} from "@excalidraw/element/types";
|
||||||
|
|
||||||
import type { StaticCanvasRenderConfig } from "../scene/types";
|
import type { StaticCanvasRenderConfig } from "../scene/types";
|
||||||
import type { AppState, StaticCanvasAppState } from "../types";
|
import type { AppState, StaticCanvasAppState } from "../types";
|
||||||
|
|
||||||
@@ -76,7 +97,7 @@ export const bootstrapCanvas = ({
|
|||||||
return context;
|
return context;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const strokeRectWithRotation = (
|
export const strokeRectWithRotation_simple = (
|
||||||
context: CanvasRenderingContext2D,
|
context: CanvasRenderingContext2D,
|
||||||
x: number,
|
x: number,
|
||||||
y: number,
|
y: number,
|
||||||
@@ -105,3 +126,304 @@ export const strokeRectWithRotation = (
|
|||||||
}
|
}
|
||||||
context.restore();
|
context.restore();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function drawCatmullRomQuadraticApprox(
|
||||||
|
ctx: CanvasRenderingContext2D,
|
||||||
|
points: GlobalPoint[],
|
||||||
|
tension = 0.5,
|
||||||
|
) {
|
||||||
|
const pointSets = curveCatmullRomQuadraticApproxPoints(points, tension);
|
||||||
|
if (pointSets) {
|
||||||
|
for (let i = 0; i < pointSets.length - 1; i++) {
|
||||||
|
const [[cpX, cpY], [p2X, p2Y]] = pointSets[i];
|
||||||
|
|
||||||
|
ctx.quadraticCurveTo(cpX, cpY, p2X, p2Y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawCatmullRomCubicApprox(
|
||||||
|
ctx: CanvasRenderingContext2D,
|
||||||
|
points: GlobalPoint[],
|
||||||
|
tension = 0.5,
|
||||||
|
) {
|
||||||
|
const pointSets = curveCatmullRomCubicApproxPoints(points, tension);
|
||||||
|
if (pointSets) {
|
||||||
|
for (let i = 0; i < pointSets.length; i++) {
|
||||||
|
const [[cp1x, cp1y], [cp2x, cp2y], [x, y]] = pointSets[i];
|
||||||
|
ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const drawHighlightForRectWithRotation_simple = (
|
||||||
|
context: CanvasRenderingContext2D,
|
||||||
|
element: ExcalidrawRectanguloidElement,
|
||||||
|
elementsMap: ElementsMap,
|
||||||
|
padding: number,
|
||||||
|
) => {
|
||||||
|
const [x, y] = pointRotateRads(
|
||||||
|
pointFrom<GlobalPoint>(element.x, element.y),
|
||||||
|
elementCenterPoint(element, elementsMap),
|
||||||
|
element.angle,
|
||||||
|
);
|
||||||
|
|
||||||
|
context.save();
|
||||||
|
context.translate(x, y);
|
||||||
|
context.rotate(element.angle);
|
||||||
|
|
||||||
|
let radius = getCornerRadius(
|
||||||
|
Math.min(element.width, element.height),
|
||||||
|
element,
|
||||||
|
);
|
||||||
|
if (radius === 0) {
|
||||||
|
radius = 0.01;
|
||||||
|
}
|
||||||
|
|
||||||
|
context.beginPath();
|
||||||
|
|
||||||
|
{
|
||||||
|
const topLeftApprox = offsetPointsForQuadraticBezier(
|
||||||
|
pointFrom(0, 0 + radius),
|
||||||
|
pointFrom(0, 0),
|
||||||
|
pointFrom(0 + radius, 0),
|
||||||
|
padding,
|
||||||
|
);
|
||||||
|
const topRightApprox = offsetPointsForQuadraticBezier(
|
||||||
|
pointFrom(element.width - radius, 0),
|
||||||
|
pointFrom(element.width, 0),
|
||||||
|
pointFrom(element.width, radius),
|
||||||
|
padding,
|
||||||
|
);
|
||||||
|
const bottomRightApprox = offsetPointsForQuadraticBezier(
|
||||||
|
pointFrom(element.width, element.height - radius),
|
||||||
|
pointFrom(element.width, element.height),
|
||||||
|
pointFrom(element.width - radius, element.height),
|
||||||
|
padding,
|
||||||
|
);
|
||||||
|
const bottomLeftApprox = offsetPointsForQuadraticBezier(
|
||||||
|
pointFrom(radius, element.height),
|
||||||
|
pointFrom(0, element.height),
|
||||||
|
pointFrom(0, element.height - radius),
|
||||||
|
padding,
|
||||||
|
);
|
||||||
|
|
||||||
|
context.moveTo(
|
||||||
|
topLeftApprox[topLeftApprox.length - 1][0],
|
||||||
|
topLeftApprox[topLeftApprox.length - 1][1],
|
||||||
|
);
|
||||||
|
context.lineTo(topRightApprox[0][0], topRightApprox[0][1]);
|
||||||
|
drawCatmullRomQuadraticApprox(context, topRightApprox);
|
||||||
|
context.lineTo(bottomRightApprox[0][0], bottomRightApprox[0][1]);
|
||||||
|
drawCatmullRomQuadraticApprox(context, bottomRightApprox);
|
||||||
|
context.lineTo(bottomLeftApprox[0][0], bottomLeftApprox[0][1]);
|
||||||
|
drawCatmullRomQuadraticApprox(context, bottomLeftApprox);
|
||||||
|
context.lineTo(topLeftApprox[0][0], topLeftApprox[0][1]);
|
||||||
|
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.fill();
|
||||||
|
|
||||||
|
context.restore();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const strokeEllipseWithRotation_simple = (
|
||||||
|
context: CanvasRenderingContext2D,
|
||||||
|
width: number,
|
||||||
|
height: number,
|
||||||
|
cx: number,
|
||||||
|
cy: number,
|
||||||
|
angle: number,
|
||||||
|
) => {
|
||||||
|
context.beginPath();
|
||||||
|
context.ellipse(cx, cy, width / 2, height / 2, angle, 0, Math.PI * 2);
|
||||||
|
context.stroke();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const drawHighlightForDiamondWithRotation_simple = (
|
||||||
|
context: CanvasRenderingContext2D,
|
||||||
|
padding: number,
|
||||||
|
element: ExcalidrawDiamondElement,
|
||||||
|
elementsMap: ElementsMap,
|
||||||
|
) => {
|
||||||
|
const [x, y] = pointRotateRads(
|
||||||
|
pointFrom<GlobalPoint>(element.x, element.y),
|
||||||
|
elementCenterPoint(element, elementsMap),
|
||||||
|
element.angle,
|
||||||
|
);
|
||||||
|
context.save();
|
||||||
|
context.translate(x, y);
|
||||||
|
context.rotate(element.angle);
|
||||||
|
|
||||||
|
{
|
||||||
|
context.beginPath();
|
||||||
|
|
||||||
|
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),
|
||||||
|
),
|
||||||
|
padding,
|
||||||
|
);
|
||||||
|
const rightApprox = curveOffsetPoints(
|
||||||
|
curve(
|
||||||
|
pointFrom(rightX - verticalRadius, rightY - horizontalRadius),
|
||||||
|
pointFrom(rightX, rightY),
|
||||||
|
pointFrom(rightX, rightY),
|
||||||
|
pointFrom(rightX - verticalRadius, rightY + horizontalRadius),
|
||||||
|
),
|
||||||
|
padding,
|
||||||
|
);
|
||||||
|
const bottomApprox = curveOffsetPoints(
|
||||||
|
curve(
|
||||||
|
pointFrom(bottomX + verticalRadius, bottomY - horizontalRadius),
|
||||||
|
pointFrom(bottomX, bottomY),
|
||||||
|
pointFrom(bottomX, bottomY),
|
||||||
|
pointFrom(bottomX - verticalRadius, bottomY - horizontalRadius),
|
||||||
|
),
|
||||||
|
padding,
|
||||||
|
);
|
||||||
|
const leftApprox = curveOffsetPoints(
|
||||||
|
curve(
|
||||||
|
pointFrom(leftX + verticalRadius, leftY + horizontalRadius),
|
||||||
|
pointFrom(leftX, leftY),
|
||||||
|
pointFrom(leftX, leftY),
|
||||||
|
pointFrom(leftX + verticalRadius, leftY - horizontalRadius),
|
||||||
|
),
|
||||||
|
padding,
|
||||||
|
);
|
||||||
|
|
||||||
|
context.moveTo(
|
||||||
|
topApprox[topApprox.length - 1][0],
|
||||||
|
topApprox[topApprox.length - 1][1],
|
||||||
|
);
|
||||||
|
context.lineTo(rightApprox[1][0], rightApprox[1][1]);
|
||||||
|
drawCatmullRomCubicApprox(context, rightApprox);
|
||||||
|
context.lineTo(bottomApprox[1][0], bottomApprox[1][1]);
|
||||||
|
drawCatmullRomCubicApprox(context, bottomApprox);
|
||||||
|
context.lineTo(leftApprox[1][0], leftApprox[1][1]);
|
||||||
|
drawCatmullRomCubicApprox(context, leftApprox);
|
||||||
|
context.lineTo(topApprox[1][0], topApprox[1][1]);
|
||||||
|
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.fill();
|
||||||
|
context.restore();
|
||||||
|
};
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
BIND_MODE_TIMEOUT,
|
BIND_MODE_TIMEOUT,
|
||||||
DEFAULT_TRANSFORM_HANDLE_SPACING,
|
DEFAULT_TRANSFORM_HANDLE_SPACING,
|
||||||
FRAME_STYLE,
|
FRAME_STYLE,
|
||||||
|
getFeatureFlag,
|
||||||
invariant,
|
invariant,
|
||||||
THEME,
|
THEME,
|
||||||
throttleRAF,
|
throttleRAF,
|
||||||
@@ -22,7 +23,9 @@ import {
|
|||||||
deconstructDiamondElement,
|
deconstructDiamondElement,
|
||||||
deconstructRectanguloidElement,
|
deconstructRectanguloidElement,
|
||||||
elementCenterPoint,
|
elementCenterPoint,
|
||||||
|
FIXED_BINDING_DISTANCE,
|
||||||
LinearElementEditor,
|
LinearElementEditor,
|
||||||
|
maxBindingGap_simple,
|
||||||
} from "@excalidraw/element";
|
} from "@excalidraw/element";
|
||||||
import {
|
import {
|
||||||
getOmitSidesForDevice,
|
getOmitSidesForDevice,
|
||||||
@@ -85,9 +88,12 @@ import { getClientColor, renderRemoteCursors } from "../clients";
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
bootstrapCanvas,
|
bootstrapCanvas,
|
||||||
|
drawHighlightForDiamondWithRotation_simple,
|
||||||
|
drawHighlightForRectWithRotation_simple,
|
||||||
fillCircle,
|
fillCircle,
|
||||||
getNormalizedCanvasDimensions,
|
getNormalizedCanvasDimensions,
|
||||||
strokeRectWithRotation,
|
strokeEllipseWithRotation_simple,
|
||||||
|
strokeRectWithRotation_simple,
|
||||||
} from "./helpers";
|
} from "./helpers";
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
@@ -191,7 +197,66 @@ const renderSingleLinearPoint = <Point extends GlobalPoint | LocalPoint>(
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderBindingHighlightForBindableElement = (
|
const renderBindingHighlightForBindableElement_simple = (
|
||||||
|
context: CanvasRenderingContext2D,
|
||||||
|
element: ExcalidrawBindableElement,
|
||||||
|
elementsMap: ElementsMap,
|
||||||
|
zoom: InteractiveCanvasAppState["zoom"],
|
||||||
|
) => {
|
||||||
|
const padding = maxBindingGap_simple(
|
||||||
|
element,
|
||||||
|
element.width,
|
||||||
|
element.height,
|
||||||
|
zoom,
|
||||||
|
);
|
||||||
|
|
||||||
|
context.fillStyle = "rgba(0,0,0,.05)";
|
||||||
|
|
||||||
|
switch (element.type) {
|
||||||
|
case "rectangle":
|
||||||
|
case "text":
|
||||||
|
case "image":
|
||||||
|
case "iframe":
|
||||||
|
case "embeddable":
|
||||||
|
case "frame":
|
||||||
|
case "magicframe":
|
||||||
|
drawHighlightForRectWithRotation_simple(
|
||||||
|
context,
|
||||||
|
element,
|
||||||
|
elementsMap,
|
||||||
|
padding,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case "diamond":
|
||||||
|
drawHighlightForDiamondWithRotation_simple(
|
||||||
|
context,
|
||||||
|
padding,
|
||||||
|
element,
|
||||||
|
elementsMap,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case "ellipse": {
|
||||||
|
const [x1, y1, x2, y2] = getElementAbsoluteCoords(element, elementsMap);
|
||||||
|
const width = x2 - x1;
|
||||||
|
const height = y2 - y1;
|
||||||
|
|
||||||
|
context.strokeStyle = "rgba(0,0,0,.05)";
|
||||||
|
context.lineWidth = padding - FIXED_BINDING_DISTANCE;
|
||||||
|
|
||||||
|
strokeEllipseWithRotation_simple(
|
||||||
|
context,
|
||||||
|
width + padding + FIXED_BINDING_DISTANCE,
|
||||||
|
height + padding + FIXED_BINDING_DISTANCE,
|
||||||
|
x1 + width / 2,
|
||||||
|
y1 + height / 2,
|
||||||
|
element.angle,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderBindingHighlightForBindableElement_complex = (
|
||||||
app: AppClassProperties,
|
app: AppClassProperties,
|
||||||
context: CanvasRenderingContext2D,
|
context: CanvasRenderingContext2D,
|
||||||
element: ExcalidrawBindableElement,
|
element: ExcalidrawBindableElement,
|
||||||
@@ -458,6 +523,38 @@ const renderBindingHighlightForBindableElement = (
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const renderBindingHighlightForBindableElement = (
|
||||||
|
app: AppClassProperties,
|
||||||
|
context: CanvasRenderingContext2D,
|
||||||
|
element: ExcalidrawBindableElement,
|
||||||
|
allElementsMap: NonDeletedSceneElementsMap,
|
||||||
|
appState: InteractiveCanvasAppState,
|
||||||
|
deltaTime: number,
|
||||||
|
state?: { runtime: number },
|
||||||
|
) => {
|
||||||
|
if (getFeatureFlag("COMPLEX_BINDINGS")) {
|
||||||
|
return renderBindingHighlightForBindableElement_complex(
|
||||||
|
app,
|
||||||
|
context,
|
||||||
|
element,
|
||||||
|
allElementsMap,
|
||||||
|
appState,
|
||||||
|
deltaTime,
|
||||||
|
state,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
context.save();
|
||||||
|
context.translate(appState.scrollX, appState.scrollY);
|
||||||
|
renderBindingHighlightForBindableElement_simple(
|
||||||
|
context,
|
||||||
|
element,
|
||||||
|
allElementsMap,
|
||||||
|
appState.zoom,
|
||||||
|
);
|
||||||
|
context.restore();
|
||||||
|
};
|
||||||
|
|
||||||
type ElementSelectionBorder = {
|
type ElementSelectionBorder = {
|
||||||
angle: number;
|
angle: number;
|
||||||
x1: number;
|
x1: number;
|
||||||
@@ -513,7 +610,7 @@ const renderSelectionBorder = (
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
context.lineDashOffset = (lineWidth + spaceWidth) * index;
|
context.lineDashOffset = (lineWidth + spaceWidth) * index;
|
||||||
strokeRectWithRotation(
|
strokeRectWithRotation_simple(
|
||||||
context,
|
context,
|
||||||
x1 - linePadding,
|
x1 - linePadding,
|
||||||
y1 - linePadding,
|
y1 - linePadding,
|
||||||
@@ -542,7 +639,7 @@ const renderFrameHighlight = (
|
|||||||
|
|
||||||
context.save();
|
context.save();
|
||||||
context.translate(appState.scrollX, appState.scrollY);
|
context.translate(appState.scrollX, appState.scrollY);
|
||||||
strokeRectWithRotation(
|
strokeRectWithRotation_simple(
|
||||||
context,
|
context,
|
||||||
x1,
|
x1,
|
||||||
y1,
|
y1,
|
||||||
@@ -755,7 +852,7 @@ const renderTransformHandles = (
|
|||||||
context.fill();
|
context.fill();
|
||||||
context.stroke();
|
context.stroke();
|
||||||
} else {
|
} else {
|
||||||
strokeRectWithRotation(
|
strokeRectWithRotation_simple(
|
||||||
context,
|
context,
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
@@ -1264,7 +1361,7 @@ const _renderInteractiveScene = ({
|
|||||||
const lineWidth = context.lineWidth;
|
const lineWidth = context.lineWidth;
|
||||||
context.lineWidth = 1 / appState.zoom.value;
|
context.lineWidth = 1 / appState.zoom.value;
|
||||||
context.strokeStyle = selectionColor;
|
context.strokeStyle = selectionColor;
|
||||||
strokeRectWithRotation(
|
strokeRectWithRotation_simple(
|
||||||
context,
|
context,
|
||||||
x1 - dashedLinePadding,
|
x1 - dashedLinePadding,
|
||||||
y1 - dashedLinePadding,
|
y1 - dashedLinePadding,
|
||||||
|
|||||||
Reference in New Issue
Block a user