Merge remote-tracking branch 'upstream/master

This commit is contained in:
Paulo Menezes 2020-01-02 23:32:49 -03:00
commit 0b043b942b
2 changed files with 165 additions and 47 deletions

View file

@ -123,11 +123,13 @@ function newElement(type: string, x: number, y: number, width = 0, height = 0) {
function exportAsPNG({ function exportAsPNG({
exportBackground, exportBackground,
exportVisibleOnly, exportVisibleOnly,
exportPadding = 10 exportPadding = 10,
viewBgColor
}: { }: {
exportBackground: boolean; exportBackground: boolean;
exportVisibleOnly: boolean; exportVisibleOnly: boolean;
exportPadding?: number; exportPadding?: number;
viewBgColor: string;
}) { }) {
if (!elements.length) return window.alert("Cannot export empty canvas."); if (!elements.length) return window.alert("Cannot export empty canvas.");
@ -164,7 +166,7 @@ function exportAsPNG({
: canvas.height; : canvas.height;
if (exportBackground) { if (exportBackground) {
tempCanvasCtx.fillStyle = "#FFF"; tempCanvasCtx.fillStyle = viewBgColor;
tempCanvasCtx.fillRect(0, 0, canvas.width, canvas.height); tempCanvasCtx.fillRect(0, 0, canvas.width, canvas.height);
} }
@ -240,7 +242,11 @@ function getArrowPoints(element: ExcaliburElement) {
return [x1, y1, x2, y2, x3, y3, x4, y4]; return [x1, y1, x2, y2, x3, y3, x4, y4];
} }
function generateDraw(element: ExcaliburElement) { function generateDraw(
element: ExcaliburElement,
itemStrokeColor: string,
itemBackgroundColorColor: string
) {
if (element.type === "selection") { if (element.type === "selection") {
element.draw = (rc, context) => { element.draw = (rc, context) => {
const fillStyle = context.fillStyle; const fillStyle = context.fillStyle;
@ -249,7 +255,10 @@ function generateDraw(element: ExcaliburElement) {
context.fillStyle = fillStyle; context.fillStyle = fillStyle;
}; };
} else if (element.type === "rectangle") { } else if (element.type === "rectangle") {
const shape = generator.rectangle(0, 0, element.width, element.height); const shape = generator.rectangle(0, 0, element.width, element.height, {
stroke: itemStrokeColor,
fill: itemBackgroundColorColor
});
element.draw = (rc, context) => { element.draw = (rc, context) => {
context.translate(element.x, element.y); context.translate(element.x, element.y);
rc.draw(shape); rc.draw(shape);
@ -260,7 +269,8 @@ function generateDraw(element: ExcaliburElement) {
element.width / 2, element.width / 2,
element.height / 2, element.height / 2,
element.width, element.width,
element.height element.height,
{ stroke: itemStrokeColor, fill: itemBackgroundColorColor }
); );
element.draw = (rc, context) => { element.draw = (rc, context) => {
context.translate(element.x, element.y); context.translate(element.x, element.y);
@ -271,11 +281,11 @@ function generateDraw(element: ExcaliburElement) {
const [x1, y1, x2, y2, x3, y3, x4, y4] = getArrowPoints(element); const [x1, y1, x2, y2, x3, y3, x4, y4] = getArrowPoints(element);
const shapes = [ const shapes = [
// \ // \
generator.line(x3, y3, x2, y2), generator.line(x3, y3, x2, y2, { stroke: itemStrokeColor }),
// ----- // -----
generator.line(x1, y1, x2, y2), generator.line(x1, y1, x2, y2, { stroke: itemStrokeColor }),
// / // /
generator.line(x4, y4, x2, y2) generator.line(x4, y4, x2, y2, { stroke: itemStrokeColor })
]; ];
element.draw = (rc, context) => { element.draw = (rc, context) => {
@ -369,7 +379,7 @@ function restore() {
if (el) { if (el) {
const items = JSON.parse(el); const items = JSON.parse(el);
for (let item of items) { for (let item of items) {
item = generateDraw(item); item = generateDraw(item, "#000000", "#ffffff");
} }
elements = [...items]; elements = [...items];
} }
@ -393,6 +403,9 @@ type AppState = {
exportBackground: boolean; exportBackground: boolean;
exportVisibleOnly: boolean; exportVisibleOnly: boolean;
exportPadding: number; exportPadding: number;
itemStrokeColor: string;
itemBackgroundColor: string;
viewBgColor: string;
}; };
class App extends React.Component<{}, AppState> { class App extends React.Component<{}, AppState> {
@ -410,7 +423,10 @@ class App extends React.Component<{}, AppState> {
elementType: "selection", elementType: "selection",
exportBackground: false, exportBackground: false,
exportVisibleOnly: true, exportVisibleOnly: true,
exportPadding: 10 exportPadding: 10,
itemStrokeColor: "#000000",
itemBackgroundColor: "#ffffff",
viewBgColor: "#ffffff"
}; };
private onKeyDown = (event: KeyboardEvent) => { private onKeyDown = (event: KeyboardEvent) => {
@ -493,7 +509,8 @@ class App extends React.Component<{}, AppState> {
exportAsPNG({ exportAsPNG({
exportBackground: this.state.exportBackground, exportBackground: this.state.exportBackground,
exportVisibleOnly: this.state.exportVisibleOnly, exportVisibleOnly: this.state.exportVisibleOnly,
exportPadding: this.state.exportPadding exportPadding: this.state.exportPadding,
viewBgColor: this.state.viewBgColor
}); });
}} }}
> >
@ -531,6 +548,14 @@ class App extends React.Component<{}, AppState> {
px) px)
</div> </div>
</div> </div>
<fieldset>
<legend>Shapes</legend>
{this.renderOption({ type: "rectangle", children: "Rectangle" })}
{this.renderOption({ type: "ellipse", children: "Ellipse" })}
{this.renderOption({ type: "arrow", children: "Arrow" })}
{this.renderOption({ type: "text", children: "Text" })}
{this.renderOption({ type: "selection", children: "Selection" })}
</fieldset>
<div <div
onCut={e => { onCut={e => {
e.clipboardData.setData( e.clipboardData.setData(
@ -563,7 +588,11 @@ class App extends React.Component<{}, AppState> {
parsedElements.forEach(parsedElement => { parsedElements.forEach(parsedElement => {
parsedElement.x += 10; parsedElement.x += 10;
parsedElement.y += 10; parsedElement.y += 10;
generateDraw(parsedElement); generateDraw(
parsedElement,
this.state.itemStrokeColor,
this.state.itemBackgroundColor
);
elements.push(parsedElement); elements.push(parsedElement);
addOnBeforeUnload(); addOnBeforeUnload();
}); });
@ -572,15 +601,10 @@ class App extends React.Component<{}, AppState> {
e.preventDefault(); e.preventDefault();
}} }}
> >
{this.renderOption({ type: "rectangle", children: "Rectangle" })}
{this.renderOption({ type: "ellipse", children: "Ellipse" })}
{this.renderOption({ type: "arrow", children: "Arrow" })}
{this.renderOption({ type: "text", children: "Text" })}
{this.renderOption({ type: "selection", children: "Selection" })}
<canvas <canvas
id="canvas" id="canvas"
width={window.innerWidth} width={window.innerWidth}
height={window.innerHeight} height={window.innerHeight - 200}
onMouseDown={e => { onMouseDown={e => {
const x = e.clientX - (e.target as HTMLElement).offsetLeft; const x = e.clientX - (e.target as HTMLElement).offsetLeft;
const y = e.clientY - (e.target as HTMLElement).offsetTop; const y = e.clientY - (e.target as HTMLElement).offsetTop;
@ -646,7 +670,11 @@ class App extends React.Component<{}, AppState> {
element.height = height; element.height = height;
} }
generateDraw(element); generateDraw(
element,
this.state.itemStrokeColor,
this.state.itemBackgroundColor
);
elements.push(element); elements.push(element);
if (this.state.elementType === "text") { if (this.state.elementType === "text") {
this.setState({ this.setState({
@ -694,7 +722,11 @@ class App extends React.Component<{}, AppState> {
// Make a perfect square or circle when shift is enabled // Make a perfect square or circle when shift is enabled
draggingElement.height = e.shiftKey ? width : height; draggingElement.height = e.shiftKey ? width : height;
generateDraw(draggingElement); generateDraw(
draggingElement,
this.state.itemStrokeColor,
this.state.itemBackgroundColor
);
if (this.state.elementType === "selection") { if (this.state.elementType === "selection") {
setSelection(draggingElement); setSelection(draggingElement);
@ -740,9 +772,114 @@ class App extends React.Component<{}, AppState> {
}} }}
/> />
</div> </div>
<fieldset>
<legend>Colors</legend>
<label>
<input
type="color"
value={this.state.viewBgColor}
onChange={e => {
this.setState({ viewBgColor: e.target.value });
}}
/>
Background
</label>
<label>
<input
type="color"
value={this.state.itemStrokeColor}
onChange={e => {
this.setState({ itemStrokeColor: e.target.value });
}}
/>
Shape Stroke
</label>
<label>
<input
type="color"
value={this.state.itemBackgroundColor}
onChange={e => {
this.setState({ itemBackgroundColor: e.target.value });
}}
/>
Shape Background
</label>
</fieldset>
<fieldset>
<legend>Export</legend>
<button
onClick={() => {
exportAsPNG({
exportBackground: this.state.exportBackground,
exportVisibleOnly: this.state.exportVisibleOnly,
exportPadding: this.state.exportPadding,
viewBgColor: this.state.viewBgColor
});
}}
>
Export to png
</button>
<label>
<input
type="checkbox"
checked={this.state.exportBackground}
onChange={e => {
this.setState({ exportBackground: e.target.checked });
}}
/>
background
</label>
<label>
<input
type="checkbox"
checked={this.state.exportVisibleOnly}
onChange={e => {
this.setState({ exportVisibleOnly: e.target.checked });
}}
/>
visible area only
</label>
(padding:
<input
type="number"
value={this.state.exportPadding}
onChange={e => {
this.setState({ exportPadding: Number(e.target.value) });
}}
disabled={!this.state.exportVisibleOnly}
/>
px)
</fieldset>
</> </>
); );
} }
componentDidUpdate() {
const fillStyle = context.fillStyle;
context.fillStyle = this.state.viewBgColor;
context.fillRect(-0.5, -0.5, canvas.width, canvas.height);
context.fillStyle = fillStyle;
elements.forEach(element => {
element.draw(rc, context);
if (element.isSelected) {
const margin = 4;
const elementX1 = getElementAbsoluteX1(element);
const elementX2 = getElementAbsoluteX2(element);
const elementY1 = getElementAbsoluteY1(element);
const elementY2 = getElementAbsoluteY2(element);
const lineDash = context.getLineDash();
context.setLineDash([8, 4]);
context.strokeRect(
elementX1 - margin,
elementY1 - margin,
elementX2 - elementX1 + margin * 2,
elementY2 - elementY1 + margin * 2
);
context.setLineDash(lineDash);
}
});
}
} }
const rootElement = document.getElementById("root"); const rootElement = document.getElementById("root");
@ -759,29 +896,6 @@ restore();
function drawScene() { function drawScene() {
ReactDOM.render(<App />, rootElement); ReactDOM.render(<App />, rootElement);
context.clearRect(-0.5, -0.5, canvas.width, canvas.height);
elements.forEach(element => {
element.draw(rc, context);
if (element.isSelected) {
const margin = 4;
const elementX1 = getElementAbsoluteX1(element);
const elementX2 = getElementAbsoluteX2(element);
const elementY1 = getElementAbsoluteY1(element);
const elementY2 = getElementAbsoluteY2(element);
const lineDash = context.getLineDash();
context.setLineDash([8, 4]);
context.strokeRect(
elementX1 - margin,
elementY1 - margin,
elementX2 - elementX1 + margin * 2,
elementY2 - elementY1 + margin * 2
);
context.setLineDash(lineDash);
}
});
} }
drawScene(); drawScene();

View file

@ -24,8 +24,12 @@
margin-right: 10px; margin-right: 10px;
} }
.exportWrapper input[type="number"] { label {
width: 40px; margin-right: 10px;
padding: 2px; }
margin-left: 10px; input[type="number"] {
width: 30px;
}
input {
margin-right: 5px;
} }