mirror of
				https://github.com/excalidraw/excalidraw.git
				synced 2025-11-04 12:54:23 +01:00 
			
		
		
		
	Binding highlight refactor
This commit is contained in:
		@@ -1,6 +1,5 @@
 | 
			
		||||
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";
 | 
			
		||||
 | 
			
		||||
@@ -130,7 +129,6 @@ export const drawHighlightForRectWithRotation = (
 | 
			
		||||
  context: CanvasRenderingContext2D,
 | 
			
		||||
  element: ExcalidrawRectanguloidElement,
 | 
			
		||||
  elementsMap: ElementsMap,
 | 
			
		||||
  padding: number,
 | 
			
		||||
) => {
 | 
			
		||||
  const [x, y] = pointRotateRads(
 | 
			
		||||
    pointFrom<GlobalPoint>(element.x, element.y),
 | 
			
		||||
@@ -157,25 +155,25 @@ export const drawHighlightForRectWithRotation = (
 | 
			
		||||
      pointFrom(0, 0 + radius),
 | 
			
		||||
      pointFrom(0, 0),
 | 
			
		||||
      pointFrom(0 + radius, 0),
 | 
			
		||||
      padding,
 | 
			
		||||
      0,
 | 
			
		||||
    );
 | 
			
		||||
    const topRightApprox = offsetPointsForQuadraticBezier(
 | 
			
		||||
      pointFrom(element.width - radius, 0),
 | 
			
		||||
      pointFrom(element.width, 0),
 | 
			
		||||
      pointFrom(element.width, radius),
 | 
			
		||||
      padding,
 | 
			
		||||
      0,
 | 
			
		||||
    );
 | 
			
		||||
    const bottomRightApprox = offsetPointsForQuadraticBezier(
 | 
			
		||||
      pointFrom(element.width, element.height - radius),
 | 
			
		||||
      pointFrom(element.width, element.height),
 | 
			
		||||
      pointFrom(element.width - radius, element.height),
 | 
			
		||||
      padding,
 | 
			
		||||
      0,
 | 
			
		||||
    );
 | 
			
		||||
    const bottomLeftApprox = offsetPointsForQuadraticBezier(
 | 
			
		||||
      pointFrom(radius, element.height),
 | 
			
		||||
      pointFrom(0, element.height),
 | 
			
		||||
      pointFrom(0, element.height - radius),
 | 
			
		||||
      padding,
 | 
			
		||||
      0,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    context.moveTo(
 | 
			
		||||
@@ -192,51 +190,8 @@ export const drawHighlightForRectWithRotation = (
 | 
			
		||||
    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.stroke();
 | 
			
		||||
 | 
			
		||||
  context.restore();
 | 
			
		||||
};
 | 
			
		||||
@@ -286,7 +241,6 @@ export const strokeRectWithRotation = (
 | 
			
		||||
 | 
			
		||||
export const drawHighlightForDiamondWithRotation = (
 | 
			
		||||
  context: CanvasRenderingContext2D,
 | 
			
		||||
  padding: number,
 | 
			
		||||
  element: ExcalidrawDiamondElement,
 | 
			
		||||
  elementsMap: ElementsMap,
 | 
			
		||||
) => {
 | 
			
		||||
@@ -317,7 +271,7 @@ export const drawHighlightForDiamondWithRotation = (
 | 
			
		||||
        pointFrom(topX, topY),
 | 
			
		||||
        pointFrom(topX + verticalRadius, topY + horizontalRadius),
 | 
			
		||||
      ),
 | 
			
		||||
      padding,
 | 
			
		||||
      0,
 | 
			
		||||
    );
 | 
			
		||||
    const rightApprox = curveOffsetPoints(
 | 
			
		||||
      curve(
 | 
			
		||||
@@ -326,7 +280,7 @@ export const drawHighlightForDiamondWithRotation = (
 | 
			
		||||
        pointFrom(rightX, rightY),
 | 
			
		||||
        pointFrom(rightX - verticalRadius, rightY + horizontalRadius),
 | 
			
		||||
      ),
 | 
			
		||||
      padding,
 | 
			
		||||
      0,
 | 
			
		||||
    );
 | 
			
		||||
    const bottomApprox = curveOffsetPoints(
 | 
			
		||||
      curve(
 | 
			
		||||
@@ -335,7 +289,7 @@ export const drawHighlightForDiamondWithRotation = (
 | 
			
		||||
        pointFrom(bottomX, bottomY),
 | 
			
		||||
        pointFrom(bottomX - verticalRadius, bottomY - horizontalRadius),
 | 
			
		||||
      ),
 | 
			
		||||
      padding,
 | 
			
		||||
      0,
 | 
			
		||||
    );
 | 
			
		||||
    const leftApprox = curveOffsetPoints(
 | 
			
		||||
      curve(
 | 
			
		||||
@@ -344,7 +298,7 @@ export const drawHighlightForDiamondWithRotation = (
 | 
			
		||||
        pointFrom(leftX, leftY),
 | 
			
		||||
        pointFrom(leftX + verticalRadius, leftY - horizontalRadius),
 | 
			
		||||
      ),
 | 
			
		||||
      padding,
 | 
			
		||||
      0,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    context.moveTo(
 | 
			
		||||
@@ -361,69 +315,7 @@ export const drawHighlightForDiamondWithRotation = (
 | 
			
		||||
    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.stroke();
 | 
			
		||||
  context.restore();
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -16,7 +16,6 @@ import {
 | 
			
		||||
  throttleRAF,
 | 
			
		||||
} from "@excalidraw/common";
 | 
			
		||||
 | 
			
		||||
import { FIXED_BINDING_DISTANCE } from "@excalidraw/element";
 | 
			
		||||
import { LinearElementEditor } from "@excalidraw/element";
 | 
			
		||||
import {
 | 
			
		||||
  getOmitSidesForDevice,
 | 
			
		||||
@@ -192,11 +191,11 @@ const renderBindingHighlightForBindableElement = (
 | 
			
		||||
  context: CanvasRenderingContext2D,
 | 
			
		||||
  element: ExcalidrawBindableElement,
 | 
			
		||||
  elementsMap: ElementsMap,
 | 
			
		||||
  zoom: InteractiveCanvasAppState["zoom"],
 | 
			
		||||
) => {
 | 
			
		||||
  const padding = 5;
 | 
			
		||||
 | 
			
		||||
  context.fillStyle = "rgba(0,0,0,.05)";
 | 
			
		||||
  context.lineWidth = 1;
 | 
			
		||||
  context.strokeStyle = element.strokeColor;
 | 
			
		||||
  context.shadowColor = element.strokeColor;
 | 
			
		||||
  context.shadowBlur = 10;
 | 
			
		||||
 | 
			
		||||
  switch (element.type) {
 | 
			
		||||
    case "rectangle":
 | 
			
		||||
@@ -206,28 +205,20 @@ const renderBindingHighlightForBindableElement = (
 | 
			
		||||
    case "embeddable":
 | 
			
		||||
    case "frame":
 | 
			
		||||
    case "magicframe":
 | 
			
		||||
      drawHighlightForRectWithRotation(context, element, elementsMap, padding);
 | 
			
		||||
      drawHighlightForRectWithRotation(context, element, elementsMap);
 | 
			
		||||
      break;
 | 
			
		||||
    case "diamond":
 | 
			
		||||
      drawHighlightForDiamondWithRotation(
 | 
			
		||||
        context,
 | 
			
		||||
        padding,
 | 
			
		||||
        element,
 | 
			
		||||
        elementsMap,
 | 
			
		||||
      );
 | 
			
		||||
      drawHighlightForDiamondWithRotation(context, 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(
 | 
			
		||||
        context,
 | 
			
		||||
        width + padding + FIXED_BINDING_DISTANCE,
 | 
			
		||||
        height + padding + FIXED_BINDING_DISTANCE,
 | 
			
		||||
        width,
 | 
			
		||||
        height,
 | 
			
		||||
        x1 + width / 2,
 | 
			
		||||
        y1 + height / 2,
 | 
			
		||||
        element.angle,
 | 
			
		||||
@@ -241,7 +232,6 @@ const renderBindingHighlightForSuggestedPointBinding = (
 | 
			
		||||
  context: CanvasRenderingContext2D,
 | 
			
		||||
  suggestedBinding: SuggestedPointBinding,
 | 
			
		||||
  elementsMap: ElementsMap,
 | 
			
		||||
  zoom: InteractiveCanvasAppState["zoom"],
 | 
			
		||||
) => {
 | 
			
		||||
  const [element, startOrEnd] = suggestedBinding;
 | 
			
		||||
 | 
			
		||||
@@ -343,7 +333,7 @@ const renderBindingHighlight = (
 | 
			
		||||
 | 
			
		||||
  context.save();
 | 
			
		||||
  context.translate(appState.scrollX, appState.scrollY);
 | 
			
		||||
  renderHighlight(context, suggestedBinding as any, elementsMap, appState.zoom);
 | 
			
		||||
  renderHighlight(context, suggestedBinding as any, elementsMap);
 | 
			
		||||
 | 
			
		||||
  context.restore();
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user