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
1
src/renderer/index.ts
Normal file
1
src/renderer/index.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export { renderScene } from "./renderScene";
|
142
src/renderer/renderElement.ts
Normal file
142
src/renderer/renderElement.ts
Normal file
|
@ -0,0 +1,142 @@
|
|||
import rough from "roughjs/bin/wrappers/rough";
|
||||
|
||||
import { withCustomMathRandom } from "../random";
|
||||
|
||||
import { ExcalidrawElement } from "../element/types";
|
||||
import { isTextElement } from "../element/typeChecks";
|
||||
import { getDiamondPoints, getArrowPoints } from "../element/bounds";
|
||||
import { RoughCanvas } from "roughjs/bin/canvas";
|
||||
import { SceneState } from "../scene/types";
|
||||
|
||||
// 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 renderElement(
|
||||
element: ExcalidrawElement,
|
||||
rc: RoughCanvas,
|
||||
context: CanvasRenderingContext2D,
|
||||
{ scrollX, scrollY }: SceneState
|
||||
) {
|
||||
if (element.type === "selection") {
|
||||
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
|
||||
});
|
||||
});
|
||||
|
||||
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
|
||||
}
|
||||
);
|
||||
});
|
||||
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
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
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)
|
||||
]);
|
||||
|
||||
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)) {
|
||||
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);
|
||||
}
|
||||
}
|
113
src/renderer/renderScene.ts
Normal file
113
src/renderer/renderScene.ts
Normal file
|
@ -0,0 +1,113 @@
|
|||
import { RoughCanvas } from "roughjs/bin/canvas";
|
||||
|
||||
import { ExcalidrawElement } from "../element/types";
|
||||
import { getElementAbsoluteCoords, handlerRectangles } from "../element";
|
||||
|
||||
import { roundRect } from "../scene/roundRect";
|
||||
import { SceneState } from "../scene/types";
|
||||
import {
|
||||
getScrollBars,
|
||||
SCROLLBAR_COLOR,
|
||||
SCROLLBAR_WIDTH
|
||||
} from "../scene/scrollbars";
|
||||
|
||||
import { renderElement } from "./renderElement";
|
||||
|
||||
export function renderScene(
|
||||
elements: ExcalidrawElement[],
|
||||
rc: RoughCanvas,
|
||||
canvas: HTMLCanvasElement,
|
||||
sceneState: SceneState,
|
||||
// extra options, currently passed by export helper
|
||||
{
|
||||
offsetX,
|
||||
offsetY,
|
||||
renderScrollbars = true,
|
||||
renderSelection = true
|
||||
}: {
|
||||
offsetX?: number;
|
||||
offsetY?: number;
|
||||
renderScrollbars?: boolean;
|
||||
renderSelection?: boolean;
|
||||
} = {}
|
||||
) {
|
||||
if (!canvas) return;
|
||||
const context = canvas.getContext("2d")!;
|
||||
|
||||
const fillStyle = context.fillStyle;
|
||||
if (typeof sceneState.viewBackgroundColor === "string") {
|
||||
context.fillStyle = sceneState.viewBackgroundColor;
|
||||
context.fillRect(0, 0, canvas.width, canvas.height);
|
||||
} else {
|
||||
context.clearRect(0, 0, canvas.width, canvas.height);
|
||||
}
|
||||
context.fillStyle = fillStyle;
|
||||
|
||||
sceneState = {
|
||||
...sceneState,
|
||||
scrollX: typeof offsetX === "number" ? offsetX : sceneState.scrollX,
|
||||
scrollY: typeof offsetY === "number" ? offsetY : sceneState.scrollY
|
||||
};
|
||||
|
||||
elements.forEach(element => {
|
||||
renderElement(element, rc, context, sceneState);
|
||||
});
|
||||
|
||||
if (renderSelection) {
|
||||
const selectedElements = elements.filter(el => el.isSelected);
|
||||
|
||||
selectedElements.forEach(element => {
|
||||
const margin = 4;
|
||||
|
||||
const [
|
||||
elementX1,
|
||||
elementY1,
|
||||
elementX2,
|
||||
elementY2
|
||||
] = getElementAbsoluteCoords(element);
|
||||
const lineDash = context.getLineDash();
|
||||
context.setLineDash([8, 4]);
|
||||
context.strokeRect(
|
||||
elementX1 - margin + sceneState.scrollX,
|
||||
elementY1 - margin + sceneState.scrollY,
|
||||
elementX2 - elementX1 + margin * 2,
|
||||
elementY2 - elementY1 + margin * 2
|
||||
);
|
||||
context.setLineDash(lineDash);
|
||||
});
|
||||
|
||||
if (selectedElements.length === 1 && selectedElements[0].type !== "text") {
|
||||
const handlers = handlerRectangles(selectedElements[0], sceneState);
|
||||
Object.values(handlers).forEach(handler => {
|
||||
context.strokeRect(handler[0], handler[1], handler[2], handler[3]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (renderScrollbars) {
|
||||
const scrollBars = getScrollBars(
|
||||
elements,
|
||||
context.canvas.width / window.devicePixelRatio,
|
||||
context.canvas.height / window.devicePixelRatio,
|
||||
sceneState.scrollX,
|
||||
sceneState.scrollY
|
||||
);
|
||||
|
||||
const strokeStyle = context.strokeStyle;
|
||||
context.fillStyle = SCROLLBAR_COLOR;
|
||||
context.strokeStyle = "rgba(255,255,255,0.8)";
|
||||
[scrollBars.horizontal, scrollBars.vertical].forEach(scrollBar => {
|
||||
if (scrollBar)
|
||||
roundRect(
|
||||
context,
|
||||
scrollBar.x,
|
||||
scrollBar.y,
|
||||
scrollBar.width,
|
||||
scrollBar.height,
|
||||
SCROLLBAR_WIDTH / 2
|
||||
);
|
||||
});
|
||||
context.strokeStyle = strokeStyle;
|
||||
context.fillStyle = fillStyle;
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue