diff --git a/src/appState.ts b/src/appState.ts index e7a828e8..52576b32 100644 --- a/src/appState.ts +++ b/src/appState.ts @@ -24,6 +24,7 @@ export function getDefaultAppState(): AppState { scrollY: 0, cursorX: 0, cursorY: 0, + scrolledOutside: false, name: DEFAULT_PROJECT_NAME, }; } diff --git a/src/index.tsx b/src/index.tsx index 9d906fe5..2d46022c 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -36,6 +36,7 @@ import { importFromBackend, addToLoadedScenes, loadedScenes, + calculateScrollCenter, } from "./scene"; import { renderScene } from "./renderer"; @@ -1764,6 +1765,16 @@ export class App extends React.Component { currentLanguage={getLanguage()} /> {this.renderIdsDropdown()} + {this.state.scrolledOutside && ( + + )} ); @@ -1872,11 +1883,20 @@ export class App extends React.Component { }, 300); componentDidUpdate() { - renderScene(elements, this.rc!, this.canvas!, { - scrollX: this.state.scrollX, - scrollY: this.state.scrollY, - viewBackgroundColor: this.state.viewBackgroundColor, - }); + const atLeastOneVisibleElement = renderScene( + elements, + this.rc!, + this.canvas!, + { + scrollX: this.state.scrollX, + scrollY: this.state.scrollY, + viewBackgroundColor: this.state.viewBackgroundColor, + }, + ); + const scrolledOutside = !atLeastOneVisibleElement && elements.length > 0; + if (this.state.scrolledOutside !== scrolledOutside) { + this.setState({ scrolledOutside: scrolledOutside }); + } this.saveDebounced(); if (history.isRecording()) { history.pushEntry( diff --git a/src/locales/en.json b/src/locales/en.json index 308319dc..d832e56a 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -52,7 +52,8 @@ "getShareableLink": "Get shareable link", "close": "Close", "selectLanguage": "Select Language", - "previouslyLoadedScenes": "Previously loaded scenes" + "previouslyLoadedScenes": "Previously loaded scenes", + "scrollBackToContent": "Scroll back to content" }, "alerts": { "clearReset": "This will clear the whole canvas. Are you sure?", diff --git a/src/renderer/renderScene.ts b/src/renderer/renderScene.ts index d326817b..09639252 100644 --- a/src/renderer/renderScene.ts +++ b/src/renderer/renderScene.ts @@ -32,7 +32,7 @@ export function renderScene( renderSelection?: boolean; } = {}, ) { - if (!canvas) return; + if (!canvas) return false; const context = canvas.getContext("2d")!; const fillStyle = context.fillStyle; @@ -57,6 +57,7 @@ export function renderScene( scrollY: typeof offsetY === "number" ? offsetY : sceneState.scrollY, }; + let atLeastOneVisibleElement = false; elements.forEach(element => { if ( !isVisibleElement( @@ -71,6 +72,7 @@ export function renderScene( ) { return; } + atLeastOneVisibleElement = true; context.translate( element.x + sceneState.scrollX, element.y + sceneState.scrollY, @@ -141,6 +143,8 @@ export function renderScene( context.strokeStyle = strokeStyle; context.fillStyle = fillStyle; } + + return atLeastOneVisibleElement; } function isVisibleElement( diff --git a/src/scene/data.ts b/src/scene/data.ts index 436f0d1a..efe69cdd 100644 --- a/src/scene/data.ts +++ b/src/scene/data.ts @@ -46,7 +46,7 @@ export function serializeAsJSON( ); } -function calculateScrollCenter( +export function calculateScrollCenter( elements: readonly ExcalidrawElement[], ): { scrollX: number; scrollY: number } { let [x1, y1, x2, y2] = getCommonBounds(elements); diff --git a/src/scene/index.ts b/src/scene/index.ts index c1a7cd34..e44907eb 100644 --- a/src/scene/index.ts +++ b/src/scene/index.ts @@ -17,6 +17,7 @@ export { importFromBackend, addToLoadedScenes, loadedScenes, + calculateScrollCenter, } from "./data"; export { hasBackground, diff --git a/src/styles.scss b/src/styles.scss index c7f57ecc..b0d6b6e2 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -256,3 +256,11 @@ button, clip: rect(1px, 1px, 1px, 1px); white-space: nowrap; /* added line */ } + +.scroll-back-to-content { + position: fixed; + left: 50%; + bottom: 20px; + transform: translateX(-50%); + padding: 10px 20px; +} diff --git a/src/types.ts b/src/types.ts index f85d2ebd..69d76764 100644 --- a/src/types.ts +++ b/src/types.ts @@ -22,6 +22,7 @@ export type AppState = { scrollY: number; cursorX: number; cursorY: number; + scrolledOutside: boolean; name: string; selectedId?: string; };