Moved map asset loading progress to use refs to stop re-renders

Moved map loading overlay to use animation frames for a smooth progress bar
This commit is contained in:
Mitchell McCaffrey 2020-06-28 13:37:04 +10:00
parent bf022e2686
commit a81031e84e
4 changed files with 82 additions and 26 deletions

View File

@ -1,9 +1,9 @@
import React from "react";
import { Box, Progress } from "theme-ui";
import { Box } from "theme-ui";
import Spinner from "./Spinner";
function LoadingOverlay({ progress }) {
function LoadingOverlay() {
return (
<Box
sx={{
@ -20,9 +20,6 @@ function LoadingOverlay({ progress }) {
bg="muted"
>
<Spinner />
{progress && (
<Progress max={1} value={progress} m={2} sx={{ width: "24px" }} />
)}
</Box>
);
}

View File

@ -8,13 +8,12 @@ import MapFog from "./MapFog";
import MapDice from "./MapDice";
import MapGrid from "./MapGrid";
import MapMeasure from "./MapMeasure";
import MapLoadingOverlay from "./MapLoadingOverlay";
import TokenDataContext from "../../contexts/TokenDataContext";
import MapLoadingContext from "../../contexts/MapLoadingContext";
import TokenMenu from "../token/TokenMenu";
import TokenDragOverlay from "../token/TokenDragOverlay";
import LoadingOverlay from "../LoadingOverlay";
import { drawActionsToShapes } from "../../helpers/drawing";
@ -36,7 +35,6 @@ function Map({
disabledTokens,
}) {
const { tokensById } = useContext(TokenDataContext);
const { isLoading, loadingProgress } = useContext(MapLoadingContext);
const gridX = map && map.gridX;
const gridY = map && map.gridY;
@ -299,7 +297,7 @@ function Map({
{tokenMenu}
{tokenDragOverlay}
<MapDice />
{isLoading && <LoadingOverlay progress={loadingProgress} />}
<MapLoadingOverlay />
</>
}
selectedToolId={selectedToolId}

View File

@ -0,0 +1,61 @@
import React, { useContext, useEffect, useRef } from "react";
import { Box, Progress } from "theme-ui";
import Spinner from "../Spinner";
import MapLoadingContext from "../../contexts/MapLoadingContext";
function MapLoadingOverlay() {
const { isLoading, loadingProgressRef } = useContext(MapLoadingContext);
const requestRef = useRef();
const progressBarRef = useRef();
// Use an animation frame to update the progress bar
// This bypasses react allowing the animation to be smooth
useEffect(() => {
function animate() {
if (!isLoading) {
return;
}
requestRef.current = requestAnimationFrame(animate);
progressBarRef.current.value = loadingProgressRef.current;
}
requestRef.current = requestAnimationFrame(animate);
return () => {
cancelAnimationFrame(requestRef.current);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isLoading]);
return (
isLoading && (
<Box
sx={{
position: "absolute",
width: "100%",
height: "100%",
display: "flex",
justifyContent: "center",
alignItems: "center",
top: 0,
left: 0,
flexDirection: "column",
}}
bg="muted"
>
<Spinner />
<Progress
ref={progressBarRef}
max={1}
value={0}
m={2}
sx={{ width: "32px" }}
/>
</Box>
)
);
}
export default MapLoadingOverlay;

View File

@ -1,4 +1,4 @@
import React, { useState } from "react";
import React, { useState, useRef } from "react";
import { omit, isEmpty } from "../helpers/shared";
const MapLoadingContext = React.createContext();
@ -14,36 +14,36 @@ export function MapLoadingProvider({ children }) {
setLoadingAssetCount((prevLoadingAssets) => prevLoadingAssets - 1);
}
const [assetProgress, setAssetProgress] = useState({});
const assetProgressRef = useRef({});
const loadingProgressRef = useRef(null);
function assetProgressUpdate({ id, count, total }) {
if (count === total) {
setAssetProgress(omit(assetProgress, [id]));
assetProgressRef.current = omit(assetProgressRef.current, [id]);
} else {
setAssetProgress((prevAssetProgress) => ({
...prevAssetProgress,
assetProgressRef.current = {
...assetProgressRef.current,
[id]: { count, total },
}));
};
}
if (!isEmpty(assetProgressRef.current)) {
let total = 0;
let count = 0;
for (let progress of Object.values(assetProgressRef.current)) {
total += progress.total;
count += progress.count;
}
loadingProgressRef.current = count / total;
}
}
const isLoading = loadingAssetCount > 0;
let loadingProgress = null;
if (!isEmpty(assetProgress)) {
let total = 0;
let count = 0;
for (let progress of Object.values(assetProgress)) {
total += progress.total;
count += progress.count;
}
loadingProgress = count / total;
}
const value = {
assetLoadStart,
assetLoadFinish,
isLoading,
assetProgressUpdate,
loadingProgress,
loadingProgressRef,
};
return (