Skip to content

grid support (1st iteration)#1788

Merged
dwelle merged 24 commits intoexcalidraw:masterfrom
dai-shi:feat/grid-support
Jun 23, 2020
Merged

grid support (1st iteration)#1788
dwelle merged 24 commits intoexcalidraw:masterfrom
dai-shi:feat/grid-support

Conversation

@dai-shi
Copy link
Member

@dai-shi dai-shi commented Jun 20, 2020

Fixes #521

Thanks to the refactored resize functions, it was pretty easy to add grids for resizing a single element. (Drawing grids was harder.)

exdraw61

TODOs:

  • enable/disable shortcut (disabled by default)
  • resize a single element
  • resize multiple elements
  • drag a single elements
  • drag multiple elements
  • create new elements (rectangles, diamonds, ellipses)
  • create new elements (two-point lines)

For follow-up PRs: see #1810

  • context menu to toggle and customize grid size
  • edit multi-point lines
  • create new elements (multi-point lines)
  • create new elements (texts)

@vercel
Copy link

vercel bot commented Jun 20, 2020

This pull request is being automatically deployed with Vercel (learn more).
To see the status of your deployment, click below or on the icon next to each commit.

🔍 Inspect: https://vercel.com/excalidraw/excalidraw/6op6v08x7
✅ Preview: https://excalidraw-git-fork-dai-shi-feat-grid-support.excalidraw.vercel.app

@dwelle
Copy link
Member

dwelle commented Jun 20, 2020

Cool. I thought it's gonna be much harder than this!

  • we'll want to expose it under a shortcut (Cmd/Ctrl+' is the standard), not as a default
  • moving elements should move on the grid, too

@dai-shi
Copy link
Member Author

dai-shi commented Jun 20, 2020

And, creating new elements too.

@vercel vercel bot temporarily deployed to Preview June 20, 2020 23:29 Inactive
@dai-shi
Copy link
Member Author

dai-shi commented Jun 20, 2020

exdraw62

} else {
dragOffsetXY = getDragOffsetXY([hitElement], x, y);
}
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not 100% sure if this covers all the cases, especially with grouping.

src/math.ts Outdated
return false;
};

export const pointOnGrids = (
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can suggest a better name?

@dai-shi
Copy link
Member Author

dai-shi commented Jun 21, 2020

I would like to have a context menu to enable/disable grid and configure the grid size (10, 20, 40), but not sure how to add context sub menus.

@dai-shi dai-shi changed the title [Not Yet For Merge] grid support grid support (1st iteration) Jun 21, 2020
context.stroke();
}
context.strokeStyle = origStrokeStyle;
};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can increase the grid render performance by doing a single stroke. Fewer drawing operation the better, every time. (on large screen goes from 0.25ms to 0.05ms) May not be noticeable but every ns counts when drawing :)

context.beginPath();
for (let x = offsetX; x < offsetX + width + gridSize; x += gridSize) {
  context.moveTo(x, offsetY - gridSize);
  context.lineTo(x, offsetY + height + gridSize);
}
for (let y = offsetY; y < offsetY + height + gridSize; y += gridSize) {
  context.moveTo(offsetX - gridSize, y);
  context.lineTo(offsetX + width + gridSize, y);
}
context.stroke();

Btw, since grid never changes per se, we should consider some way of not having to render it every time.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @pshihn ! Fixed in f4da892.

Btw, since grid never changes per se, we should consider some way of not having to render it every time.

That's not only for grid, but all shapes.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I can revisit #780 which I closed because the multiplayer didn't play well with that heuristic. Maybe it can be worked around, dunno.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another crazy idea is to use webgl, which I've experimented in excalidraw-layers. I'm not suggesting it for the current excalidraw, but it might be interesting in general to explore utilizing webgl for 2D.

F_KEY_CODE: 70,
ALT_KEY_CODE: 18,
Z_KEY_CODE: 90,
GRID_KEY_CODE: 222,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure about this. 39 didn't work on macos.

@dwelle
Copy link
Member

dwelle commented Jun 21, 2020

I would like to have a context menu to enable/disable grid and configure the grid size (10, 20, 40), but not sure how to add context sub menus.

We'll want submenus sooner or later, anyway. But maybe stick with single grid size for this PR and add the granular support later.

@dwelle
Copy link
Member

dwelle commented Jun 21, 2020

I love this!

@dai-shi
Copy link
Member Author

dai-shi commented Jun 21, 2020

Would you help with the snapshot tests? Don't know why it fails other than gridSize: null.

@dwelle
Copy link
Member

dwelle commented Jun 21, 2020

There's one failing case that seems like a regression (but I couldn't figure out what exactly that regression is, playing with the preview):

 ● duplicate element on move when ALT is clicked › rectangle

    expect(received).toEqual(expected) // deep equality

    - Expected
    + Received

      Array [
    -   30,
    -   20,
    +   0,
    +   40,
      ]

      89 |
      90 |     // previous element should stay intact
    > 91 |     expect([h.elements[0].x, h.elements[0].y]).toEqual([30, 20]);
         |                                                ^
      92 |     expect([h.elements[1].x, h.elements[1].y]).toEqual([-10, 60]);
      93 |
      94 |     h.elements.forEach((element) => expect(element).toMatchSnapshot());

      at Object.<anonymous> (src/tests/move.test.tsx:91:48)

And some of the snapshots are failing on similar significant changes to x/y as well (not just gridSize: null).

@dwelle
Copy link
Member

dwelle commented Jun 21, 2020

Ok, I'm not sure if this is the exact regression, but here's one: when you drag an element and press Alt mid-way, the non-duplicated element should revert to orig position.

@dai-shi
Copy link
Member Author

dai-shi commented Jun 22, 2020

collaboration mode

I don't know which is better. Notice the background color is not in sync.

Maybe someone on the other end wants to draw on grid, but others don't?

On second thought, I can imagine this would happen. So, my suggestion is to keep it separate for now.

const [gridX, gridY] = getGridPoint(
x,
y,
this.state.elementType === "draw" ? null : this.state.gridSize,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is very good in readability. Feel free to refactor...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's fine. Will be removed when we add support for all types later, anyway.

@dwelle
Copy link
Member

dwelle commented Jun 23, 2020

Looks great!

One last thing, I'd store gridSize when exporting to file (cleanAppStateForExport()) so it's restored on open.

@dwelle
Copy link
Member

dwelle commented Jun 23, 2020

Ad the shortcut. Can you try if Cmd+' works for on you on Figma? I think they must be using event.key because there, on French keyboard (AZERTY) that shortcut doesn't work (the ' is one key to the right of QUERTY physical position, but Figma doesn't accept that physical key on my QUERTY ISO keyboard either, so it must be using event.key that key is actually backtick.).

I'd really love to get my hands on Mac (keyboard) so I can do proper debugging.

@dai-shi
Copy link
Member Author

dai-shi commented Jun 23, 2020

(Never used figma...)

Should we go with this?

diff --git a/src/components/App.tsx b/src/components/App.tsx
index dba59fd..27e18b4 100644
--- a/src/components/App.tsx
+++ b/src/components/App.tsx
@@ -1184,7 +1184,7 @@ class App extends React.Component<any, AppState> {
       this.toggleZenMode();
     }
 
-    if (event[KEYS.CTRL_OR_CMD] && event.keyCode === KEYS.GRID_KEY_CODE) {
+    if (event[KEYS.CTRL_OR_CMD] && event.key === KEYS.GRID_KEY) {
       this.toggleGridMode();
     }
 
diff --git a/src/keys.ts b/src/keys.ts
index 10e82bc..d31d0d5 100644
--- a/src/keys.ts
+++ b/src/keys.ts
@@ -16,7 +16,7 @@ export const KEYS = {
   F_KEY_CODE: 70,
   ALT_KEY_CODE: 18,
   Z_KEY_CODE: 90,
-  GRID_KEY_CODE: 222,
+  GRID_KEY: "'",
   G_KEY_CODE: 71,
 } as const;
 

@dwelle
Copy link
Member

dwelle commented Jun 23, 2020

I dunno TBH. Per discussion on chat, if we go with event.key for this one, it'll take layout into account (which is generally good), but won't work for AZERTY layout (and maybe others) at all :/, whereas what you used previously will work on all layouts, but e.g. on AZERTY the labeling won't be useful for users at all (the physical key will be ù on AZERTY).

What others do:

  • Figma likely uses event.key, so on AZERTY doesn't work at all.
  • https://www.photopea.com/ (PS clone) uses event.keyCode so works everywhere, but labeling is off
  • Affinity (Desktop PS Clone) --- not clear what they do. On AZERTY, it doesn't work at all (even Ctrl+4 which is Ctrl+' for AZERTY).

So probably leave it as you had it previously?

@dai-shi
Copy link
Member Author

dai-shi commented Jun 23, 2020

OK, as there doesn't seem to be a perfect solution, let's leave it.

@dwelle
Copy link
Member

dwelle commented Jun 23, 2020

Actually, keyCode doesn't work on AZERTY at all either, I must have tested it wrong. It seems the ù uses a different keyCode altogether. Anyway, let's leave it for now.

@dwelle
Copy link
Member

dwelle commented Jun 23, 2020

That said, some users wouldn't be able to use this feature until we added it to UI somewhere. I'm wondering if we should add it to contextmenu right now, even if we later decide to move it elsewhere.

@dai-shi
Copy link
Member Author

dai-shi commented Jun 23, 2020

For me as a user, I'd be happy to have context menu anyway (not only about grid), even if we had another config UI.

@dwelle
Copy link
Member

dwelle commented Jun 23, 2020

Yea, let's do it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Snap to grid

6 participants