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:
parent
7c9e6dd3f1
commit
be97bd980e
@ -24,6 +24,7 @@ export function getDefaultAppState(): AppState {
|
|||||||
scrollY: 0,
|
scrollY: 0,
|
||||||
cursorX: 0,
|
cursorX: 0,
|
||||||
cursorY: 0,
|
cursorY: 0,
|
||||||
|
scrolledOutside: false,
|
||||||
name: DEFAULT_PROJECT_NAME,
|
name: DEFAULT_PROJECT_NAME,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,7 @@ import {
|
|||||||
importFromBackend,
|
importFromBackend,
|
||||||
addToLoadedScenes,
|
addToLoadedScenes,
|
||||||
loadedScenes,
|
loadedScenes,
|
||||||
|
calculateScrollCenter,
|
||||||
} from "./scene";
|
} from "./scene";
|
||||||
|
|
||||||
import { renderScene } from "./renderer";
|
import { renderScene } from "./renderer";
|
||||||
@ -1764,6 +1765,16 @@ export class App extends React.Component<any, AppState> {
|
|||||||
currentLanguage={getLanguage()}
|
currentLanguage={getLanguage()}
|
||||||
/>
|
/>
|
||||||
{this.renderIdsDropdown()}
|
{this.renderIdsDropdown()}
|
||||||
|
{this.state.scrolledOutside && (
|
||||||
|
<button
|
||||||
|
className="scroll-back-to-content"
|
||||||
|
onClick={() => {
|
||||||
|
this.setState({ ...calculateScrollCenter(elements) });
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t("buttons.scrollBackToContent")}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -1872,11 +1883,20 @@ export class App extends React.Component<any, AppState> {
|
|||||||
}, 300);
|
}, 300);
|
||||||
|
|
||||||
componentDidUpdate() {
|
componentDidUpdate() {
|
||||||
renderScene(elements, this.rc!, this.canvas!, {
|
const atLeastOneVisibleElement = renderScene(
|
||||||
|
elements,
|
||||||
|
this.rc!,
|
||||||
|
this.canvas!,
|
||||||
|
{
|
||||||
scrollX: this.state.scrollX,
|
scrollX: this.state.scrollX,
|
||||||
scrollY: this.state.scrollY,
|
scrollY: this.state.scrollY,
|
||||||
viewBackgroundColor: this.state.viewBackgroundColor,
|
viewBackgroundColor: this.state.viewBackgroundColor,
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
const scrolledOutside = !atLeastOneVisibleElement && elements.length > 0;
|
||||||
|
if (this.state.scrolledOutside !== scrolledOutside) {
|
||||||
|
this.setState({ scrolledOutside: scrolledOutside });
|
||||||
|
}
|
||||||
this.saveDebounced();
|
this.saveDebounced();
|
||||||
if (history.isRecording()) {
|
if (history.isRecording()) {
|
||||||
history.pushEntry(
|
history.pushEntry(
|
||||||
|
@ -52,7 +52,8 @@
|
|||||||
"getShareableLink": "Get shareable link",
|
"getShareableLink": "Get shareable link",
|
||||||
"close": "Close",
|
"close": "Close",
|
||||||
"selectLanguage": "Select Language",
|
"selectLanguage": "Select Language",
|
||||||
"previouslyLoadedScenes": "Previously loaded scenes"
|
"previouslyLoadedScenes": "Previously loaded scenes",
|
||||||
|
"scrollBackToContent": "Scroll back to content"
|
||||||
},
|
},
|
||||||
"alerts": {
|
"alerts": {
|
||||||
"clearReset": "This will clear the whole canvas. Are you sure?",
|
"clearReset": "This will clear the whole canvas. Are you sure?",
|
||||||
|
@ -32,7 +32,7 @@ export function renderScene(
|
|||||||
renderSelection?: boolean;
|
renderSelection?: boolean;
|
||||||
} = {},
|
} = {},
|
||||||
) {
|
) {
|
||||||
if (!canvas) return;
|
if (!canvas) return false;
|
||||||
const context = canvas.getContext("2d")!;
|
const context = canvas.getContext("2d")!;
|
||||||
|
|
||||||
const fillStyle = context.fillStyle;
|
const fillStyle = context.fillStyle;
|
||||||
@ -57,6 +57,7 @@ export function renderScene(
|
|||||||
scrollY: typeof offsetY === "number" ? offsetY : sceneState.scrollY,
|
scrollY: typeof offsetY === "number" ? offsetY : sceneState.scrollY,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let atLeastOneVisibleElement = false;
|
||||||
elements.forEach(element => {
|
elements.forEach(element => {
|
||||||
if (
|
if (
|
||||||
!isVisibleElement(
|
!isVisibleElement(
|
||||||
@ -71,6 +72,7 @@ export function renderScene(
|
|||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
atLeastOneVisibleElement = true;
|
||||||
context.translate(
|
context.translate(
|
||||||
element.x + sceneState.scrollX,
|
element.x + sceneState.scrollX,
|
||||||
element.y + sceneState.scrollY,
|
element.y + sceneState.scrollY,
|
||||||
@ -141,6 +143,8 @@ export function renderScene(
|
|||||||
context.strokeStyle = strokeStyle;
|
context.strokeStyle = strokeStyle;
|
||||||
context.fillStyle = fillStyle;
|
context.fillStyle = fillStyle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return atLeastOneVisibleElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isVisibleElement(
|
function isVisibleElement(
|
||||||
|
@ -46,7 +46,7 @@ export function serializeAsJSON(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function calculateScrollCenter(
|
export function calculateScrollCenter(
|
||||||
elements: readonly ExcalidrawElement[],
|
elements: readonly ExcalidrawElement[],
|
||||||
): { scrollX: number; scrollY: number } {
|
): { scrollX: number; scrollY: number } {
|
||||||
let [x1, y1, x2, y2] = getCommonBounds(elements);
|
let [x1, y1, x2, y2] = getCommonBounds(elements);
|
||||||
|
@ -17,6 +17,7 @@ export {
|
|||||||
importFromBackend,
|
importFromBackend,
|
||||||
addToLoadedScenes,
|
addToLoadedScenes,
|
||||||
loadedScenes,
|
loadedScenes,
|
||||||
|
calculateScrollCenter,
|
||||||
} from "./data";
|
} from "./data";
|
||||||
export {
|
export {
|
||||||
hasBackground,
|
hasBackground,
|
||||||
|
@ -256,3 +256,11 @@ button,
|
|||||||
clip: rect(1px, 1px, 1px, 1px);
|
clip: rect(1px, 1px, 1px, 1px);
|
||||||
white-space: nowrap; /* added line */
|
white-space: nowrap; /* added line */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.scroll-back-to-content {
|
||||||
|
position: fixed;
|
||||||
|
left: 50%;
|
||||||
|
bottom: 20px;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
padding: 10px 20px;
|
||||||
|
}
|
||||||
|
@ -22,6 +22,7 @@ export type AppState = {
|
|||||||
scrollY: number;
|
scrollY: number;
|
||||||
cursorX: number;
|
cursorX: number;
|
||||||
cursorY: number;
|
cursorY: number;
|
||||||
|
scrolledOutside: boolean;
|
||||||
name: string;
|
name: string;
|
||||||
selectedId?: string;
|
selectedId?: string;
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user