mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-05-03 10:00:07 -04:00
Prettier
This commit is contained in:
parent
de114b9f59
commit
8525480ad4
1 changed files with 154 additions and 154 deletions
308
src/index.tsx
308
src/index.tsx
|
@ -1,23 +1,23 @@
|
||||||
import React from 'react';
|
import React from "react";
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from "react-dom";
|
||||||
import rough from 'roughjs/bin/wrappers/rough';
|
import rough from "roughjs/bin/wrappers/rough";
|
||||||
import { RoughCanvas } from 'roughjs/bin/canvas';
|
import { RoughCanvas } from "roughjs/bin/canvas";
|
||||||
import { SketchPicker } from 'react-color';
|
import { SketchPicker } from "react-color";
|
||||||
|
|
||||||
import { moveOneLeft, moveAllLeft, moveOneRight, moveAllRight } from './zindex';
|
import { moveOneLeft, moveAllLeft, moveOneRight, moveAllRight } from "./zindex";
|
||||||
|
|
||||||
import './styles.scss';
|
import "./styles.scss";
|
||||||
|
|
||||||
type ExcalidrawElement = ReturnType<typeof newElement>;
|
type ExcalidrawElement = ReturnType<typeof newElement>;
|
||||||
type ExcalidrawTextElement = ExcalidrawElement & {
|
type ExcalidrawTextElement = ExcalidrawElement & {
|
||||||
type: 'text';
|
type: "text";
|
||||||
font: string;
|
font: string;
|
||||||
text: string;
|
text: string;
|
||||||
actualBoundingBoxAscent: number;
|
actualBoundingBoxAscent: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
const LOCAL_STORAGE_KEY = 'excalidraw';
|
const LOCAL_STORAGE_KEY = "excalidraw";
|
||||||
const LOCAL_STORAGE_KEY_STATE = 'excalidraw-state';
|
const LOCAL_STORAGE_KEY_STATE = "excalidraw-state";
|
||||||
|
|
||||||
const elements = Array.of<ExcalidrawElement>();
|
const elements = Array.of<ExcalidrawElement>();
|
||||||
|
|
||||||
|
@ -112,7 +112,7 @@ function hitTest(element: ExcalidrawElement, x: number, y: number): boolean {
|
||||||
// of the click is less than x pixels of any of the lines that the shape is composed of
|
// of the click is less than x pixels of any of the lines that the shape is composed of
|
||||||
const lineThreshold = 10;
|
const lineThreshold = 10;
|
||||||
|
|
||||||
if (element.type === 'ellipse') {
|
if (element.type === "ellipse") {
|
||||||
// https://stackoverflow.com/a/46007540/232122
|
// https://stackoverflow.com/a/46007540/232122
|
||||||
const px = Math.abs(x - element.x - element.width / 2);
|
const px = Math.abs(x - element.x - element.width / 2);
|
||||||
const py = Math.abs(y - element.y - element.height / 2);
|
const py = Math.abs(y - element.y - element.height / 2);
|
||||||
|
@ -147,7 +147,7 @@ function hitTest(element: ExcalidrawElement, x: number, y: number): boolean {
|
||||||
});
|
});
|
||||||
|
|
||||||
return Math.hypot(a * tx - px, b * ty - py) < lineThreshold;
|
return Math.hypot(a * tx - px, b * ty - py) < lineThreshold;
|
||||||
} else if (element.type === 'rectangle') {
|
} else if (element.type === "rectangle") {
|
||||||
const x1 = getElementAbsoluteX1(element);
|
const x1 = getElementAbsoluteX1(element);
|
||||||
const x2 = getElementAbsoluteX2(element);
|
const x2 = getElementAbsoluteX2(element);
|
||||||
const y1 = getElementAbsoluteY1(element);
|
const y1 = getElementAbsoluteY1(element);
|
||||||
|
@ -162,7 +162,7 @@ function hitTest(element: ExcalidrawElement, x: number, y: number): boolean {
|
||||||
distanceBetweenPointAndSegment(x, y, x2, y2, x1, y2) < lineThreshold || // C
|
distanceBetweenPointAndSegment(x, y, x2, y2, x1, y2) < lineThreshold || // C
|
||||||
distanceBetweenPointAndSegment(x, y, x1, y2, x1, y1) < lineThreshold // D
|
distanceBetweenPointAndSegment(x, y, x1, y2, x1, y1) < lineThreshold // D
|
||||||
);
|
);
|
||||||
} else if (element.type === 'arrow') {
|
} else if (element.type === "arrow") {
|
||||||
let [x1, y1, x2, y2, x3, y3, x4, y4] = getArrowPoints(element);
|
let [x1, y1, x2, y2, x3, y3, x4, y4] = getArrowPoints(element);
|
||||||
// The computation is done at the origin, we need to add a translation
|
// The computation is done at the origin, we need to add a translation
|
||||||
x -= element.x;
|
x -= element.x;
|
||||||
|
@ -176,18 +176,18 @@ function hitTest(element: ExcalidrawElement, x: number, y: number): boolean {
|
||||||
// /
|
// /
|
||||||
distanceBetweenPointAndSegment(x, y, x4, y4, x2, y2) < lineThreshold
|
distanceBetweenPointAndSegment(x, y, x4, y4, x2, y2) < lineThreshold
|
||||||
);
|
);
|
||||||
} else if (element.type === 'text') {
|
} else if (element.type === "text") {
|
||||||
const x1 = getElementAbsoluteX1(element);
|
const x1 = getElementAbsoluteX1(element);
|
||||||
const x2 = getElementAbsoluteX2(element);
|
const x2 = getElementAbsoluteX2(element);
|
||||||
const y1 = getElementAbsoluteY1(element);
|
const y1 = getElementAbsoluteY1(element);
|
||||||
const y2 = getElementAbsoluteY2(element);
|
const y2 = getElementAbsoluteY2(element);
|
||||||
|
|
||||||
return x >= x1 && x <= x2 && y >= y1 && y <= y2;
|
return x >= x1 && x <= x2 && y >= y1 && y <= y2;
|
||||||
} else if (element.type === 'selection') {
|
} else if (element.type === "selection") {
|
||||||
console.warn('This should not happen, we need to investigate why it does.');
|
console.warn("This should not happen, we need to investigate why it does.");
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Unimplemented type ' + element.type);
|
throw new Error("Unimplemented type " + element.type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,7 +197,7 @@ function resizeTest(
|
||||||
y: number,
|
y: number,
|
||||||
sceneState: SceneState
|
sceneState: SceneState
|
||||||
): string | false {
|
): string | false {
|
||||||
if (element.type === 'text' || element.type === 'arrow') return false;
|
if (element.type === "text" || element.type === "arrow") return false;
|
||||||
|
|
||||||
const handlers = handlerRectangles(element, sceneState);
|
const handlers = handlerRectangles(element, sceneState);
|
||||||
|
|
||||||
|
@ -242,7 +242,7 @@ function newElement(
|
||||||
rc: RoughCanvas,
|
rc: RoughCanvas,
|
||||||
context: CanvasRenderingContext2D,
|
context: CanvasRenderingContext2D,
|
||||||
sceneState: SceneState
|
sceneState: SceneState
|
||||||
) {},
|
) {}
|
||||||
};
|
};
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
@ -256,7 +256,7 @@ type SceneState = {
|
||||||
|
|
||||||
const SCROLLBAR_WIDTH = 6;
|
const SCROLLBAR_WIDTH = 6;
|
||||||
const SCROLLBAR_MARGIN = 4;
|
const SCROLLBAR_MARGIN = 4;
|
||||||
const SCROLLBAR_COLOR = 'rgba(0,0,0,0.3)';
|
const SCROLLBAR_COLOR = "rgba(0,0,0,0.3)";
|
||||||
const CANVAS_WINDOW_OFFSET_LEFT = 250;
|
const CANVAS_WINDOW_OFFSET_LEFT = 250;
|
||||||
const CANVAS_WINDOW_OFFSET_TOP = 0;
|
const CANVAS_WINDOW_OFFSET_TOP = 0;
|
||||||
|
|
||||||
|
@ -274,7 +274,7 @@ function getScrollbars(
|
||||||
x: scrollBarX + SCROLLBAR_MARGIN,
|
x: scrollBarX + SCROLLBAR_MARGIN,
|
||||||
y: canvasHeight - SCROLLBAR_WIDTH - SCROLLBAR_MARGIN,
|
y: canvasHeight - SCROLLBAR_WIDTH - SCROLLBAR_MARGIN,
|
||||||
width: scrollBarWidth - SCROLLBAR_MARGIN * 2,
|
width: scrollBarWidth - SCROLLBAR_MARGIN * 2,
|
||||||
height: SCROLLBAR_WIDTH,
|
height: SCROLLBAR_WIDTH
|
||||||
};
|
};
|
||||||
|
|
||||||
// vertical scrollbar
|
// vertical scrollbar
|
||||||
|
@ -285,12 +285,12 @@ function getScrollbars(
|
||||||
x: canvasWidth - SCROLLBAR_WIDTH - SCROLLBAR_MARGIN,
|
x: canvasWidth - SCROLLBAR_WIDTH - SCROLLBAR_MARGIN,
|
||||||
y: scrollBarY + SCROLLBAR_MARGIN,
|
y: scrollBarY + SCROLLBAR_MARGIN,
|
||||||
width: SCROLLBAR_WIDTH,
|
width: SCROLLBAR_WIDTH,
|
||||||
height: scrollBarHeight - SCROLLBAR_WIDTH * 2,
|
height: scrollBarHeight - SCROLLBAR_WIDTH * 2
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
horizontal: horizontalScrollBar,
|
horizontal: horizontalScrollBar,
|
||||||
vertical: verticalScrollBar,
|
vertical: verticalScrollBar
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -306,7 +306,7 @@ function isOverScrollBars(
|
||||||
|
|
||||||
const [isOverHorizontalScrollBar, isOverVerticalScrollBar] = [
|
const [isOverHorizontalScrollBar, isOverVerticalScrollBar] = [
|
||||||
scrollBars.horizontal,
|
scrollBars.horizontal,
|
||||||
scrollBars.vertical,
|
scrollBars.vertical
|
||||||
].map(
|
].map(
|
||||||
scrollBar =>
|
scrollBar =>
|
||||||
scrollBar.x <= x &&
|
scrollBar.x <= x &&
|
||||||
|
@ -317,7 +317,7 @@ function isOverScrollBars(
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isOverHorizontalScrollBar,
|
isOverHorizontalScrollBar,
|
||||||
isOverVerticalScrollBar,
|
isOverVerticalScrollBar
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -335,60 +335,60 @@ function handlerRectangles(element: ExcalidrawElement, sceneState: SceneState) {
|
||||||
const marginY = element.height < 0 ? 8 : -8;
|
const marginY = element.height < 0 ? 8 : -8;
|
||||||
|
|
||||||
if (Math.abs(elementX2 - elementX1) > minimumSize) {
|
if (Math.abs(elementX2 - elementX1) > minimumSize) {
|
||||||
handlers['n'] = [
|
handlers["n"] = [
|
||||||
elementX1 + (elementX2 - elementX1) / 2 + sceneState.scrollX - 4,
|
elementX1 + (elementX2 - elementX1) / 2 + sceneState.scrollX - 4,
|
||||||
elementY1 - margin + sceneState.scrollY + marginY,
|
elementY1 - margin + sceneState.scrollY + marginY,
|
||||||
8,
|
8,
|
||||||
8,
|
8
|
||||||
];
|
];
|
||||||
|
|
||||||
handlers['s'] = [
|
handlers["s"] = [
|
||||||
elementX1 + (elementX2 - elementX1) / 2 + sceneState.scrollX - 4,
|
elementX1 + (elementX2 - elementX1) / 2 + sceneState.scrollX - 4,
|
||||||
elementY2 - margin + sceneState.scrollY - marginY,
|
elementY2 - margin + sceneState.scrollY - marginY,
|
||||||
8,
|
8,
|
||||||
8,
|
8
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Math.abs(elementY2 - elementY1) > minimumSize) {
|
if (Math.abs(elementY2 - elementY1) > minimumSize) {
|
||||||
handlers['w'] = [
|
handlers["w"] = [
|
||||||
elementX1 - margin + sceneState.scrollX + marginX,
|
elementX1 - margin + sceneState.scrollX + marginX,
|
||||||
elementY1 + (elementY2 - elementY1) / 2 + sceneState.scrollY - 4,
|
elementY1 + (elementY2 - elementY1) / 2 + sceneState.scrollY - 4,
|
||||||
8,
|
8,
|
||||||
8,
|
8
|
||||||
];
|
];
|
||||||
|
|
||||||
handlers['e'] = [
|
handlers["e"] = [
|
||||||
elementX2 - margin + sceneState.scrollX - marginX,
|
elementX2 - margin + sceneState.scrollX - marginX,
|
||||||
elementY1 + (elementY2 - elementY1) / 2 + sceneState.scrollY - 4,
|
elementY1 + (elementY2 - elementY1) / 2 + sceneState.scrollY - 4,
|
||||||
8,
|
8,
|
||||||
8,
|
8
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
handlers['nw'] = [
|
handlers["nw"] = [
|
||||||
elementX1 - margin + sceneState.scrollX + marginX,
|
elementX1 - margin + sceneState.scrollX + marginX,
|
||||||
elementY1 - margin + sceneState.scrollY + marginY,
|
elementY1 - margin + sceneState.scrollY + marginY,
|
||||||
8,
|
8,
|
||||||
8,
|
8
|
||||||
]; // nw
|
]; // nw
|
||||||
handlers['ne'] = [
|
handlers["ne"] = [
|
||||||
elementX2 - margin + sceneState.scrollX - marginX,
|
elementX2 - margin + sceneState.scrollX - marginX,
|
||||||
elementY1 - margin + sceneState.scrollY + marginY,
|
elementY1 - margin + sceneState.scrollY + marginY,
|
||||||
8,
|
8,
|
||||||
8,
|
8
|
||||||
]; // ne
|
]; // ne
|
||||||
handlers['sw'] = [
|
handlers["sw"] = [
|
||||||
elementX1 - margin + sceneState.scrollX + marginX,
|
elementX1 - margin + sceneState.scrollX + marginX,
|
||||||
elementY2 - margin + sceneState.scrollY - marginY,
|
elementY2 - margin + sceneState.scrollY - marginY,
|
||||||
8,
|
8,
|
||||||
8,
|
8
|
||||||
]; // sw
|
]; // sw
|
||||||
handlers['se'] = [
|
handlers["se"] = [
|
||||||
elementX2 - margin + sceneState.scrollX - marginX,
|
elementX2 - margin + sceneState.scrollX - marginX,
|
||||||
elementY2 - margin + sceneState.scrollY - marginY,
|
elementY2 - margin + sceneState.scrollY - marginY,
|
||||||
8,
|
8,
|
||||||
8,
|
8
|
||||||
]; // se
|
]; // se
|
||||||
|
|
||||||
return handlers;
|
return handlers;
|
||||||
|
@ -403,7 +403,7 @@ function renderScene(
|
||||||
offsetX,
|
offsetX,
|
||||||
offsetY,
|
offsetY,
|
||||||
renderScrollbars = true,
|
renderScrollbars = true,
|
||||||
renderSelection = true,
|
renderSelection = true
|
||||||
}: {
|
}: {
|
||||||
offsetX?: number;
|
offsetX?: number;
|
||||||
offsetY?: number;
|
offsetY?: number;
|
||||||
|
@ -412,10 +412,10 @@ function renderScene(
|
||||||
} = {}
|
} = {}
|
||||||
) {
|
) {
|
||||||
if (!canvas) return;
|
if (!canvas) return;
|
||||||
const context = canvas.getContext('2d')!;
|
const context = canvas.getContext("2d")!;
|
||||||
|
|
||||||
const fillStyle = context.fillStyle;
|
const fillStyle = context.fillStyle;
|
||||||
if (typeof sceneState.viewBackgroundColor === 'string') {
|
if (typeof sceneState.viewBackgroundColor === "string") {
|
||||||
context.fillStyle = sceneState.viewBackgroundColor;
|
context.fillStyle = sceneState.viewBackgroundColor;
|
||||||
context.fillRect(0, 0, canvas.width, canvas.height);
|
context.fillRect(0, 0, canvas.width, canvas.height);
|
||||||
} else {
|
} else {
|
||||||
|
@ -427,8 +427,8 @@ function renderScene(
|
||||||
|
|
||||||
sceneState = {
|
sceneState = {
|
||||||
...sceneState,
|
...sceneState,
|
||||||
scrollX: typeof offsetX === 'number' ? offsetX : sceneState.scrollX,
|
scrollX: typeof offsetX === "number" ? offsetX : sceneState.scrollX,
|
||||||
scrollY: typeof offsetY === 'number' ? offsetY : sceneState.scrollY,
|
scrollY: typeof offsetY === "number" ? offsetY : sceneState.scrollY
|
||||||
};
|
};
|
||||||
|
|
||||||
elements.forEach(element => {
|
elements.forEach(element => {
|
||||||
|
@ -451,8 +451,8 @@ function renderScene(
|
||||||
context.setLineDash(lineDash);
|
context.setLineDash(lineDash);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
element.type !== 'text' &&
|
element.type !== "text" &&
|
||||||
element.type !== 'arrow' &&
|
element.type !== "arrow" &&
|
||||||
selectedIndices.length === 1
|
selectedIndices.length === 1
|
||||||
) {
|
) {
|
||||||
const handlers = handlerRectangles(element, sceneState);
|
const handlers = handlerRectangles(element, sceneState);
|
||||||
|
@ -492,28 +492,28 @@ function saveAsJSON() {
|
||||||
const serialized = JSON.stringify({
|
const serialized = JSON.stringify({
|
||||||
version: 1,
|
version: 1,
|
||||||
source: window.location.origin,
|
source: window.location.origin,
|
||||||
elements,
|
elements
|
||||||
});
|
});
|
||||||
|
|
||||||
saveFile(
|
saveFile(
|
||||||
'excalidraw.json',
|
"excalidraw.json",
|
||||||
'data:text/plain;charset=utf-8,' + encodeURIComponent(serialized)
|
"data:text/plain;charset=utf-8," + encodeURIComponent(serialized)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadFromJSON() {
|
function loadFromJSON() {
|
||||||
const input = document.createElement('input');
|
const input = document.createElement("input");
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
input.type = 'file';
|
input.type = "file";
|
||||||
input.accept = '.json';
|
input.accept = ".json";
|
||||||
|
|
||||||
input.onchange = () => {
|
input.onchange = () => {
|
||||||
if (!input.files!.length) {
|
if (!input.files!.length) {
|
||||||
alert('A file was not selected.');
|
alert("A file was not selected.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
reader.readAsText(input.files![0], 'utf8');
|
reader.readAsText(input.files![0], "utf8");
|
||||||
};
|
};
|
||||||
|
|
||||||
input.click();
|
input.click();
|
||||||
|
@ -532,7 +532,7 @@ function loadFromJSON() {
|
||||||
function exportAsPNG({
|
function exportAsPNG({
|
||||||
exportBackground,
|
exportBackground,
|
||||||
exportPadding = 10,
|
exportPadding = 10,
|
||||||
viewBackgroundColor,
|
viewBackgroundColor
|
||||||
}: {
|
}: {
|
||||||
exportBackground: boolean;
|
exportBackground: boolean;
|
||||||
exportPadding?: number;
|
exportPadding?: number;
|
||||||
|
@ -540,7 +540,7 @@ function exportAsPNG({
|
||||||
scrollX: number;
|
scrollX: number;
|
||||||
scrollY: number;
|
scrollY: number;
|
||||||
}) {
|
}) {
|
||||||
if (!elements.length) return window.alert('Cannot export empty canvas.');
|
if (!elements.length) return window.alert("Cannot export empty canvas.");
|
||||||
|
|
||||||
// calculate smallest area to fit the contents in
|
// calculate smallest area to fit the contents in
|
||||||
|
|
||||||
|
@ -560,8 +560,8 @@ function exportAsPNG({
|
||||||
return Math.abs(x > y ? x - y : y - x);
|
return Math.abs(x > y ? x - y : y - x);
|
||||||
}
|
}
|
||||||
|
|
||||||
const tempCanvas = document.createElement('canvas');
|
const tempCanvas = document.createElement("canvas");
|
||||||
tempCanvas.style.display = 'none';
|
tempCanvas.style.display = "none";
|
||||||
document.body.appendChild(tempCanvas);
|
document.body.appendChild(tempCanvas);
|
||||||
tempCanvas.width = distance(subCanvasX1, subCanvasX2) + exportPadding * 2;
|
tempCanvas.width = distance(subCanvasX1, subCanvasX2) + exportPadding * 2;
|
||||||
tempCanvas.height = distance(subCanvasY1, subCanvasY2) + exportPadding * 2;
|
tempCanvas.height = distance(subCanvasY1, subCanvasY2) + exportPadding * 2;
|
||||||
|
@ -572,17 +572,17 @@ function exportAsPNG({
|
||||||
{
|
{
|
||||||
viewBackgroundColor: exportBackground ? viewBackgroundColor : null,
|
viewBackgroundColor: exportBackground ? viewBackgroundColor : null,
|
||||||
scrollX: 0,
|
scrollX: 0,
|
||||||
scrollY: 0,
|
scrollY: 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
offsetX: -subCanvasX1 + exportPadding,
|
offsetX: -subCanvasX1 + exportPadding,
|
||||||
offsetY: -subCanvasY1 + exportPadding,
|
offsetY: -subCanvasY1 + exportPadding,
|
||||||
renderScrollbars: false,
|
renderScrollbars: false,
|
||||||
renderSelection: false,
|
renderSelection: false
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
saveFile('excalidraw.png', tempCanvas.toDataURL('image/png'));
|
saveFile("excalidraw.png", tempCanvas.toDataURL("image/png"));
|
||||||
|
|
||||||
// clean up the DOM
|
// clean up the DOM
|
||||||
if (tempCanvas !== canvas) tempCanvas.remove();
|
if (tempCanvas !== canvas) tempCanvas.remove();
|
||||||
|
@ -590,9 +590,9 @@ function exportAsPNG({
|
||||||
|
|
||||||
function saveFile(name: string, data: string) {
|
function saveFile(name: string, data: string) {
|
||||||
// create a temporary <a> elem which we'll use to download the image
|
// create a temporary <a> elem which we'll use to download the image
|
||||||
const link = document.createElement('a');
|
const link = document.createElement("a");
|
||||||
link.setAttribute('download', name);
|
link.setAttribute("download", name);
|
||||||
link.setAttribute('href', data);
|
link.setAttribute("href", data);
|
||||||
link.click();
|
link.click();
|
||||||
|
|
||||||
// clean up
|
// clean up
|
||||||
|
@ -605,7 +605,7 @@ function rotate(x1: number, y1: number, x2: number, y2: number, angle: number) {
|
||||||
// https://math.stackexchange.com/questions/2204520/how-do-i-rotate-a-line-segment-in-a-specific-point-on-the-line
|
// https://math.stackexchange.com/questions/2204520/how-do-i-rotate-a-line-segment-in-a-specific-point-on-the-line
|
||||||
return [
|
return [
|
||||||
(x1 - x2) * Math.cos(angle) - (y1 - y2) * Math.sin(angle) + x2,
|
(x1 - x2) * Math.cos(angle) - (y1 - y2) * Math.sin(angle) + x2,
|
||||||
(x1 - x2) * Math.sin(angle) + (y1 - y2) * Math.cos(angle) + y2,
|
(x1 - x2) * Math.sin(angle) + (y1 - y2) * Math.cos(angle) + y2
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -616,7 +616,7 @@ const generator = rough.generator(null, null as any);
|
||||||
function isTextElement(
|
function isTextElement(
|
||||||
element: ExcalidrawElement
|
element: ExcalidrawElement
|
||||||
): element is ExcalidrawTextElement {
|
): element is ExcalidrawTextElement {
|
||||||
return element.type === 'text';
|
return element.type === "text";
|
||||||
}
|
}
|
||||||
|
|
||||||
function isInputLike(
|
function isInputLike(
|
||||||
|
@ -650,10 +650,10 @@ function getArrowPoints(element: ExcalidrawElement) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateDraw(element: ExcalidrawElement) {
|
function generateDraw(element: ExcalidrawElement) {
|
||||||
if (element.type === 'selection') {
|
if (element.type === "selection") {
|
||||||
element.draw = (rc, context, { scrollX, scrollY }) => {
|
element.draw = (rc, context, { scrollX, scrollY }) => {
|
||||||
const fillStyle = context.fillStyle;
|
const fillStyle = context.fillStyle;
|
||||||
context.fillStyle = 'rgba(0, 0, 255, 0.10)';
|
context.fillStyle = "rgba(0, 0, 255, 0.10)";
|
||||||
context.fillRect(
|
context.fillRect(
|
||||||
element.x + scrollX,
|
element.x + scrollX,
|
||||||
element.y + scrollY,
|
element.y + scrollY,
|
||||||
|
@ -662,11 +662,11 @@ function generateDraw(element: ExcalidrawElement) {
|
||||||
);
|
);
|
||||||
context.fillStyle = fillStyle;
|
context.fillStyle = fillStyle;
|
||||||
};
|
};
|
||||||
} else if (element.type === 'rectangle') {
|
} else if (element.type === "rectangle") {
|
||||||
const shape = withCustomMathRandom(element.seed, () => {
|
const shape = withCustomMathRandom(element.seed, () => {
|
||||||
return generator.rectangle(0, 0, element.width, element.height, {
|
return generator.rectangle(0, 0, element.width, element.height, {
|
||||||
stroke: element.strokeColor,
|
stroke: element.strokeColor,
|
||||||
fill: element.backgroundColor,
|
fill: element.backgroundColor
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
element.draw = (rc, context, { scrollX, scrollY }) => {
|
element.draw = (rc, context, { scrollX, scrollY }) => {
|
||||||
|
@ -674,7 +674,7 @@ function generateDraw(element: ExcalidrawElement) {
|
||||||
rc.draw(shape);
|
rc.draw(shape);
|
||||||
context.translate(-element.x - scrollX, -element.y - scrollY);
|
context.translate(-element.x - scrollX, -element.y - scrollY);
|
||||||
};
|
};
|
||||||
} else if (element.type === 'ellipse') {
|
} else if (element.type === "ellipse") {
|
||||||
const shape = withCustomMathRandom(element.seed, () =>
|
const shape = withCustomMathRandom(element.seed, () =>
|
||||||
generator.ellipse(
|
generator.ellipse(
|
||||||
element.width / 2,
|
element.width / 2,
|
||||||
|
@ -689,7 +689,7 @@ function generateDraw(element: ExcalidrawElement) {
|
||||||
rc.draw(shape);
|
rc.draw(shape);
|
||||||
context.translate(-element.x - scrollX, -element.y - scrollY);
|
context.translate(-element.x - scrollX, -element.y - scrollY);
|
||||||
};
|
};
|
||||||
} else if (element.type === 'arrow') {
|
} else if (element.type === "arrow") {
|
||||||
const [x1, y1, x2, y2, x3, y3, x4, y4] = getArrowPoints(element);
|
const [x1, y1, x2, y2, x3, y3, x4, y4] = getArrowPoints(element);
|
||||||
const shapes = withCustomMathRandom(element.seed, () => [
|
const shapes = withCustomMathRandom(element.seed, () => [
|
||||||
// \
|
// \
|
||||||
|
@ -697,7 +697,7 @@ function generateDraw(element: ExcalidrawElement) {
|
||||||
// -----
|
// -----
|
||||||
generator.line(x1, y1, x2, y2, { stroke: element.strokeColor }),
|
generator.line(x1, y1, x2, y2, { stroke: element.strokeColor }),
|
||||||
// /
|
// /
|
||||||
generator.line(x4, y4, x2, y2, { stroke: element.strokeColor }),
|
generator.line(x4, y4, x2, y2, { stroke: element.strokeColor })
|
||||||
]);
|
]);
|
||||||
|
|
||||||
element.draw = (rc, context, { scrollX, scrollY }) => {
|
element.draw = (rc, context, { scrollX, scrollY }) => {
|
||||||
|
@ -721,7 +721,7 @@ function generateDraw(element: ExcalidrawElement) {
|
||||||
context.font = font;
|
context.font = font;
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Unimplemented type ' + element.type);
|
throw new Error("Unimplemented type " + element.type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -753,7 +753,7 @@ function setSelection(selection: ExcalidrawElement) {
|
||||||
const elementY1 = getElementAbsoluteY1(element);
|
const elementY1 = getElementAbsoluteY1(element);
|
||||||
const elementY2 = getElementAbsoluteY2(element);
|
const elementY2 = getElementAbsoluteY2(element);
|
||||||
element.isSelected =
|
element.isSelected =
|
||||||
element.type !== 'selection' &&
|
element.type !== "selection" &&
|
||||||
selectionX1 <= elementX1 &&
|
selectionX1 <= elementX1 &&
|
||||||
selectionY1 <= elementY1 &&
|
selectionY1 <= elementY1 &&
|
||||||
selectionX2 >= elementX2 &&
|
selectionX2 >= elementX2 &&
|
||||||
|
@ -768,7 +768,7 @@ function clearSelection() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function resetCursor() {
|
function resetCursor() {
|
||||||
document.documentElement.style.cursor = '';
|
document.documentElement.style.cursor = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteSelectedElements() {
|
function deleteSelectedElements() {
|
||||||
|
@ -800,7 +800,7 @@ function restore(
|
||||||
elements.splice(
|
elements.splice(
|
||||||
0,
|
0,
|
||||||
elements.length,
|
elements.length,
|
||||||
...(typeof savedElements === 'string'
|
...(typeof savedElements === "string"
|
||||||
? JSON.parse(savedElements)
|
? JSON.parse(savedElements)
|
||||||
: savedElements)
|
: savedElements)
|
||||||
);
|
);
|
||||||
|
@ -817,7 +817,7 @@ function restore(
|
||||||
type AppState = {
|
type AppState = {
|
||||||
draggingElement: ExcalidrawElement | null;
|
draggingElement: ExcalidrawElement | null;
|
||||||
resizingElement: ExcalidrawElement | null;
|
resizingElement: ExcalidrawElement | null;
|
||||||
currentColorPicker: 'Background' | null;
|
currentColorPicker: "Background" | null;
|
||||||
elementType: string;
|
elementType: string;
|
||||||
exportBackground: boolean;
|
exportBackground: boolean;
|
||||||
currentItemStrokeColor: string;
|
currentItemStrokeColor: string;
|
||||||
|
@ -828,13 +828,13 @@ type AppState = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const KEYS = {
|
const KEYS = {
|
||||||
ARROW_LEFT: 'ArrowLeft',
|
ARROW_LEFT: "ArrowLeft",
|
||||||
ARROW_RIGHT: 'ArrowRight',
|
ARROW_RIGHT: "ArrowRight",
|
||||||
ARROW_DOWN: 'ArrowDown',
|
ARROW_DOWN: "ArrowDown",
|
||||||
ARROW_UP: 'ArrowUp',
|
ARROW_UP: "ArrowUp",
|
||||||
ESCAPE: 'Escape',
|
ESCAPE: "Escape",
|
||||||
DELETE: 'Delete',
|
DELETE: "Delete",
|
||||||
BACKSPACE: 'Backspace',
|
BACKSPACE: "Backspace"
|
||||||
};
|
};
|
||||||
|
|
||||||
// We inline font-awesome icons in order to save on js size rather than including the font awesome react library
|
// We inline font-awesome icons in order to save on js size rather than including the font awesome react library
|
||||||
|
@ -846,7 +846,7 @@ const SHAPES = [
|
||||||
<path d="M302.189 329.126H196.105l55.831 135.993c3.889 9.428-.555 19.999-9.444 23.999l-49.165 21.427c-9.165 4-19.443-.571-23.332-9.714l-53.053-129.136-86.664 89.138C18.729 472.71 0 463.554 0 447.977V18.299C0 1.899 19.921-6.096 30.277 5.443l284.412 292.542c11.472 11.179 3.007 31.141-12.5 31.141z" />
|
<path d="M302.189 329.126H196.105l55.831 135.993c3.889 9.428-.555 19.999-9.444 23.999l-49.165 21.427c-9.165 4-19.443-.571-23.332-9.714l-53.053-129.136-86.664 89.138C18.729 472.71 0 463.554 0 447.977V18.299C0 1.899 19.921-6.096 30.277 5.443l284.412 292.542c11.472 11.179 3.007 31.141-12.5 31.141z" />
|
||||||
</svg>
|
</svg>
|
||||||
),
|
),
|
||||||
value: 'selection',
|
value: "selection"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: (
|
icon: (
|
||||||
|
@ -855,7 +855,7 @@ const SHAPES = [
|
||||||
<path d="M400 32H48C21.5 32 0 53.5 0 80v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V80c0-26.5-21.5-48-48-48z" />
|
<path d="M400 32H48C21.5 32 0 53.5 0 80v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V80c0-26.5-21.5-48-48-48z" />
|
||||||
</svg>
|
</svg>
|
||||||
),
|
),
|
||||||
value: 'rectangle',
|
value: "rectangle"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: (
|
icon: (
|
||||||
|
@ -864,7 +864,7 @@ const SHAPES = [
|
||||||
<path d="M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8z" />
|
<path d="M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8z" />
|
||||||
</svg>
|
</svg>
|
||||||
),
|
),
|
||||||
value: 'ellipse',
|
value: "ellipse"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: (
|
icon: (
|
||||||
|
@ -873,7 +873,7 @@ const SHAPES = [
|
||||||
<path d="M313.941 216H12c-6.627 0-12 5.373-12 12v56c0 6.627 5.373 12 12 12h301.941v46.059c0 21.382 25.851 32.09 40.971 16.971l86.059-86.059c9.373-9.373 9.373-24.569 0-33.941l-86.059-86.059c-15.119-15.119-40.971-4.411-40.971 16.971V216z" />
|
<path d="M313.941 216H12c-6.627 0-12 5.373-12 12v56c0 6.627 5.373 12 12 12h301.941v46.059c0 21.382 25.851 32.09 40.971 16.971l86.059-86.059c9.373-9.373 9.373-24.569 0-33.941l-86.059-86.059c-15.119-15.119-40.971-4.411-40.971 16.971V216z" />
|
||||||
</svg>
|
</svg>
|
||||||
),
|
),
|
||||||
value: 'arrow',
|
value: "arrow"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: (
|
icon: (
|
||||||
|
@ -882,8 +882,8 @@ const SHAPES = [
|
||||||
<path d="M432 416h-23.41L277.88 53.69A32 32 0 0 0 247.58 32h-47.16a32 32 0 0 0-30.3 21.69L39.41 416H16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h128a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16h-19.58l23.3-64h152.56l23.3 64H304a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h128a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM176.85 272L224 142.51 271.15 272z" />
|
<path d="M432 416h-23.41L277.88 53.69A32 32 0 0 0 247.58 32h-47.16a32 32 0 0 0-30.3 21.69L39.41 416H16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h128a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16h-19.58l23.3-64h152.56l23.3 64H304a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h128a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM176.85 272L224 142.51 271.15 272z" />
|
||||||
</svg>
|
</svg>
|
||||||
),
|
),
|
||||||
value: 'text',
|
value: "text"
|
||||||
},
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
const shapesShortcutKeys = SHAPES.map(shape => shape.value[0]);
|
const shapesShortcutKeys = SHAPES.map(shape => shape.value[0]);
|
||||||
|
@ -893,7 +893,7 @@ function capitalize(str: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function findElementByKey(key: string) {
|
function findElementByKey(key: string) {
|
||||||
const defaultElement = 'selection';
|
const defaultElement = "selection";
|
||||||
return SHAPES.reduce((element, shape) => {
|
return SHAPES.reduce((element, shape) => {
|
||||||
if (shape.value[0] !== key) return element;
|
if (shape.value[0] !== key) return element;
|
||||||
|
|
||||||
|
@ -933,8 +933,8 @@ let lastMouseUp: ((e: any) => void) | null = null;
|
||||||
|
|
||||||
class App extends React.Component<{}, AppState> {
|
class App extends React.Component<{}, AppState> {
|
||||||
public componentDidMount() {
|
public componentDidMount() {
|
||||||
document.addEventListener('keydown', this.onKeyDown, false);
|
document.addEventListener("keydown", this.onKeyDown, false);
|
||||||
window.addEventListener('resize', this.onResize, false);
|
window.addEventListener("resize", this.onResize, false);
|
||||||
|
|
||||||
const savedState = restoreFromLocalStorage();
|
const savedState = restoreFromLocalStorage();
|
||||||
if (savedState) {
|
if (savedState) {
|
||||||
|
@ -943,21 +943,21 @@ class App extends React.Component<{}, AppState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentWillUnmount() {
|
public componentWillUnmount() {
|
||||||
document.removeEventListener('keydown', this.onKeyDown, false);
|
document.removeEventListener("keydown", this.onKeyDown, false);
|
||||||
window.removeEventListener('resize', this.onResize, false);
|
window.removeEventListener("resize", this.onResize, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public state: AppState = {
|
public state: AppState = {
|
||||||
draggingElement: null,
|
draggingElement: null,
|
||||||
resizingElement: null,
|
resizingElement: null,
|
||||||
elementType: 'selection',
|
elementType: "selection",
|
||||||
currentColorPicker: null,
|
currentColorPicker: null,
|
||||||
exportBackground: true,
|
exportBackground: true,
|
||||||
currentItemStrokeColor: '#000000',
|
currentItemStrokeColor: "#000000",
|
||||||
currentItemBackgroundColor: '#ffffff',
|
currentItemBackgroundColor: "#ffffff",
|
||||||
viewBackgroundColor: '#ffffff',
|
viewBackgroundColor: "#ffffff",
|
||||||
scrollX: 0,
|
scrollX: 0,
|
||||||
scrollY: 0,
|
scrollY: 0
|
||||||
};
|
};
|
||||||
|
|
||||||
private onResize = () => {
|
private onResize = () => {
|
||||||
|
@ -995,13 +995,13 @@ class App extends React.Component<{}, AppState> {
|
||||||
event.metaKey &&
|
event.metaKey &&
|
||||||
event.shiftKey &&
|
event.shiftKey &&
|
||||||
event.altKey &&
|
event.altKey &&
|
||||||
event.code === 'KeyB'
|
event.code === "KeyB"
|
||||||
) {
|
) {
|
||||||
this.moveOneLeft();
|
this.moveOneLeft();
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
// Send to back: Cmd-Shift-B
|
// Send to back: Cmd-Shift-B
|
||||||
} else if (event.metaKey && event.shiftKey && event.code === 'KeyB') {
|
} else if (event.metaKey && event.shiftKey && event.code === "KeyB") {
|
||||||
this.moveAllLeft();
|
this.moveAllLeft();
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
|
@ -1010,18 +1010,18 @@ class App extends React.Component<{}, AppState> {
|
||||||
event.metaKey &&
|
event.metaKey &&
|
||||||
event.shiftKey &&
|
event.shiftKey &&
|
||||||
event.altKey &&
|
event.altKey &&
|
||||||
event.code === 'KeyF'
|
event.code === "KeyF"
|
||||||
) {
|
) {
|
||||||
this.moveOneRight();
|
this.moveOneRight();
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
// Bring to front: Cmd-Shift-F
|
// Bring to front: Cmd-Shift-F
|
||||||
} else if (event.metaKey && event.shiftKey && event.code === 'KeyF') {
|
} else if (event.metaKey && event.shiftKey && event.code === "KeyF") {
|
||||||
this.moveAllRight();
|
this.moveAllRight();
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
// Select all: Cmd-A
|
// Select all: Cmd-A
|
||||||
} else if (event.metaKey && event.code === 'KeyA') {
|
} else if (event.metaKey && event.code === "KeyA") {
|
||||||
elements.forEach(element => {
|
elements.forEach(element => {
|
||||||
element.isSelected = true;
|
element.isSelected = true;
|
||||||
});
|
});
|
||||||
|
@ -1029,7 +1029,7 @@ class App extends React.Component<{}, AppState> {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
} else if (shapesShortcutKeys.includes(event.key.toLowerCase())) {
|
} else if (shapesShortcutKeys.includes(event.key.toLowerCase())) {
|
||||||
this.setState({ elementType: findElementByKey(event.key) });
|
this.setState({ elementType: findElementByKey(event.key) });
|
||||||
} else if (event.metaKey && event.code === 'KeyZ') {
|
} else if (event.metaKey && event.code === "KeyZ") {
|
||||||
let lastEntry = stateHistory.pop();
|
let lastEntry = stateHistory.pop();
|
||||||
// If nothing was changed since last, take the previous one
|
// If nothing was changed since last, take the previous one
|
||||||
if (generateHistoryCurrentEntry() === lastEntry) {
|
if (generateHistoryCurrentEntry() === lastEntry) {
|
||||||
|
@ -1049,12 +1049,12 @@ class App extends React.Component<{}, AppState> {
|
||||||
};
|
};
|
||||||
|
|
||||||
private clearCanvas = () => {
|
private clearCanvas = () => {
|
||||||
if (window.confirm('This will clear the whole canvas. Are you sure?')) {
|
if (window.confirm("This will clear the whole canvas. Are you sure?")) {
|
||||||
elements.splice(0, elements.length);
|
elements.splice(0, elements.length);
|
||||||
this.setState({
|
this.setState({
|
||||||
viewBackgroundColor: '#ffffff',
|
viewBackgroundColor: "#ffffff",
|
||||||
scrollX: 0,
|
scrollX: 0,
|
||||||
scrollY: 0,
|
scrollY: 0
|
||||||
});
|
});
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
}
|
}
|
||||||
|
@ -1091,7 +1091,7 @@ class App extends React.Component<{}, AppState> {
|
||||||
className="container"
|
className="container"
|
||||||
onCut={e => {
|
onCut={e => {
|
||||||
e.clipboardData.setData(
|
e.clipboardData.setData(
|
||||||
'text/plain',
|
"text/plain",
|
||||||
JSON.stringify(elements.filter(element => element.isSelected))
|
JSON.stringify(elements.filter(element => element.isSelected))
|
||||||
);
|
);
|
||||||
deleteSelectedElements();
|
deleteSelectedElements();
|
||||||
|
@ -1100,13 +1100,13 @@ class App extends React.Component<{}, AppState> {
|
||||||
}}
|
}}
|
||||||
onCopy={e => {
|
onCopy={e => {
|
||||||
e.clipboardData.setData(
|
e.clipboardData.setData(
|
||||||
'text/plain',
|
"text/plain",
|
||||||
JSON.stringify(elements.filter(element => element.isSelected))
|
JSON.stringify(elements.filter(element => element.isSelected))
|
||||||
);
|
);
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}}
|
}}
|
||||||
onPaste={e => {
|
onPaste={e => {
|
||||||
const paste = e.clipboardData.getData('text');
|
const paste = e.clipboardData.getData("text");
|
||||||
let parsedElements;
|
let parsedElements;
|
||||||
try {
|
try {
|
||||||
parsedElements = JSON.parse(paste);
|
parsedElements = JSON.parse(paste);
|
||||||
|
@ -1145,7 +1145,7 @@ class App extends React.Component<{}, AppState> {
|
||||||
this.setState({ elementType: value });
|
this.setState({ elementType: value });
|
||||||
clearSelection();
|
clearSelection();
|
||||||
document.documentElement.style.cursor =
|
document.documentElement.style.cursor =
|
||||||
value === 'text' ? 'text' : 'crosshair';
|
value === "text" ? "text" : "crosshair";
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
@ -1161,18 +1161,18 @@ class App extends React.Component<{}, AppState> {
|
||||||
<button
|
<button
|
||||||
className="swatch"
|
className="swatch"
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: this.state.viewBackgroundColor,
|
backgroundColor: this.state.viewBackgroundColor
|
||||||
}}
|
}}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
this.setState(s => ({
|
this.setState(s => ({
|
||||||
currentColorPicker:
|
currentColorPicker:
|
||||||
s.currentColorPicker === 'Background'
|
s.currentColorPicker === "Background"
|
||||||
? null
|
? null
|
||||||
: 'Background',
|
: "Background"
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
></button>
|
></button>
|
||||||
{this.state.currentColorPicker === 'Background' ? (
|
{this.state.currentColorPicker === "Background" ? (
|
||||||
<div className="popover">
|
<div className="popover">
|
||||||
<div
|
<div
|
||||||
className="cover"
|
className="cover"
|
||||||
|
@ -1279,7 +1279,7 @@ class App extends React.Component<{}, AppState> {
|
||||||
id="canvas"
|
id="canvas"
|
||||||
style={{
|
style={{
|
||||||
width: canvasWidth,
|
width: canvasWidth,
|
||||||
height: canvasHeight,
|
height: canvasHeight
|
||||||
}}
|
}}
|
||||||
width={canvasWidth * window.devicePixelRatio}
|
width={canvasWidth * window.devicePixelRatio}
|
||||||
height={canvasHeight * window.devicePixelRatio}
|
height={canvasHeight * window.devicePixelRatio}
|
||||||
|
@ -1289,11 +1289,11 @@ class App extends React.Component<{}, AppState> {
|
||||||
this.removeWheelEventListener = undefined;
|
this.removeWheelEventListener = undefined;
|
||||||
}
|
}
|
||||||
if (canvas) {
|
if (canvas) {
|
||||||
canvas.addEventListener('wheel', this.handleWheel, {
|
canvas.addEventListener("wheel", this.handleWheel, {
|
||||||
passive: false,
|
passive: false
|
||||||
});
|
});
|
||||||
this.removeWheelEventListener = () =>
|
this.removeWheelEventListener = () =>
|
||||||
canvas.removeEventListener('wheel', this.handleWheel);
|
canvas.removeEventListener("wheel", this.handleWheel);
|
||||||
|
|
||||||
// Whenever React sets the width/height of the canvas element,
|
// Whenever React sets the width/height of the canvas element,
|
||||||
// the context loses the scale transform. We need to re-apply it
|
// the context loses the scale transform. We need to re-apply it
|
||||||
|
@ -1304,7 +1304,7 @@ class App extends React.Component<{}, AppState> {
|
||||||
lastCanvasWidth = canvasWidth;
|
lastCanvasWidth = canvasWidth;
|
||||||
lastCanvasHeight = canvasHeight;
|
lastCanvasHeight = canvasHeight;
|
||||||
canvas
|
canvas
|
||||||
.getContext('2d')!
|
.getContext("2d")!
|
||||||
.scale(window.devicePixelRatio, window.devicePixelRatio);
|
.scale(window.devicePixelRatio, window.devicePixelRatio);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1330,7 +1330,7 @@ class App extends React.Component<{}, AppState> {
|
||||||
// Handle scrollbars dragging
|
// Handle scrollbars dragging
|
||||||
const {
|
const {
|
||||||
isOverHorizontalScrollBar,
|
isOverHorizontalScrollBar,
|
||||||
isOverVerticalScrollBar,
|
isOverVerticalScrollBar
|
||||||
} = isOverScrollBars(
|
} = isOverScrollBars(
|
||||||
e.clientX - CANVAS_WINDOW_OFFSET_LEFT,
|
e.clientX - CANVAS_WINDOW_OFFSET_LEFT,
|
||||||
e.clientY - CANVAS_WINDOW_OFFSET_TOP,
|
e.clientY - CANVAS_WINDOW_OFFSET_TOP,
|
||||||
|
@ -1353,24 +1353,24 @@ class App extends React.Component<{}, AppState> {
|
||||||
let resizeHandle: string | false = false;
|
let resizeHandle: string | false = false;
|
||||||
let isDraggingElements = false;
|
let isDraggingElements = false;
|
||||||
let isResizingElements = false;
|
let isResizingElements = false;
|
||||||
if (this.state.elementType === 'selection') {
|
if (this.state.elementType === "selection") {
|
||||||
const resizeElement = elements.find(element => {
|
const resizeElement = elements.find(element => {
|
||||||
return resizeTest(element, x, y, {
|
return resizeTest(element, x, y, {
|
||||||
scrollX: this.state.scrollX,
|
scrollX: this.state.scrollX,
|
||||||
scrollY: this.state.scrollY,
|
scrollY: this.state.scrollY,
|
||||||
viewBackgroundColor: this.state.viewBackgroundColor,
|
viewBackgroundColor: this.state.viewBackgroundColor
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
resizingElement: resizeElement ? resizeElement : null,
|
resizingElement: resizeElement ? resizeElement : null
|
||||||
});
|
});
|
||||||
|
|
||||||
if (resizeElement) {
|
if (resizeElement) {
|
||||||
resizeHandle = resizeTest(resizeElement, x, y, {
|
resizeHandle = resizeTest(resizeElement, x, y, {
|
||||||
scrollX: this.state.scrollX,
|
scrollX: this.state.scrollX,
|
||||||
scrollY: this.state.scrollY,
|
scrollY: this.state.scrollY,
|
||||||
viewBackgroundColor: this.state.viewBackgroundColor,
|
viewBackgroundColor: this.state.viewBackgroundColor
|
||||||
});
|
});
|
||||||
document.documentElement.style.cursor = `${resizeHandle}-resize`;
|
document.documentElement.style.cursor = `${resizeHandle}-resize`;
|
||||||
isResizingElements = true;
|
isResizingElements = true;
|
||||||
|
@ -1405,14 +1405,14 @@ class App extends React.Component<{}, AppState> {
|
||||||
isDraggingElements = someElementIsSelected();
|
isDraggingElements = someElementIsSelected();
|
||||||
|
|
||||||
if (isDraggingElements) {
|
if (isDraggingElements) {
|
||||||
document.documentElement.style.cursor = 'move';
|
document.documentElement.style.cursor = "move";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isTextElement(element)) {
|
if (isTextElement(element)) {
|
||||||
resetCursor();
|
resetCursor();
|
||||||
const text = prompt('What text do you want?');
|
const text = prompt("What text do you want?");
|
||||||
if (text === null) {
|
if (text === null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1439,10 +1439,10 @@ class App extends React.Component<{}, AppState> {
|
||||||
|
|
||||||
generateDraw(element);
|
generateDraw(element);
|
||||||
elements.push(element);
|
elements.push(element);
|
||||||
if (this.state.elementType === 'text') {
|
if (this.state.elementType === "text") {
|
||||||
this.setState({
|
this.setState({
|
||||||
draggingElement: null,
|
draggingElement: null,
|
||||||
elementType: 'selection',
|
elementType: "selection"
|
||||||
});
|
});
|
||||||
element.isSelected = true;
|
element.isSelected = true;
|
||||||
} else {
|
} else {
|
||||||
|
@ -1489,23 +1489,23 @@ class App extends React.Component<{}, AppState> {
|
||||||
e.clientY - CANVAS_WINDOW_OFFSET_TOP - this.state.scrollY;
|
e.clientY - CANVAS_WINDOW_OFFSET_TOP - this.state.scrollY;
|
||||||
selectedElements.forEach(element => {
|
selectedElements.forEach(element => {
|
||||||
switch (resizeHandle) {
|
switch (resizeHandle) {
|
||||||
case 'nw':
|
case "nw":
|
||||||
element.width += element.x - lastX;
|
element.width += element.x - lastX;
|
||||||
element.height += element.y - lastY;
|
element.height += element.y - lastY;
|
||||||
element.x = lastX;
|
element.x = lastX;
|
||||||
element.y = lastY;
|
element.y = lastY;
|
||||||
break;
|
break;
|
||||||
case 'ne':
|
case "ne":
|
||||||
element.width = lastX - element.x;
|
element.width = lastX - element.x;
|
||||||
element.height += element.y - lastY;
|
element.height += element.y - lastY;
|
||||||
element.y = lastY;
|
element.y = lastY;
|
||||||
break;
|
break;
|
||||||
case 'sw':
|
case "sw":
|
||||||
element.width += element.x - lastX;
|
element.width += element.x - lastX;
|
||||||
element.x = lastX;
|
element.x = lastX;
|
||||||
element.height = lastY - element.y;
|
element.height = lastY - element.y;
|
||||||
break;
|
break;
|
||||||
case 'se':
|
case "se":
|
||||||
element.width += x - lastX;
|
element.width += x - lastX;
|
||||||
if (e.shiftKey) {
|
if (e.shiftKey) {
|
||||||
element.height = element.width;
|
element.height = element.width;
|
||||||
|
@ -1513,18 +1513,18 @@ class App extends React.Component<{}, AppState> {
|
||||||
element.height += y - lastY;
|
element.height += y - lastY;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'n':
|
case "n":
|
||||||
element.height += element.y - lastY;
|
element.height += element.y - lastY;
|
||||||
element.y = lastY;
|
element.y = lastY;
|
||||||
break;
|
break;
|
||||||
case 'w':
|
case "w":
|
||||||
element.width += element.x - lastX;
|
element.width += element.x - lastX;
|
||||||
element.x = lastX;
|
element.x = lastX;
|
||||||
break;
|
break;
|
||||||
case 's':
|
case "s":
|
||||||
element.height = lastY - element.y;
|
element.height = lastY - element.y;
|
||||||
break;
|
break;
|
||||||
case 'e':
|
case "e":
|
||||||
element.width = lastX - element.x;
|
element.width = lastX - element.x;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1582,7 +1582,7 @@ class App extends React.Component<{}, AppState> {
|
||||||
|
|
||||||
generateDraw(draggingElement);
|
generateDraw(draggingElement);
|
||||||
|
|
||||||
if (this.state.elementType === 'selection') {
|
if (this.state.elementType === "selection") {
|
||||||
setSelection(draggingElement);
|
setSelection(draggingElement);
|
||||||
}
|
}
|
||||||
// We don't want to save history when moving an element
|
// We don't want to save history when moving an element
|
||||||
|
@ -1594,8 +1594,8 @@ class App extends React.Component<{}, AppState> {
|
||||||
const { draggingElement, elementType } = this.state;
|
const { draggingElement, elementType } = this.state;
|
||||||
|
|
||||||
lastMouseUp = null;
|
lastMouseUp = null;
|
||||||
window.removeEventListener('mousemove', onMouseMove);
|
window.removeEventListener("mousemove", onMouseMove);
|
||||||
window.removeEventListener('mouseup', onMouseUp);
|
window.removeEventListener("mouseup", onMouseUp);
|
||||||
|
|
||||||
resetCursor();
|
resetCursor();
|
||||||
|
|
||||||
|
@ -1606,7 +1606,7 @@ class App extends React.Component<{}, AppState> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (elementType === 'selection') {
|
if (elementType === "selection") {
|
||||||
if (isDraggingElements) {
|
if (isDraggingElements) {
|
||||||
isDraggingElements = false;
|
isDraggingElements = false;
|
||||||
}
|
}
|
||||||
|
@ -1617,15 +1617,15 @@ class App extends React.Component<{}, AppState> {
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
draggingElement: null,
|
draggingElement: null,
|
||||||
elementType: 'selection',
|
elementType: "selection"
|
||||||
});
|
});
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
};
|
};
|
||||||
|
|
||||||
lastMouseUp = onMouseUp;
|
lastMouseUp = onMouseUp;
|
||||||
|
|
||||||
window.addEventListener('mousemove', onMouseMove);
|
window.addEventListener("mousemove", onMouseMove);
|
||||||
window.addEventListener('mouseup', onMouseUp);
|
window.addEventListener("mouseup", onMouseUp);
|
||||||
|
|
||||||
// We don't want to save history on mouseDown, only on mouseUp when it's fully configured
|
// We don't want to save history on mouseDown, only on mouseUp when it's fully configured
|
||||||
skipHistory = true;
|
skipHistory = true;
|
||||||
|
@ -1641,7 +1641,7 @@ class App extends React.Component<{}, AppState> {
|
||||||
const { deltaX, deltaY } = e;
|
const { deltaX, deltaY } = e;
|
||||||
this.setState(state => ({
|
this.setState(state => ({
|
||||||
scrollX: state.scrollX - deltaX,
|
scrollX: state.scrollX - deltaX,
|
||||||
scrollY: state.scrollY - deltaY,
|
scrollY: state.scrollY - deltaY
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1649,7 +1649,7 @@ class App extends React.Component<{}, AppState> {
|
||||||
renderScene(rc, canvas, {
|
renderScene(rc, canvas, {
|
||||||
scrollX: this.state.scrollX,
|
scrollX: this.state.scrollX,
|
||||||
scrollY: this.state.scrollY,
|
scrollY: this.state.scrollY,
|
||||||
viewBackgroundColor: this.state.viewBackgroundColor,
|
viewBackgroundColor: this.state.viewBackgroundColor
|
||||||
});
|
});
|
||||||
save(this.state);
|
save(this.state);
|
||||||
if (!skipHistory) {
|
if (!skipHistory) {
|
||||||
|
@ -1659,10 +1659,10 @@ class App extends React.Component<{}, AppState> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const rootElement = document.getElementById('root');
|
const rootElement = document.getElementById("root");
|
||||||
ReactDOM.render(<App />, rootElement);
|
ReactDOM.render(<App />, rootElement);
|
||||||
const canvas = document.getElementById('canvas') as HTMLCanvasElement;
|
const canvas = document.getElementById("canvas") as HTMLCanvasElement;
|
||||||
const rc = rough.canvas(canvas);
|
const rc = rough.canvas(canvas);
|
||||||
const context = canvas.getContext('2d')!;
|
const context = canvas.getContext("2d")!;
|
||||||
|
|
||||||
ReactDOM.render(<App />, rootElement);
|
ReactDOM.render(<App />, rootElement);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue