mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-05-03 10:00:07 -04:00
Refactor Element Functions (#233)
* Remove `generatedraw` from element object - Create a function that renders a single element - Refactor rendering selected elements * Replace getElementAbsoluteXY with getElementAbsoluteCoords
This commit is contained in:
parent
85365e5bcb
commit
829a65b8cb
14 changed files with 206 additions and 184 deletions
67
src/element/bounds.test.ts
Normal file
67
src/element/bounds.test.ts
Normal file
|
@ -0,0 +1,67 @@
|
|||
import { getElementAbsoluteCoords } from "./bounds";
|
||||
import { ExcalidrawElement } from "./types";
|
||||
|
||||
const _ce = ({ x, y, w, h }: { x: number; y: number; w: number; h: number }) =>
|
||||
({
|
||||
type: "test",
|
||||
strokeColor: "#000",
|
||||
backgroundColor: "#000",
|
||||
fillStyle: "solid",
|
||||
strokeWidth: 1,
|
||||
roughness: 1,
|
||||
opacity: 1,
|
||||
x,
|
||||
y,
|
||||
width: w,
|
||||
height: h
|
||||
} as ExcalidrawElement);
|
||||
|
||||
describe("getElementAbsoluteCoords", () => {
|
||||
it("test x1 coordinate if width is positive or zero", () => {
|
||||
const [x1] = getElementAbsoluteCoords(_ce({ x: 10, y: 0, w: 10, h: 0 }));
|
||||
expect(x1).toEqual(10);
|
||||
});
|
||||
|
||||
it("test x1 coordinate if width is negative", () => {
|
||||
const [x1] = getElementAbsoluteCoords(_ce({ x: 20, y: 0, w: -10, h: 0 }));
|
||||
expect(x1).toEqual(10);
|
||||
});
|
||||
|
||||
it("test x2 coordinate if width is positive or zero", () => {
|
||||
const [, , x2] = getElementAbsoluteCoords(
|
||||
_ce({ x: 10, y: 0, w: 10, h: 0 })
|
||||
);
|
||||
expect(x2).toEqual(20);
|
||||
});
|
||||
|
||||
it("test x2 coordinate if width is negative", () => {
|
||||
const [, , x2] = getElementAbsoluteCoords(
|
||||
_ce({ x: 10, y: 0, w: -10, h: 0 })
|
||||
);
|
||||
expect(x2).toEqual(10);
|
||||
});
|
||||
|
||||
it("test y1 coordinate if height is positive or zero", () => {
|
||||
const [, y1] = getElementAbsoluteCoords(_ce({ x: 0, y: 10, w: 0, h: 10 }));
|
||||
expect(y1).toEqual(10);
|
||||
});
|
||||
|
||||
it("test y1 coordinate if height is negative", () => {
|
||||
const [, y1] = getElementAbsoluteCoords(_ce({ x: 0, y: 20, w: 0, h: -10 }));
|
||||
expect(y1).toEqual(10);
|
||||
});
|
||||
|
||||
it("test y2 coordinate if height is positive or zero", () => {
|
||||
const [, , , y2] = getElementAbsoluteCoords(
|
||||
_ce({ x: 0, y: 10, w: 0, h: 10 })
|
||||
);
|
||||
expect(y2).toEqual(20);
|
||||
});
|
||||
|
||||
it("test y2 coordinate if height is negative", () => {
|
||||
const [, , , y2] = getElementAbsoluteCoords(
|
||||
_ce({ x: 0, y: 10, w: 0, h: -10 })
|
||||
);
|
||||
expect(y2).toEqual(10);
|
||||
});
|
||||
});
|
|
@ -5,17 +5,13 @@ import { rotate } from "../math";
|
|||
// This set of functions retrieves the absolute position of the 4 points.
|
||||
// We can't just always normalize it since we need to remember the fact that an arrow
|
||||
// is pointing left or right.
|
||||
export function getElementAbsoluteX1(element: ExcalidrawElement) {
|
||||
return element.width >= 0 ? element.x : element.x + element.width;
|
||||
}
|
||||
export function getElementAbsoluteX2(element: ExcalidrawElement) {
|
||||
return element.width >= 0 ? element.x + element.width : element.x;
|
||||
}
|
||||
export function getElementAbsoluteY1(element: ExcalidrawElement) {
|
||||
return element.height >= 0 ? element.y : element.y + element.height;
|
||||
}
|
||||
export function getElementAbsoluteY2(element: ExcalidrawElement) {
|
||||
return element.height >= 0 ? element.y + element.height : element.y;
|
||||
export function getElementAbsoluteCoords(element: ExcalidrawElement) {
|
||||
return [
|
||||
element.width >= 0 ? element.x : element.x + element.width, // x1
|
||||
element.height >= 0 ? element.y : element.y + element.height, // y1
|
||||
element.width >= 0 ? element.x + element.width : element.x, // x2
|
||||
element.height >= 0 ? element.y + element.height : element.y // y2
|
||||
];
|
||||
}
|
||||
|
||||
export function getDiamondPoints(element: ExcalidrawElement) {
|
||||
|
|
|
@ -2,12 +2,9 @@ import { distanceBetweenPointAndSegment } from "../math";
|
|||
|
||||
import { ExcalidrawElement } from "./types";
|
||||
import {
|
||||
getElementAbsoluteX1,
|
||||
getElementAbsoluteX2,
|
||||
getElementAbsoluteY1,
|
||||
getElementAbsoluteY2,
|
||||
getArrowPoints,
|
||||
getDiamondPoints
|
||||
getDiamondPoints,
|
||||
getElementAbsoluteCoords
|
||||
} from "./bounds";
|
||||
|
||||
export function hitTest(
|
||||
|
@ -55,10 +52,7 @@ export function hitTest(
|
|||
|
||||
return Math.hypot(a * tx - px, b * ty - py) < lineThreshold;
|
||||
} else if (element.type === "rectangle") {
|
||||
const x1 = getElementAbsoluteX1(element);
|
||||
const x2 = getElementAbsoluteX2(element);
|
||||
const y1 = getElementAbsoluteY1(element);
|
||||
const y2 = getElementAbsoluteY2(element);
|
||||
const [x1, y1, x2, y2] = getElementAbsoluteCoords(element);
|
||||
|
||||
// (x1, y1) --A-- (x2, y1)
|
||||
// |D |B
|
||||
|
@ -109,10 +103,7 @@ export function hitTest(
|
|||
distanceBetweenPointAndSegment(x, y, x4, y4, x2, y2) < lineThreshold
|
||||
);
|
||||
} else if (element.type === "text") {
|
||||
const x1 = getElementAbsoluteX1(element);
|
||||
const x2 = getElementAbsoluteX2(element);
|
||||
const y1 = getElementAbsoluteY1(element);
|
||||
const y2 = getElementAbsoluteY2(element);
|
||||
const [x1, y1, x2, y2] = getElementAbsoluteCoords(element);
|
||||
|
||||
return x >= x1 && x <= x2 && y >= y1 && y <= y2;
|
||||
} else if (element.type === "selection") {
|
||||
|
|
|
@ -1,145 +0,0 @@
|
|||
import rough from "roughjs/bin/wrappers/rough";
|
||||
|
||||
import { withCustomMathRandom } from "../random";
|
||||
|
||||
import { ExcalidrawElement } from "./types";
|
||||
import { isTextElement } from "./typeChecks";
|
||||
import { getDiamondPoints, getArrowPoints } from "./bounds";
|
||||
|
||||
// Casting second argument (DrawingSurface) to any,
|
||||
// because it is requred by TS definitions and not required at runtime
|
||||
const generator = rough.generator(null, null as any);
|
||||
|
||||
export function generateDraw(element: ExcalidrawElement) {
|
||||
if (element.type === "selection") {
|
||||
element.draw = (rc, context, { scrollX, scrollY }) => {
|
||||
const fillStyle = context.fillStyle;
|
||||
context.fillStyle = "rgba(0, 0, 255, 0.10)";
|
||||
context.fillRect(
|
||||
element.x + scrollX,
|
||||
element.y + scrollY,
|
||||
element.width,
|
||||
element.height
|
||||
);
|
||||
context.fillStyle = fillStyle;
|
||||
};
|
||||
} else if (element.type === "rectangle") {
|
||||
const shape = withCustomMathRandom(element.seed, () => {
|
||||
return generator.rectangle(0, 0, element.width, element.height, {
|
||||
stroke: element.strokeColor,
|
||||
fill: element.backgroundColor,
|
||||
fillStyle: element.fillStyle,
|
||||
strokeWidth: element.strokeWidth,
|
||||
roughness: element.roughness
|
||||
});
|
||||
});
|
||||
element.draw = (rc, context, { scrollX, scrollY }) => {
|
||||
context.globalAlpha = element.opacity / 100;
|
||||
context.translate(element.x + scrollX, element.y + scrollY);
|
||||
rc.draw(shape);
|
||||
context.translate(-element.x - scrollX, -element.y - scrollY);
|
||||
context.globalAlpha = 1;
|
||||
};
|
||||
} else if (element.type === "diamond") {
|
||||
const shape = withCustomMathRandom(element.seed, () => {
|
||||
const [
|
||||
topX,
|
||||
topY,
|
||||
rightX,
|
||||
rightY,
|
||||
bottomX,
|
||||
bottomY,
|
||||
leftX,
|
||||
leftY
|
||||
] = getDiamondPoints(element);
|
||||
return generator.polygon(
|
||||
[
|
||||
[topX, topY],
|
||||
[rightX, rightY],
|
||||
[bottomX, bottomY],
|
||||
[leftX, leftY]
|
||||
],
|
||||
{
|
||||
stroke: element.strokeColor,
|
||||
fill: element.backgroundColor,
|
||||
fillStyle: element.fillStyle,
|
||||
strokeWidth: element.strokeWidth,
|
||||
roughness: element.roughness
|
||||
}
|
||||
);
|
||||
});
|
||||
element.draw = (rc, context, { scrollX, scrollY }) => {
|
||||
context.globalAlpha = element.opacity / 100;
|
||||
context.translate(element.x + scrollX, element.y + scrollY);
|
||||
rc.draw(shape);
|
||||
context.translate(-element.x - scrollX, -element.y - scrollY);
|
||||
context.globalAlpha = 1;
|
||||
};
|
||||
} else if (element.type === "ellipse") {
|
||||
const shape = withCustomMathRandom(element.seed, () =>
|
||||
generator.ellipse(
|
||||
element.width / 2,
|
||||
element.height / 2,
|
||||
element.width,
|
||||
element.height,
|
||||
{
|
||||
stroke: element.strokeColor,
|
||||
fill: element.backgroundColor,
|
||||
fillStyle: element.fillStyle,
|
||||
strokeWidth: element.strokeWidth,
|
||||
roughness: element.roughness
|
||||
}
|
||||
)
|
||||
);
|
||||
element.draw = (rc, context, { scrollX, scrollY }) => {
|
||||
context.globalAlpha = element.opacity / 100;
|
||||
context.translate(element.x + scrollX, element.y + scrollY);
|
||||
rc.draw(shape);
|
||||
context.translate(-element.x - scrollX, -element.y - scrollY);
|
||||
context.globalAlpha = 1;
|
||||
};
|
||||
} else if (element.type === "arrow") {
|
||||
const [x1, y1, x2, y2, x3, y3, x4, y4] = getArrowPoints(element);
|
||||
const options = {
|
||||
stroke: element.strokeColor,
|
||||
strokeWidth: element.strokeWidth,
|
||||
roughness: element.roughness
|
||||
};
|
||||
|
||||
const shapes = withCustomMathRandom(element.seed, () => [
|
||||
// \
|
||||
generator.line(x3, y3, x2, y2, options),
|
||||
// -----
|
||||
generator.line(x1, y1, x2, y2, options),
|
||||
// /
|
||||
generator.line(x4, y4, x2, y2, options)
|
||||
]);
|
||||
|
||||
element.draw = (rc, context, { scrollX, scrollY }) => {
|
||||
context.globalAlpha = element.opacity / 100;
|
||||
context.translate(element.x + scrollX, element.y + scrollY);
|
||||
shapes.forEach(shape => rc.draw(shape));
|
||||
context.translate(-element.x - scrollX, -element.y - scrollY);
|
||||
context.globalAlpha = 1;
|
||||
};
|
||||
return;
|
||||
} else if (isTextElement(element)) {
|
||||
element.draw = (rc, context, { scrollX, scrollY }) => {
|
||||
context.globalAlpha = element.opacity / 100;
|
||||
const font = context.font;
|
||||
context.font = element.font;
|
||||
const fillStyle = context.fillStyle;
|
||||
context.fillStyle = element.strokeColor;
|
||||
context.fillText(
|
||||
element.text,
|
||||
element.x + scrollX,
|
||||
element.y + element.actualBoundingBoxAscent + scrollY
|
||||
);
|
||||
context.fillStyle = fillStyle;
|
||||
context.font = font;
|
||||
context.globalAlpha = 1;
|
||||
};
|
||||
} else {
|
||||
throw new Error("Unimplemented type " + element.type);
|
||||
}
|
||||
}
|
|
@ -1,9 +1,6 @@
|
|||
export { newElement } from "./newElement";
|
||||
export {
|
||||
getElementAbsoluteX1,
|
||||
getElementAbsoluteX2,
|
||||
getElementAbsoluteY1,
|
||||
getElementAbsoluteY2,
|
||||
getElementAbsoluteCoords,
|
||||
getDiamondPoints,
|
||||
getArrowPoints
|
||||
} from "./bounds";
|
||||
|
@ -11,5 +8,4 @@ export {
|
|||
export { handlerRectangles } from "./handlerRectangles";
|
||||
export { hitTest } from "./collision";
|
||||
export { resizeTest } from "./resizeTest";
|
||||
export { generateDraw } from "./generateDraw";
|
||||
export { isTextElement } from "./typeChecks";
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
import { RoughCanvas } from "roughjs/bin/canvas";
|
||||
|
||||
import { SceneState } from "../scene/types";
|
||||
import { randomSeed } from "../random";
|
||||
|
||||
export function newElement(
|
||||
|
@ -17,24 +14,19 @@ export function newElement(
|
|||
height = 0
|
||||
) {
|
||||
const element = {
|
||||
type: type,
|
||||
x: x,
|
||||
y: y,
|
||||
width: width,
|
||||
height: height,
|
||||
type,
|
||||
x,
|
||||
y,
|
||||
width,
|
||||
height,
|
||||
strokeColor,
|
||||
backgroundColor,
|
||||
fillStyle,
|
||||
strokeWidth,
|
||||
roughness,
|
||||
opacity,
|
||||
isSelected: false,
|
||||
strokeColor: strokeColor,
|
||||
backgroundColor: backgroundColor,
|
||||
fillStyle: fillStyle,
|
||||
strokeWidth: strokeWidth,
|
||||
roughness: roughness,
|
||||
opacity: opacity,
|
||||
seed: randomSeed(),
|
||||
draw(
|
||||
rc: RoughCanvas,
|
||||
context: CanvasRenderingContext2D,
|
||||
sceneState: SceneState
|
||||
) {}
|
||||
seed: randomSeed()
|
||||
};
|
||||
return element;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue