Add button when scrolled outside of visible area (#643)

With the infinite scroll behavior, it's easy to scroll super far away from where the content is and have a hard time getting back. This PR adds a button to refocus on the center of the scene when no elements are visible anymore.
This commit is contained in:
Christopher Chedeau 2020-02-01 16:52:10 +00:00 committed by GitHub
parent 7c9e6dd3f1
commit be97bd980e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 44 additions and 8 deletions

View File

@ -24,6 +24,7 @@ export function getDefaultAppState(): AppState {
scrollY: 0,
cursorX: 0,
cursorY: 0,
scrolledOutside: false,
name: DEFAULT_PROJECT_NAME,
};
}

View File

@ -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<any, AppState> {
currentLanguage={getLanguage()}
/>
{this.renderIdsDropdown()}
{this.state.scrolledOutside && (
<button
className="scroll-back-to-content"
onClick={() => {
this.setState({ ...calculateScrollCenter(elements) });
}}
>
{t("buttons.scrollBackToContent")}
</button>
)}
</footer>
</div>
);
@ -1872,11 +1883,20 @@ export class App extends React.Component<any, AppState> {
}, 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(

View File

@ -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?",

View File

@ -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(

View File

@ -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);

View File

@ -17,6 +17,7 @@ export {
importFromBackend,
addToLoadedScenes,
loadedScenes,
calculateScrollCenter,
} from "./data";
export {
hasBackground,

View File

@ -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;
}

View File

@ -22,6 +22,7 @@ export type AppState = {
scrollY: number;
cursorX: number;
cursorY: number;
scrolledOutside: boolean;
name: string;
selectedId?: string;
};