excalidraw
excalidraw copied to clipboard
Feature Enhancement: Define Canvas Size
Please allow for defined canvas sizes. It was requested to open a new issue here: https://github.com/excalidraw/excalidraw/issues/38
The infinite scroll is not always ideal.
Would be even better if user/admin could set/load a canvas background and interact with background with custom functions (e.g. custom SVG interactions in an SVG object layer that the canvas sits on top of in the same dimensions, and both the background layer and canvas zoom and pan seamlessly together).
Another example would be: User uploads a photo background, the canvas becomes the size of the photo, and the user can pan and zoom and mark on the photo with excalidraw. The user could use custom photo functions/tools to manipulate the photo background (rotate, crop, change color hue, etc).
I have a rough implementation of this feature. It's tailored for my specific use-case, which is: a fixed-size canvas that fills parent container, while maintaining proper aspect ratio, without panning or zooming available.
The UI/UX part is sub-optimal for the excalidraw-app, which should probably still support zooming and panning of the fixed-size canvas.
That being said, the following general-purpose functionality is also implemented:
- Proper scene initialization based on canvas size mode (fixed or infinite)
- Proper exporting to SVG / png based on canvas size mode (fixed or infinite). In the fixed size mode it'll export the full canvas of desired size instead of part of the canvas that covers all elements.
Things that are missing:
- UI for setting canvas size. For now, it's done by the Excalidraw component prop.
How it's done
defaultCanvasSize?: {width: number, height: number} prop was added to Excalidraw component, which enables "fixed size" mode.
I added canvasSize property to AppState:
export type CanvasSize =
| { mode: "fixed"; width: number; height: number }
| { mode: "infinite" }
| { mode: "default" };
The "default" mode is used to tell that it's not being specified yet, so we'll set it to either "fixed" or "infinite" based on whether defaultCanvasSize property was passed to Excalidraw component when we initialize the scene.
When loading an existing document without canvasSize property in the AppState, it's automatically set to "infinite".
Awesome @josephbuchma -
Are you saying you disabled panning and zooming? Or it's not available with this solution.
Panning and zooming would still be important to my use case; as would having a background image that pans and zooms with it..
Yes, if the scene has a fixed canvas size, panning and zooming are not available. This solution can be extended to support panning and zooming, although will require a bit more work. I didn't bother since I don't need it. But maybe I'm missing something, it's still a work in progress.
@mmmd-am I found some shortcomings in my original approach and updated how fixed-size canvas is rendered on the screen - it's more generic, and now it's possible to have pan & zoom if you like:
export type CanvasSize =
| {
mode: "fixed";
width: number;
height: number;
autoZoom?: boolean; // pan & zoom is locked if this property is set to true
}
| { mode: "infinite" }
| { mode: "default" };
The UI for setting canvas size is still not there, and UX with fixed canvas and unlocked pan & zoom still need to be polished:
- It's possible to draw outside of the fixed canvas, although when you export an image, it exports only the "fixed size part" of the canvas.
- It's possible to move an empty canvas out of the screen and it won't show the "scroll back to content" button.
- And other small things like canvas positioning when app loads etc...
It mostly works for me as it is right now, so it's unlikely that I'll work on those things in the near future, but it should be fairly easy to pick up for you and get it to the stage where it can be merged.
UPD: I'll probably fix the drawing outside of the fixed canvas thing UPD2: Fixed drawing elements outside of the canvas.
Here is a quick demo of how it works at the moment:
https://user-images.githubusercontent.com/3718145/191666539-5116f454-52d0-421f-b0d4-2f947a58629f.mov
@josephbuchma Awesome work. If you are available to discuss privately, please let me know.
Hi, great job @josephbuchma. It's actually a feature I was looking for. Any news on if or when It'll be added to Excalidraw ?
Hey folks! I apologize for not replying @mmmd-am
@alanlima-fr I don't know if this feature is in Excalidraw's roadmap.
I'm working on https://shwif.com which is based on Excalidraw. Initially, I was working on my fork of Excalidraw, but later I moved it to a private repo, which I plan to open source in the future.
Here is an example story created in Shwif - open on a smartphone for the best experience.
Another example (annotations are in Ukrainian, and it's dark there because we're bombed by russia and there is no electricity).
What I'm building differs from Excalidraw quite a lot, so keeping it backward-compatible was slowing me down substantially.
We do want to support this in some way. Perhaps the upcoming frames feature could work for this. https://github.com/excalidraw/excalidraw/issues/6044
Thanks for the reply and the solution you offer @josephbuchma Even though shwif.com looks promising, Excalidraw respond to all the problematic that we have at the office.
@dwelle That's great news, thanks ! Let me know if I can help or contribute in some way, and I'll watch the progress on issue #6044
@josephbuchma awesome! i'm waiting for it every day
PR #6731 will achieve similar result, limiting the canvas size