Преглед изворни кода

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.
Christopher Chedeau пре 5 година
родитељ
комит
be97bd980e
8 измењених фајлова са 44 додато и 8 уклоњено
  1. 1 0
      src/appState.ts
  2. 25 5
      src/index.tsx
  3. 2 1
      src/locales/en.json
  4. 5 1
      src/renderer/renderScene.ts
  5. 1 1
      src/scene/data.ts
  6. 1 0
      src/scene/index.ts
  7. 8 0
      src/styles.scss
  8. 1 0
      src/types.ts

+ 1 - 0
src/appState.ts

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

+ 25 - 5
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<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(

+ 2 - 1
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?",

+ 5 - 1
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(

+ 1 - 1
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);

+ 1 - 0
src/scene/index.ts

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

+ 8 - 0
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;
+}

+ 1 - 0
src/types.ts

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