feat: Add zoom to fit for selected elements (#2522)
This commit is contained in:
parent
59cff0f219
commit
5abe9b93e8
@ -4,12 +4,14 @@ import { getDefaultAppState } from "../appState";
|
|||||||
import { trash, zoomIn, zoomOut, resetZoom } from "../components/icons";
|
import { trash, zoomIn, zoomOut, resetZoom } from "../components/icons";
|
||||||
import { ToolButton } from "../components/ToolButton";
|
import { ToolButton } from "../components/ToolButton";
|
||||||
import { t } from "../i18n";
|
import { t } from "../i18n";
|
||||||
import { getNormalizedZoom } from "../scene";
|
import { getNormalizedZoom, getSelectedElements } from "../scene";
|
||||||
|
import { getNonDeletedElements } from "../element";
|
||||||
import { CODES, KEYS } from "../keys";
|
import { CODES, KEYS } from "../keys";
|
||||||
import { getShortcutKey } from "../utils";
|
import { getShortcutKey } from "../utils";
|
||||||
import useIsMobile from "../is-mobile";
|
import useIsMobile from "../is-mobile";
|
||||||
import { register } from "./register";
|
import { register } from "./register";
|
||||||
import { newElementWith } from "../element/mutateElement";
|
import { newElementWith } from "../element/mutateElement";
|
||||||
|
import { ExcalidrawElement } from "../element/types";
|
||||||
import { AppState, NormalizedZoomValue } from "../types";
|
import { AppState, NormalizedZoomValue } from "../types";
|
||||||
import { getCommonBounds } from "../element";
|
import { getCommonBounds } from "../element";
|
||||||
import { getNewZoom } from "../scene/zoom";
|
import { getNewZoom } from "../scene/zoom";
|
||||||
@ -204,22 +206,30 @@ const zoomValueToFitBoundsOnViewport = (
|
|||||||
return clampedZoomValueToFitElements as NormalizedZoomValue;
|
return clampedZoomValueToFitElements as NormalizedZoomValue;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const actionZoomToFit = register({
|
const zoomToFitElements = (
|
||||||
name: "zoomToFit",
|
elements: readonly ExcalidrawElement[],
|
||||||
perform: (elements, appState) => {
|
appState: Readonly<AppState>,
|
||||||
const nonDeletedElements = elements.filter((element) => !element.isDeleted);
|
zoomToSelection: boolean,
|
||||||
const commonBounds = getCommonBounds(nonDeletedElements);
|
) => {
|
||||||
|
const nonDeletedElements = getNonDeletedElements(elements);
|
||||||
|
const selectedElements = getSelectedElements(nonDeletedElements, appState);
|
||||||
|
|
||||||
|
const commonBounds =
|
||||||
|
zoomToSelection && selectedElements.length > 0
|
||||||
|
? getCommonBounds(selectedElements)
|
||||||
|
: getCommonBounds(nonDeletedElements);
|
||||||
|
|
||||||
const zoomValue = zoomValueToFitBoundsOnViewport(commonBounds, {
|
const zoomValue = zoomValueToFitBoundsOnViewport(commonBounds, {
|
||||||
width: appState.width,
|
width: appState.width,
|
||||||
height: appState.height,
|
height: appState.height,
|
||||||
});
|
});
|
||||||
const newZoom = getNewZoom(zoomValue, appState.zoom);
|
const newZoom = getNewZoom(zoomValue, appState.zoom);
|
||||||
|
const action = zoomToSelection ? "selection" : "fit";
|
||||||
|
|
||||||
const [x1, y1, x2, y2] = commonBounds;
|
const [x1, y1, x2, y2] = commonBounds;
|
||||||
const centerX = (x1 + x2) / 2;
|
const centerX = (x1 + x2) / 2;
|
||||||
const centerY = (y1 + y2) / 2;
|
const centerY = (y1 + y2) / 2;
|
||||||
trackEvent(EVENT_ACTION, "zoom", "fit", newZoom.value * 100);
|
trackEvent(EVENT_ACTION, "zoom", action, newZoom.value * 100);
|
||||||
return {
|
return {
|
||||||
appState: {
|
appState: {
|
||||||
...appState,
|
...appState,
|
||||||
@ -235,7 +245,21 @@ export const actionZoomToFit = register({
|
|||||||
},
|
},
|
||||||
commitToHistory: false,
|
commitToHistory: false,
|
||||||
};
|
};
|
||||||
},
|
};
|
||||||
|
|
||||||
|
export const actionZoomToSelected = register({
|
||||||
|
name: "zoomToSelection",
|
||||||
|
perform: (elements, appState) => zoomToFitElements(elements, appState, true),
|
||||||
|
keyTest: (event) =>
|
||||||
|
event.code === CODES.TWO &&
|
||||||
|
event.shiftKey &&
|
||||||
|
!event.altKey &&
|
||||||
|
!event[KEYS.CTRL_OR_CMD],
|
||||||
|
});
|
||||||
|
|
||||||
|
export const actionZoomToFit = register({
|
||||||
|
name: "zoomToFit",
|
||||||
|
perform: (elements, appState) => zoomToFitElements(elements, appState, false),
|
||||||
keyTest: (event) =>
|
keyTest: (event) =>
|
||||||
event.code === CODES.ONE &&
|
event.code === CODES.ONE &&
|
||||||
event.shiftKey &&
|
event.shiftKey &&
|
||||||
|
@ -58,6 +58,7 @@ export type ActionName =
|
|||||||
| "zoomOut"
|
| "zoomOut"
|
||||||
| "resetZoom"
|
| "resetZoom"
|
||||||
| "zoomToFit"
|
| "zoomToFit"
|
||||||
|
| "zoomToSelection"
|
||||||
| "changeFontFamily"
|
| "changeFontFamily"
|
||||||
| "changeTextAlign"
|
| "changeTextAlign"
|
||||||
| "toggleFullScreen"
|
| "toggleFullScreen"
|
||||||
|
@ -206,6 +206,10 @@ export const ShortcutsDialog = ({ onClose }: { onClose?: () => void }) => {
|
|||||||
label={t("shortcutsDialog.zoomToFit")}
|
label={t("shortcutsDialog.zoomToFit")}
|
||||||
shortcuts={["Shift+1"]}
|
shortcuts={["Shift+1"]}
|
||||||
/>
|
/>
|
||||||
|
<Shortcut
|
||||||
|
label={t("shortcutsDialog.zoomToSelection")}
|
||||||
|
shortcuts={["Shift+2"]}
|
||||||
|
/>
|
||||||
<Shortcut
|
<Shortcut
|
||||||
label={t("buttons.toggleFullScreen")}
|
label={t("buttons.toggleFullScreen")}
|
||||||
shortcuts={["F"]}
|
shortcuts={["F"]}
|
||||||
|
@ -9,6 +9,7 @@ export const CODES = {
|
|||||||
BRACKET_RIGHT: "BracketRight",
|
BRACKET_RIGHT: "BracketRight",
|
||||||
BRACKET_LEFT: "BracketLeft",
|
BRACKET_LEFT: "BracketLeft",
|
||||||
ONE: "Digit1",
|
ONE: "Digit1",
|
||||||
|
TWO: "Digit2",
|
||||||
NINE: "Digit9",
|
NINE: "Digit9",
|
||||||
QUOTE: "Quote",
|
QUOTE: "Quote",
|
||||||
ZERO: "Digit0",
|
ZERO: "Digit0",
|
||||||
|
@ -213,6 +213,7 @@
|
|||||||
"textNewLine": "Add new line (text)",
|
"textNewLine": "Add new line (text)",
|
||||||
"textFinish": "Finish editing (text)",
|
"textFinish": "Finish editing (text)",
|
||||||
"zoomToFit": "Zoom to fit all elements",
|
"zoomToFit": "Zoom to fit all elements",
|
||||||
|
"zoomToSelection": "Zoom to selection",
|
||||||
"preventBinding": "Prevent arrow binding"
|
"preventBinding": "Prevent arrow binding"
|
||||||
},
|
},
|
||||||
"encrypted": {
|
"encrypted": {
|
||||||
|
@ -7,11 +7,14 @@ The change should be grouped under one of the below section and must contain PR
|
|||||||
- Chore: Changes for non src files example package.json.
|
- Chore: Changes for non src files example package.json.
|
||||||
- Improvements: For any improvements.
|
- Improvements: For any improvements.
|
||||||
- Refactor: For any refactoring.
|
- Refactor: For any refactoring.
|
||||||
|
|
||||||
|
Please add the latest change on the top under the correct section.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
- Add zoom to selection [#2522](https://github.com/excalidraw/excalidraw/pull/2522)
|
||||||
- Insert Library items in the middle of the screen [#2527](https://github.com/excalidraw/excalidraw/pull/2527)
|
- Insert Library items in the middle of the screen [#2527](https://github.com/excalidraw/excalidraw/pull/2527)
|
||||||
- Show shortcut context menu [#2501](https://github.com/excalidraw/excalidraw/pull/2501)
|
- Show shortcut context menu [#2501](https://github.com/excalidraw/excalidraw/pull/2501)
|
||||||
- Aligns arrowhead schemas [#2517](https://github.com/excalidraw/excalidraw/pull/2517)
|
- Aligns arrowhead schemas [#2517](https://github.com/excalidraw/excalidraw/pull/2517)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user