You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
success/packages/excalidraw/renderer/renderSnaps.ts

190 lines
4.8 KiB
TypeScript

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import { PointSnapLine, PointerSnapLine } from "../snapping";
import { InteractiveCanvasAppState, Point } from "../types";
const SNAP_COLOR_LIGHT = "#ff6b6b";
const SNAP_COLOR_DARK = "#ff0000";
const SNAP_WIDTH = 1;
const SNAP_CROSS_SIZE = 2;
export const renderSnaps = (
context: CanvasRenderingContext2D,
appState: InteractiveCanvasAppState,
) => {
if (!appState.snapLines.length) {
return;
}
// in dark mode, we need to adjust the color to account for color inversion.
// Don't change if zen mode, because we draw only crosses, we want the
// colors to be more visible
const snapColor =
appState.theme === "light" || appState.zenModeEnabled
? SNAP_COLOR_LIGHT
: SNAP_COLOR_DARK;
// in zen mode make the cross more visible since we don't draw the lines
const snapWidth =
(appState.zenModeEnabled ? SNAP_WIDTH * 1.5 : SNAP_WIDTH) /
appState.zoom.value;
context.save();
context.translate(appState.scrollX, appState.scrollY);
for (const snapLine of appState.snapLines) {
if (snapLine.type === "pointer") {
context.lineWidth = snapWidth;
context.strokeStyle = snapColor;
drawPointerSnapLine(snapLine, context, appState);
} else if (snapLine.type === "gap") {
context.lineWidth = snapWidth;
context.strokeStyle = snapColor;
drawGapLine(
snapLine.points[0],
snapLine.points[1],
snapLine.direction,
appState,
context,
);
} else if (snapLine.type === "points") {
context.lineWidth = snapWidth;
context.strokeStyle = snapColor;
drawPointsSnapLine(snapLine, context, appState);
}
}
context.restore();
};
const drawPointsSnapLine = (
pointSnapLine: PointSnapLine,
context: CanvasRenderingContext2D,
appState: InteractiveCanvasAppState,
) => {
if (!appState.zenModeEnabled) {
const firstPoint = pointSnapLine.points[0];
const lastPoint = pointSnapLine.points[pointSnapLine.points.length - 1];
drawLine(firstPoint, lastPoint, context);
}
for (const point of pointSnapLine.points) {
drawCross(point, appState, context);
}
};
const drawPointerSnapLine = (
pointerSnapLine: PointerSnapLine,
context: CanvasRenderingContext2D,
appState: InteractiveCanvasAppState,
) => {
drawCross(pointerSnapLine.points[0], appState, context);
if (!appState.zenModeEnabled) {
drawLine(pointerSnapLine.points[0], pointerSnapLine.points[1], context);
}
};
const drawCross = (
[x, y]: Point,
appState: InteractiveCanvasAppState,
context: CanvasRenderingContext2D,
) => {
context.save();
const size =
(appState.zenModeEnabled ? SNAP_CROSS_SIZE * 1.5 : SNAP_CROSS_SIZE) /
appState.zoom.value;
context.beginPath();
context.moveTo(x - size, y - size);
context.lineTo(x + size, y + size);
context.moveTo(x + size, y - size);
context.lineTo(x - size, y + size);
context.stroke();
context.restore();
};
const drawLine = (
from: Point,
to: Point,
context: CanvasRenderingContext2D,
) => {
context.beginPath();
context.lineTo(...from);
context.lineTo(...to);
context.stroke();
};
const drawGapLine = (
from: Point,
to: Point,
direction: "horizontal" | "vertical",
appState: InteractiveCanvasAppState,
context: CanvasRenderingContext2D,
) => {
// a horizontal gap snap line
// ||||
// ^ ^ ^ ^
// \ \ \ \
// (1) (2) (3) (4)
const FULL = 8 / appState.zoom.value;
const HALF = FULL / 2;
const QUARTER = FULL / 4;
if (direction === "horizontal") {
const halfPoint = [(from[0] + to[0]) / 2, from[1]];
// (1)
if (!appState.zenModeEnabled) {
drawLine([from[0], from[1] - FULL], [from[0], from[1] + FULL], context);
}
// (3)
drawLine(
[halfPoint[0] - QUARTER, halfPoint[1] - HALF],
[halfPoint[0] - QUARTER, halfPoint[1] + HALF],
context,
);
drawLine(
[halfPoint[0] + QUARTER, halfPoint[1] - HALF],
[halfPoint[0] + QUARTER, halfPoint[1] + HALF],
context,
);
if (!appState.zenModeEnabled) {
// (4)
drawLine([to[0], to[1] - FULL], [to[0], to[1] + FULL], context);
// (2)
drawLine(from, to, context);
}
} else {
const halfPoint = [from[0], (from[1] + to[1]) / 2];
// (1)
if (!appState.zenModeEnabled) {
drawLine([from[0] - FULL, from[1]], [from[0] + FULL, from[1]], context);
}
// (3)
drawLine(
[halfPoint[0] - HALF, halfPoint[1] - QUARTER],
[halfPoint[0] + HALF, halfPoint[1] - QUARTER],
context,
);
drawLine(
[halfPoint[0] - HALF, halfPoint[1] + QUARTER],
[halfPoint[0] + HALF, halfPoint[1] + QUARTER],
context,
);
if (!appState.zenModeEnabled) {
// (4)
drawLine([to[0] - FULL, to[1]], [to[0] + FULL, to[1]], context);
// (2)
drawLine(from, to, context);
}
}
};