diff --git a/src/index.tsx b/src/index.tsx index 1ef71fb64..b77a2be87 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -2,7 +2,7 @@ import React, { ChangeEvent } from "react"; import ReactDOM from "react-dom"; import rough from "roughjs/bin/wrappers/rough"; import { RoughCanvas } from "roughjs/bin/canvas"; -import { SketchPicker } from "react-color"; +import { SketchPicker, ColorResult } from "react-color"; import { moveOneLeft, moveAllLeft, moveOneRight, moveAllRight } from "./zindex"; import { roundRect } from "./roundRect"; @@ -255,6 +255,9 @@ function newElement( strokeColor: string, backgroundColor: string, fillStyle: string, + strokeWidth: number, + roughness: number, + opacity: number, width = 0, height = 0 ) { @@ -268,6 +271,9 @@ function newElement( strokeColor: strokeColor, backgroundColor: backgroundColor, fillStyle: fillStyle, + strokeWidth: strokeWidth, + roughness: roughness, + opacity: opacity, seed: randomSeed(), draw( rc: RoughCanvas, @@ -747,7 +753,6 @@ function getDiamondPoints(element: ExcalidrawElement) { } function generateDraw(element: ExcalidrawElement) { - console.log(element); if (element.type === "selection") { element.draw = (rc, context, { scrollX, scrollY }) => { const fillStyle = context.fillStyle; @@ -765,13 +770,17 @@ function generateDraw(element: ExcalidrawElement) { return generator.rectangle(0, 0, element.width, element.height, { stroke: element.strokeColor, fill: element.backgroundColor, - fillStyle: element.fillStyle + 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, () => { @@ -810,33 +819,56 @@ function generateDraw(element: ExcalidrawElement) { element.height / 2, element.width, element.height, - { stroke: element.strokeColor, fill: element.backgroundColor } + { + 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 shapes = withCustomMathRandom(element.seed, () => [ // \ - generator.line(x3, y3, x2, y2, { stroke: element.strokeColor }), + generator.line(x3, y3, x2, y2, { + stroke: element.strokeColor, + strokeWidth: element.strokeWidth, + roughness: element.roughness + }), // ----- - generator.line(x1, y1, x2, y2, { stroke: element.strokeColor }), + generator.line(x1, y1, x2, y2, { + stroke: element.strokeColor, + strokeWidth: element.strokeWidth, + roughness: element.roughness + }), // / - generator.line(x4, y4, x2, y2, { stroke: element.strokeColor }) + generator.line(x4, y4, x2, y2, { + stroke: element.strokeColor, + 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); 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; @@ -848,6 +880,7 @@ function generateDraw(element: ExcalidrawElement) { ); context.fillStyle = fillStyle; context.font = font; + context.globalAlpha = 1; }; } else { throw new Error("Unimplemented type " + element.type); @@ -1068,6 +1101,88 @@ function getSelectedIndices() { const someElementIsSelected = () => elements.some(element => element.isSelected); +const someElementIsSelectedIsRectangleOrEllipse = () => + elements.some( + element => + element.isSelected && + (element.type === "rectangle" || element.type === "ellipse") + ); + +const someElementIsSelectedIsRectangleOrEllipseOrArrow = () => + elements.some( + element => + element.isSelected && + (element.type === "rectangle" || + element.type === "ellipse" || + element.type === "arrow") + ); + +function getSelectedFillStyles() { + const fillStyles = Array.from( + new Set( + elements + .filter(element => element.isSelected) + .map(element => element.fillStyle) + ) + ); + return fillStyles.length === 1 ? fillStyles[0] : ""; +} + +function getSelectedStrokeWidth() { + const strokeWidth = Array.from( + new Set( + elements + .filter(element => element.isSelected) + .map(element => `${element.strokeWidth}`) + ) + ); + return strokeWidth.length === 1 ? +strokeWidth[0] : ""; +} + +function getSelectedRoughness() { + const roughness = Array.from( + new Set( + elements + .filter(element => element.isSelected) + .map(element => `${element.roughness}`) + ) + ); + return roughness.length === 1 ? +roughness[0] : ""; +} + +function getSelectedOpacity() { + const opacity = Array.from( + new Set( + elements + .filter(element => element.isSelected) + .map(element => `${element.opacity}`) + ) + ); + return opacity.length === 1 ? +opacity[0] : ""; +} + +function getSelectedStrokeColor() { + const strokeColors = Array.from( + new Set( + elements + .filter(element => element.isSelected) + .map(element => element.strokeColor) + ) + ); + return strokeColors.length === 1 ? strokeColors[0] : null; +} + +function getSelectedBackgroundColor() { + const backgroundColors = Array.from( + new Set( + elements + .filter(element => element.isSelected) + .map(element => element.backgroundColor) + ) + ); + return backgroundColors.length === 1 ? backgroundColors[0] : null; +} + const ELEMENT_SHIFT_TRANSLATE_AMOUNT = 5; const ELEMENT_TRANSLATE_AMOUNT = 1; @@ -1232,19 +1347,43 @@ class App extends React.Component<{}, AppState> { this.setState({ name }); } - private changeFillStyle = (event: ChangeEvent) => { - console.log(event.target.value); + private changeProperty = (callback: (element: ExcalidrawElement) => void) => { elements.forEach(element => { - console.log("forceUpdate"); if (element.isSelected) { - element.fillStyle = event.target.value; + callback(element); + generateDraw(element); } }); - console.log("forceUpdate"); this.forceUpdate(); }; + private changeFillStyle = (style: string) => { + this.changeProperty(element => (element.fillStyle = style)); + }; + + private changeStrokeWidth = (event: ChangeEvent) => { + this.changeProperty(element => (element.strokeWidth = +event.target.value)); + }; + + private changeRoughness = (event: ChangeEvent) => { + this.changeProperty(element => (element.roughness = +event.target.value)); + }; + + private changeOpacity = (event: ChangeEvent) => { + this.changeProperty(element => (element.opacity = +event.target.value)); + }; + + private changeSrokeColor = (color: string) => { + this.changeProperty(element => (element.strokeColor = color)); + this.setState({ currentItemStrokeColor: color }); + }; + + private changeBackgroundColor = (color: string) => { + this.changeProperty(element => (element.backgroundColor = color)); + this.setState({ currentItemBackgroundColor: color }); + }; + public render() { const canvasWidth = window.innerWidth - CANVAS_WINDOW_OFFSET_LEFT; const canvasHeight = window.innerHeight - CANVAS_WINDOW_OFFSET_TOP; @@ -1370,85 +1509,6 @@ class App extends React.Component<{}, AppState> { } /> -
Shape Stroke Color
-
-
{someElementIsSelected() && ( <> + <> +

Colors

+
+
Shape Stroke Color
+
+
+ + {someElementIsSelectedIsRectangleOrEllipse() && ( +
+
Shape Background Color
+
+
+ )} + +

Shape options

@@ -1510,19 +1653,125 @@ class App extends React.Component<{}, AppState> {
-

Fill

-
- -
+ {someElementIsSelectedIsRectangleOrEllipse() && ( + <> +

Fill

+
+ {/* */} +
+ + )} + + {someElementIsSelectedIsRectangleOrEllipseOrArrow() && ( + <> +

Stroke width

+
+ +
+ +

Roughness

+
+ +
+ + )} + +

Opacity

+ )}
@@ -1600,7 +1849,10 @@ class App extends React.Component<{}, AppState> { y, this.state.currentItemStrokeColor, this.state.currentItemBackgroundColor, - "hachure" + "hachure", + 1, + 1, + 100 ); let resizeHandle: string | false = false; let isDraggingElements = false; diff --git a/src/styles.scss b/src/styles.scss index 0a05831cf..a8f7f9972 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -110,6 +110,10 @@ input[type="color"] { margin: 2px; } +input[type="range"] { + width: 230px; +} + input { margin-right: 5px; @@ -136,7 +140,7 @@ button { border-color: #d6d4d4; } - &:active { + &:active, &.active { background-color: #bdbebc; border-color: #bdbebc; }