Explorar el Código

Change styles (#179)

* Fill style

* Roughness, opacity and more styles

* Remove duplicated options

* Support diamonds

* Remove unused import

* Fix typo and remove react import
Paulo Menezes hace 5 años
padre
commit
f360c3cb33
Se han modificado 2 ficheros con 405 adiciones y 87 borrados
  1. 400 86
      src/index.tsx
  2. 5 1
      src/styles.scss

+ 400 - 86
src/index.tsx

@@ -254,6 +254,10 @@ function newElement(
   y: number,
   strokeColor: string,
   backgroundColor: string,
+  fillStyle: string,
+  strokeWidth: number,
+  roughness: number,
+  opacity: number,
   width = 0,
   height = 0
 ) {
@@ -266,6 +270,10 @@ function newElement(
     isSelected: false,
     strokeColor: strokeColor,
     backgroundColor: backgroundColor,
+    fillStyle: fillStyle,
+    strokeWidth: strokeWidth,
+    roughness: roughness,
+    opacity: opacity,
     seed: randomSeed(),
     draw(
       rc: RoughCanvas,
@@ -761,13 +769,18 @@ function generateDraw(element: ExcalidrawElement) {
     const shape = withCustomMathRandom(element.seed, () => {
       return generator.rectangle(0, 0, element.width, element.height, {
         stroke: element.strokeColor,
-        fill: element.backgroundColor
+        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 === "diamond") {
     const shape = withCustomMathRandom(element.seed, () => {
@@ -790,14 +803,19 @@ function generateDraw(element: ExcalidrawElement) {
         ],
         {
           stroke: element.strokeColor,
-          fill: element.backgroundColor
+          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 === "ellipse") {
     const shape = withCustomMathRandom(element.seed, () =>
@@ -806,33 +824,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;
@@ -844,6 +885,7 @@ function generateDraw(element: ExcalidrawElement) {
       );
       context.fillStyle = fillStyle;
       context.font = font;
+      context.globalAlpha = 1;
     };
   } else {
     throw new Error("Unimplemented type " + element.type);
@@ -1064,6 +1106,91 @@ function getSelectedIndices() {
 const someElementIsSelected = () =>
   elements.some(element => element.isSelected);
 
+const someElementIsSelectedIsRectangleOrEllipseOrDiamond = () =>
+  elements.some(
+    element =>
+      element.isSelected &&
+      (element.type === "rectangle" ||
+        element.type === "ellipse" ||
+        element.type === "diamond")
+  );
+
+const someElementIsSelectedIsRectangleOrEllipseOrDiamondOrArrow = () =>
+  elements.some(
+    element =>
+      element.isSelected &&
+      (element.type === "rectangle" ||
+        element.type === "ellipse" ||
+        element.type === "diamond" ||
+        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;
 
@@ -1228,6 +1355,43 @@ class App extends React.Component<{}, AppState> {
     this.setState({ name });
   }
 
+  private changeProperty = (callback: (element: ExcalidrawElement) => void) => {
+    elements.forEach(element => {
+      if (element.isSelected) {
+        callback(element);
+        generateDraw(element);
+      }
+    });
+
+    this.forceUpdate();
+  };
+
+  private changeFillStyle = (style: string) => {
+    this.changeProperty(element => (element.fillStyle = style));
+  };
+
+  private changeStrokeWidth = (event: React.ChangeEvent<HTMLSelectElement>) => {
+    this.changeProperty(element => (element.strokeWidth = +event.target.value));
+  };
+
+  private changeRoughness = (event: React.ChangeEvent<HTMLSelectElement>) => {
+    this.changeProperty(element => (element.roughness = +event.target.value));
+  };
+
+  private changeOpacity = (event: React.ChangeEvent<HTMLInputElement>) => {
+    this.changeProperty(element => (element.opacity = +event.target.value));
+  };
+
+  private changeStrokeColor = (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;
@@ -1353,85 +1517,6 @@ class App extends React.Component<{}, AppState> {
                 }
               />
             </div>
-            <h5>Shape Stroke Color</h5>
-            <div>
-              <button
-                className="swatch"
-                style={{
-                  backgroundColor: this.state.currentItemStrokeColor
-                }}
-                onClick={() =>
-                  this.setState(s => ({
-                    currentColorPicker:
-                      s.currentColorPicker === ColorPicker.SHAPE_STROKE
-                        ? null
-                        : ColorPicker.SHAPE_STROKE
-                  }))
-                }
-              />
-              {this.state.currentColorPicker === ColorPicker.SHAPE_STROKE ? (
-                <div className="popover">
-                  <div
-                    className="cover"
-                    onClick={() => this.setState({ currentColorPicker: null })}
-                  />
-                  <SketchPicker
-                    color={this.state.currentItemStrokeColor}
-                    onChange={color => {
-                      this.setState({ currentItemStrokeColor: color.hex });
-                    }}
-                  />
-                </div>
-              ) : null}
-              <input
-                type="text"
-                className="swatch-input"
-                value={this.state.currentItemStrokeColor}
-                onChange={e => {
-                  this.setState({ currentItemStrokeColor: e.target.value });
-                }}
-              />
-            </div>
-            <h5>Shape Background Color</h5>
-            <div>
-              <button
-                className="swatch"
-                style={{
-                  backgroundColor: this.state.currentItemBackgroundColor
-                }}
-                onClick={() =>
-                  this.setState(s => ({
-                    currentColorPicker:
-                      s.currentColorPicker === ColorPicker.SHAPE_BACKGROUND
-                        ? null
-                        : ColorPicker.SHAPE_BACKGROUND
-                  }))
-                }
-              />
-              {this.state.currentColorPicker ===
-              ColorPicker.SHAPE_BACKGROUND ? (
-                <div className="popover">
-                  <div
-                    className="cover"
-                    onClick={() => this.setState({ currentColorPicker: null })}
-                  />
-                  <SketchPicker
-                    color={this.state.currentItemBackgroundColor}
-                    onChange={color => {
-                      this.setState({ currentItemBackgroundColor: color.hex });
-                    }}
-                  />
-                </div>
-              ) : null}
-              <input
-                type="text"
-                className="swatch-input"
-                value={this.state.currentItemStrokeColor}
-                onChange={e => {
-                  this.setState({ currentItemStrokeColor: e.target.value });
-                }}
-              />
-            </div>
             <button
               onClick={this.clearCanvas}
               title="Clear the canvas & reset background color"
@@ -1482,6 +1567,231 @@ class App extends React.Component<{}, AppState> {
               Load file...
             </button>
           </div>
+          {someElementIsSelected() && (
+            <>
+              <>
+                <h4>Colors</h4>
+                <div className="panelColumn">
+                  <h5>Shape Stroke Color</h5>
+                  <div>
+                    <button
+                      className="swatch"
+                      style={{
+                        backgroundColor:
+                          getSelectedStrokeColor() ||
+                          this.state.currentItemStrokeColor
+                      }}
+                      onClick={() =>
+                        this.setState(s => ({
+                          currentColorPicker:
+                            s.currentColorPicker === ColorPicker.SHAPE_STROKE
+                              ? null
+                              : ColorPicker.SHAPE_STROKE
+                        }))
+                      }
+                    />
+                    {this.state.currentColorPicker ===
+                      ColorPicker.SHAPE_STROKE && (
+                      <div className="popover">
+                        <div
+                          className="cover"
+                          onClick={() =>
+                            this.setState({ currentColorPicker: null })
+                          }
+                        />
+                        <SketchPicker
+                          color={this.state.currentItemStrokeColor}
+                          onChange={color => this.changeStrokeColor(color.hex)}
+                        />
+                      </div>
+                    )}
+                    <input
+                      type="text"
+                      className="swatch-input"
+                      value={
+                        getSelectedStrokeColor() ||
+                        this.state.currentItemStrokeColor
+                      }
+                      onChange={e => this.changeStrokeColor(e.target.value)}
+                    />
+                  </div>
+                </div>
+
+                {someElementIsSelectedIsRectangleOrEllipseOrDiamond() && (
+                  <div className="panelColumn">
+                    <h5>Shape Background Color</h5>
+                    <div>
+                      <button
+                        className="swatch"
+                        style={{
+                          backgroundColor:
+                            getSelectedBackgroundColor() ||
+                            this.state.currentItemBackgroundColor
+                        }}
+                        onClick={() =>
+                          this.setState(s => ({
+                            currentColorPicker:
+                              s.currentColorPicker ===
+                              ColorPicker.SHAPE_BACKGROUND
+                                ? null
+                                : ColorPicker.SHAPE_BACKGROUND
+                          }))
+                        }
+                      />
+                      {this.state.currentColorPicker ===
+                      ColorPicker.SHAPE_BACKGROUND ? (
+                        <div className="popover">
+                          <div
+                            className="cover"
+                            onClick={() =>
+                              this.setState({ currentColorPicker: null })
+                            }
+                          />
+                          <SketchPicker
+                            color={this.state.currentItemBackgroundColor}
+                            onChange={color =>
+                              this.changeBackgroundColor(color.hex)
+                            }
+                          />
+                        </div>
+                      ) : null}
+                      <input
+                        type="text"
+                        className="swatch-input"
+                        value={
+                          getSelectedBackgroundColor() ||
+                          this.state.currentItemBackgroundColor
+                        }
+                        onChange={e =>
+                          this.changeBackgroundColor(e.target.value)
+                        }
+                      />
+                    </div>
+                  </div>
+                )}
+              </>
+
+              {someElementIsSelectedIsRectangleOrEllipseOrDiamond() && (
+                <>
+                  <h4>Fill</h4>
+                  <div className="panelColumn">
+                    {/* <select onChange={this.changeFillStyle} value={getSelectedFillStyles()}> */}
+                    <button
+                      onClick={() => this.changeFillStyle("hachure")}
+                      className={
+                        getSelectedFillStyles() === "hachure" ? "active" : ""
+                      }
+                    >
+                      Hachure
+                    </button>
+                    <button
+                      onClick={() => this.changeFillStyle("solid")}
+                      className={
+                        getSelectedFillStyles() === "solid" ? "active" : ""
+                      }
+                    >
+                      Solid
+                    </button>
+                    <button
+                      onClick={() => this.changeFillStyle("zigzag")}
+                      className={
+                        getSelectedFillStyles() === "zigzag" ? "active" : ""
+                      }
+                    >
+                      Zigzag
+                    </button>
+                    <button
+                      onClick={() => this.changeFillStyle("cross-hatch")}
+                      className={
+                        getSelectedFillStyles() === "cross-hatch"
+                          ? "active"
+                          : ""
+                      }
+                    >
+                      Cross-hatch
+                    </button>
+                    <button
+                      onClick={() => this.changeFillStyle("dots")}
+                      className={
+                        getSelectedFillStyles() === "dots" ? "active" : ""
+                      }
+                    >
+                      Dots
+                    </button>
+                    <button
+                      onClick={() => this.changeFillStyle("sunburst")}
+                      className={
+                        getSelectedFillStyles() === "sunburst" ? "active" : ""
+                      }
+                    >
+                      Sunburst
+                    </button>
+                    <button
+                      onClick={() => this.changeFillStyle("dashed")}
+                      className={
+                        getSelectedFillStyles() === "dashed" ? "active" : ""
+                      }
+                    >
+                      Dashed
+                    </button>
+                    <button
+                      onClick={() => this.changeFillStyle("zigzag-line")}
+                      className={
+                        getSelectedFillStyles() === "zigzag-line"
+                          ? "active"
+                          : ""
+                      }
+                    >
+                      Zigzag-line
+                    </button>
+                    {/* </select> */}
+                  </div>
+                </>
+              )}
+
+              {someElementIsSelectedIsRectangleOrEllipseOrDiamondOrArrow() && (
+                <>
+                  <h4>Stroke width</h4>
+                  <div className="panelColumn">
+                    <select
+                      onChange={this.changeStrokeWidth}
+                      value={getSelectedStrokeWidth()}
+                    >
+                      <option hidden disabled value=""></option>
+                      <option value="1">1</option>
+                      <option value="2">2</option>
+                      <option value="4">4</option>
+                      <option value="8">8</option>
+                    </select>
+                  </div>
+
+                  <h4>Roughness</h4>
+                  <div className="panelColumn">
+                    <select
+                      onChange={this.changeRoughness}
+                      value={getSelectedRoughness()}
+                    >
+                      <option hidden disabled value=""></option>
+                      <option value="1">1</option>
+                      <option value="2">2</option>
+                      <option value="4">4</option>
+                      <option value="8">8</option>
+                      <option value="10">10</option>
+                    </select>
+                  </div>
+                </>
+              )}
+
+              <h4>Opacity</h4>
+              <input
+                type="range"
+                min="0"
+                max="100"
+                onChange={this.changeOpacity}
+                value={getSelectedOpacity()}
+              />
+            </>
+          )}
         </div>
         <canvas
           id="canvas"
@@ -1556,7 +1866,11 @@ class App extends React.Component<{}, AppState> {
               x,
               y,
               this.state.currentItemStrokeColor,
-              this.state.currentItemBackgroundColor
+              this.state.currentItemBackgroundColor,
+              "hachure",
+              1,
+              1,
+              100
             );
             let resizeHandle: string | false = false;
             let isDraggingElements = false;

+ 5 - 1
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;
   }