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:
Gasim Gasimzada 2020-01-07 19:04:52 +04:00 committed by GitHub
parent 85365e5bcb
commit 829a65b8cb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 206 additions and 184 deletions

1
src/renderer/index.ts Normal file
View file

@ -0,0 +1 @@
export { renderScene } from "./renderScene";

View 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
View 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;
}
}