mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-05-03 10:00:07 -04:00
Rounded diamond binding highlight
This commit is contained in:
parent
3428148e4d
commit
6e22710bc8
2 changed files with 151 additions and 27 deletions
|
@ -476,7 +476,11 @@ export const getRectangleBoxAbsoluteCoords = (boxSceneCoords: RectangleBox) => {
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getDiamondPoints = (element: ExcalidrawElement) => {
|
export const getDiamondPoints = (
|
||||||
|
element: ExcalidrawElement,
|
||||||
|
wPadding: number = 0,
|
||||||
|
hPadding: number = 0,
|
||||||
|
) => {
|
||||||
// Here we add +1 to avoid these numbers to be 0
|
// Here we add +1 to avoid these numbers to be 0
|
||||||
// otherwise rough.js will throw an error complaining about it
|
// otherwise rough.js will throw an error complaining about it
|
||||||
const topX = Math.floor(element.width / 2) + 1;
|
const topX = Math.floor(element.width / 2) + 1;
|
||||||
|
@ -488,7 +492,16 @@ export const getDiamondPoints = (element: ExcalidrawElement) => {
|
||||||
const leftX = 0;
|
const leftX = 0;
|
||||||
const leftY = rightY;
|
const leftY = rightY;
|
||||||
|
|
||||||
return [topX, topY, rightX, rightY, bottomX, bottomY, leftX, leftY];
|
return [
|
||||||
|
topX,
|
||||||
|
topY - hPadding,
|
||||||
|
rightX + wPadding,
|
||||||
|
rightY,
|
||||||
|
bottomX,
|
||||||
|
bottomY + hPadding,
|
||||||
|
leftX - wPadding,
|
||||||
|
leftY,
|
||||||
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
// reference: https://eliot-jones.com/2019/12/cubic-bezier-curve-bounding-boxes
|
// reference: https://eliot-jones.com/2019/12/cubic-bezier-curve-bounding-boxes
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import oc from "open-color";
|
import oc from "open-color";
|
||||||
import {
|
import {
|
||||||
pointFrom,
|
pointFrom,
|
||||||
|
pointRotateRads,
|
||||||
type GlobalPoint,
|
type GlobalPoint,
|
||||||
type LocalPoint,
|
type LocalPoint,
|
||||||
type Radians,
|
type Radians,
|
||||||
|
@ -11,6 +12,7 @@ import {
|
||||||
FRAME_STYLE,
|
FRAME_STYLE,
|
||||||
THEME,
|
THEME,
|
||||||
arrayToMap,
|
arrayToMap,
|
||||||
|
elementCenterPoint,
|
||||||
invariant,
|
invariant,
|
||||||
throttleRAF,
|
throttleRAF,
|
||||||
} from "@excalidraw/common";
|
} from "@excalidraw/common";
|
||||||
|
@ -47,6 +49,7 @@ import {
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getCommonBounds,
|
getCommonBounds,
|
||||||
|
getDiamondPoints,
|
||||||
getElementAbsoluteCoords,
|
getElementAbsoluteCoords,
|
||||||
} from "@excalidraw/element/bounds";
|
} from "@excalidraw/element/bounds";
|
||||||
|
|
||||||
|
@ -63,6 +66,7 @@ import type {
|
||||||
import type {
|
import type {
|
||||||
ElementsMap,
|
ElementsMap,
|
||||||
ExcalidrawBindableElement,
|
ExcalidrawBindableElement,
|
||||||
|
ExcalidrawDiamondElement,
|
||||||
ExcalidrawElement,
|
ExcalidrawElement,
|
||||||
ExcalidrawFrameLikeElement,
|
ExcalidrawFrameLikeElement,
|
||||||
ExcalidrawImageElement,
|
ExcalidrawImageElement,
|
||||||
|
@ -191,22 +195,138 @@ const strokeRectWithRotation = (
|
||||||
|
|
||||||
const strokeDiamondWithRotation = (
|
const strokeDiamondWithRotation = (
|
||||||
context: CanvasRenderingContext2D,
|
context: CanvasRenderingContext2D,
|
||||||
width: number,
|
padding: number,
|
||||||
height: number,
|
element: ExcalidrawDiamondElement,
|
||||||
cx: number,
|
|
||||||
cy: number,
|
|
||||||
angle: number,
|
|
||||||
) => {
|
) => {
|
||||||
|
const { width, height } = element;
|
||||||
|
const side = Math.hypot(width, height);
|
||||||
|
const wPaddingMax = (1.8 * (padding * side)) / height;
|
||||||
|
const hPaddingMax = (1.8 * (padding * side)) / width;
|
||||||
|
const [x, y] = pointRotateRads(
|
||||||
|
pointFrom<GlobalPoint>(element.x, element.y),
|
||||||
|
elementCenterPoint(element),
|
||||||
|
element.angle,
|
||||||
|
);
|
||||||
context.save();
|
context.save();
|
||||||
context.translate(cx, cy);
|
context.translate(x, y);
|
||||||
context.rotate(angle);
|
context.rotate(element.angle);
|
||||||
|
|
||||||
|
{
|
||||||
context.beginPath();
|
context.beginPath();
|
||||||
context.moveTo(0, height / 2);
|
|
||||||
context.lineTo(width / 2, 0);
|
const [topX, topY, rightX, rightY, bottomX, bottomY, leftX, leftY] =
|
||||||
context.lineTo(0, -height / 2);
|
getDiamondPoints(element, wPaddingMax, hPaddingMax);
|
||||||
context.lineTo(-width / 2, 0);
|
if (element.roundness) {
|
||||||
|
const verticalRadius = getCornerRadius(Math.abs(topX - leftX), element);
|
||||||
|
const horizontalRadius = getCornerRadius(
|
||||||
|
Math.abs(rightY - topY),
|
||||||
|
element,
|
||||||
|
);
|
||||||
|
|
||||||
|
context.moveTo(topX + verticalRadius, topY + horizontalRadius);
|
||||||
|
context.lineTo(rightX - verticalRadius, rightY - horizontalRadius);
|
||||||
|
context.bezierCurveTo(
|
||||||
|
rightX,
|
||||||
|
rightY,
|
||||||
|
rightX,
|
||||||
|
rightY,
|
||||||
|
rightX - verticalRadius,
|
||||||
|
rightY + horizontalRadius,
|
||||||
|
);
|
||||||
|
context.lineTo(bottomX + verticalRadius, bottomY - horizontalRadius);
|
||||||
|
context.bezierCurveTo(
|
||||||
|
bottomX,
|
||||||
|
bottomY,
|
||||||
|
bottomX,
|
||||||
|
bottomY,
|
||||||
|
bottomX - verticalRadius,
|
||||||
|
bottomY - horizontalRadius,
|
||||||
|
);
|
||||||
|
context.lineTo(leftX + verticalRadius, leftY + horizontalRadius);
|
||||||
|
context.bezierCurveTo(
|
||||||
|
leftX,
|
||||||
|
leftY,
|
||||||
|
leftX,
|
||||||
|
leftY,
|
||||||
|
leftX + verticalRadius,
|
||||||
|
leftY - horizontalRadius,
|
||||||
|
);
|
||||||
|
context.lineTo(topX - verticalRadius, topY + horizontalRadius);
|
||||||
|
context.bezierCurveTo(
|
||||||
|
topX,
|
||||||
|
topY,
|
||||||
|
topX,
|
||||||
|
topY,
|
||||||
|
topX + verticalRadius,
|
||||||
|
topY + horizontalRadius,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
context.moveTo(topX, topY);
|
||||||
|
context.lineTo(rightX, rightY);
|
||||||
|
context.lineTo(bottomX, bottomY);
|
||||||
|
context.lineTo(leftX, leftY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const [topX, topY, rightX, rightY, bottomX, bottomY, leftX, leftY] =
|
||||||
|
getDiamondPoints(element, 5, 5);
|
||||||
|
if (element.roundness) {
|
||||||
|
const verticalRadius = getCornerRadius(Math.abs(topX - leftX), element);
|
||||||
|
const horizontalRadius = getCornerRadius(
|
||||||
|
Math.abs(rightY - topY),
|
||||||
|
element,
|
||||||
|
);
|
||||||
|
|
||||||
|
context.moveTo(topX - verticalRadius, topY + horizontalRadius);
|
||||||
|
context.lineTo(leftX + verticalRadius, leftY - horizontalRadius);
|
||||||
|
context.bezierCurveTo(
|
||||||
|
leftX,
|
||||||
|
leftY,
|
||||||
|
leftX,
|
||||||
|
leftY,
|
||||||
|
leftX + verticalRadius,
|
||||||
|
leftY + horizontalRadius,
|
||||||
|
);
|
||||||
|
context.lineTo(bottomX - verticalRadius, bottomY - horizontalRadius);
|
||||||
|
context.bezierCurveTo(
|
||||||
|
bottomX,
|
||||||
|
bottomY,
|
||||||
|
bottomX,
|
||||||
|
bottomY,
|
||||||
|
bottomX + verticalRadius,
|
||||||
|
bottomY - horizontalRadius,
|
||||||
|
);
|
||||||
|
context.lineTo(rightX - verticalRadius, rightY + horizontalRadius);
|
||||||
|
context.bezierCurveTo(
|
||||||
|
rightX,
|
||||||
|
rightY,
|
||||||
|
rightX,
|
||||||
|
rightY,
|
||||||
|
rightX - verticalRadius,
|
||||||
|
rightY - horizontalRadius,
|
||||||
|
);
|
||||||
|
context.lineTo(topX + verticalRadius, topY + horizontalRadius);
|
||||||
|
context.bezierCurveTo(
|
||||||
|
topX,
|
||||||
|
topY,
|
||||||
|
topX,
|
||||||
|
topY,
|
||||||
|
topX - verticalRadius,
|
||||||
|
topY + horizontalRadius,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
context.moveTo(topX, topY);
|
||||||
|
context.lineTo(rightX, rightY);
|
||||||
|
context.lineTo(bottomX, bottomY);
|
||||||
|
context.lineTo(leftX, leftY);
|
||||||
|
}
|
||||||
|
|
||||||
context.closePath();
|
context.closePath();
|
||||||
context.stroke();
|
context.fill();
|
||||||
|
//context.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
context.restore();
|
context.restore();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -260,6 +380,7 @@ const renderBindingHighlightForBindableElement = (
|
||||||
const height = y2 - y1;
|
const height = y2 - y1;
|
||||||
|
|
||||||
context.strokeStyle = "rgba(0,0,0,.05)";
|
context.strokeStyle = "rgba(0,0,0,.05)";
|
||||||
|
context.fillStyle = "rgba(0,0,0,.05)";
|
||||||
// When zooming out, make line width greater for visibility
|
// When zooming out, make line width greater for visibility
|
||||||
context.lineWidth =
|
context.lineWidth =
|
||||||
maxBindingGap(element, element.width, element.height, zoom) -
|
maxBindingGap(element, element.width, element.height, zoom) -
|
||||||
|
@ -294,17 +415,7 @@ const renderBindingHighlightForBindableElement = (
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case "diamond":
|
case "diamond":
|
||||||
const side = Math.hypot(width, height);
|
strokeDiamondWithRotation(context, padding, element);
|
||||||
const wPadding = (padding * side) / height;
|
|
||||||
const hPadding = (padding * side) / width;
|
|
||||||
strokeDiamondWithRotation(
|
|
||||||
context,
|
|
||||||
width + wPadding * 2,
|
|
||||||
height + hPadding * 2,
|
|
||||||
x1 + width / 2,
|
|
||||||
y1 + height / 2,
|
|
||||||
element.angle,
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
case "ellipse":
|
case "ellipse":
|
||||||
strokeEllipseWithRotation(
|
strokeEllipseWithRotation(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue