Floating player position-listener fixes (#289)
## Issues - 251 Dragging the floating player is super laggy Recent changes and/or refactoring combined the effects or added dependancies into the effects, causing them to re-run excessively. ## Changes - Restored effects to their original behavior. - Don't perform the position check when dragging -- only do it when released. - Do proper debouncing for the 'resize' listener -- the previous method was incorrect as a new function is created on each render.
This commit is contained in:
parent
cb104017ad
commit
c8ad9718bb
1 changed files with 98 additions and 90 deletions
|
@ -19,9 +19,64 @@ import { isURIEqual } from 'util/lbryURI';
|
|||
import AutoplayCountdown from 'component/autoplayCountdown';
|
||||
|
||||
const IS_DESKTOP_MAC = typeof process === 'object' ? process.platform === 'darwin' : false;
|
||||
const DEBOUNCE_WINDOW_RESIZE_HANDLER_MS = 60;
|
||||
const DEBOUNCE_WINDOW_RESIZE_HANDLER_MS = 100;
|
||||
export const INLINE_PLAYER_WRAPPER_CLASS = 'inline-player__wrapper';
|
||||
|
||||
function getScreenWidth() {
|
||||
if (document && document.documentElement) {
|
||||
return document.documentElement.clientWidth;
|
||||
} else {
|
||||
return window.innerWidth;
|
||||
}
|
||||
}
|
||||
|
||||
function getScreenHeight() {
|
||||
if (document && document.documentElement) {
|
||||
return document.documentElement.clientHeight;
|
||||
} else {
|
||||
return window.innerHeight;
|
||||
}
|
||||
}
|
||||
|
||||
function getFloatingPlayerRect() {
|
||||
// TODO: use 'fileViewerRect'?
|
||||
const FLOATING_PLAYER_CLASS = 'content__viewer--floating';
|
||||
const elem = document.querySelector(`.${FLOATING_PLAYER_CLASS}`);
|
||||
if (elem) {
|
||||
return elem.getBoundingClientRect();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function clampRectToScreen(x, y, rect) {
|
||||
if (rect) {
|
||||
const ESTIMATED_SCROLL_BAR_PX = 50;
|
||||
const screenW = getScreenWidth();
|
||||
const screenH = getScreenHeight();
|
||||
|
||||
if (x + rect.width > screenW - ESTIMATED_SCROLL_BAR_PX) {
|
||||
x = screenW - rect.width - ESTIMATED_SCROLL_BAR_PX;
|
||||
}
|
||||
|
||||
if (y + rect.height > screenH) {
|
||||
y = screenH - rect.height;
|
||||
}
|
||||
}
|
||||
|
||||
return { x, y };
|
||||
}
|
||||
|
||||
function calculateRelativePos(x, y) {
|
||||
return {
|
||||
x: x / getScreenWidth(),
|
||||
y: y / getScreenHeight(),
|
||||
};
|
||||
}
|
||||
|
||||
// ****************************************************************************
|
||||
// ****************************************************************************
|
||||
|
||||
type Props = {
|
||||
isFloating: boolean,
|
||||
fileInfo: FileListItem,
|
||||
|
@ -84,10 +139,7 @@ export default function FileRenderFloating(props: Props) {
|
|||
x: -25,
|
||||
y: window.innerHeight - 400,
|
||||
});
|
||||
const [relativePos, setRelativePos] = useState({
|
||||
x: 0,
|
||||
y: 0,
|
||||
});
|
||||
const relativePosRef = React.useRef({ x: 0, y: 0 });
|
||||
|
||||
const navigateUrl =
|
||||
playingUri &&
|
||||
|
@ -102,93 +154,49 @@ export default function FileRenderFloating(props: Props) {
|
|||
? __("It looks like you deleted or moved this file. We're rebuilding it now. It will only take a few seconds.")
|
||||
: __('Loading');
|
||||
|
||||
function getScreenWidth() {
|
||||
if (document && document.documentElement) {
|
||||
return document.documentElement.clientWidth;
|
||||
} else {
|
||||
return window.innerWidth;
|
||||
}
|
||||
function restoreToRelativePosition() {
|
||||
const newX = Math.round(relativePosRef.current.x * getScreenWidth());
|
||||
const newY = Math.round(relativePosRef.current.y * getScreenHeight());
|
||||
setPosition(clampRectToScreen(newX, newY, getFloatingPlayerRect()));
|
||||
}
|
||||
|
||||
function getScreenHeight() {
|
||||
if (document && document.documentElement) {
|
||||
return document.documentElement.clientHeight;
|
||||
} else {
|
||||
return window.innerHeight;
|
||||
}
|
||||
}
|
||||
const clampToScreenOnResize = React.useCallback(
|
||||
debounce(() => {
|
||||
restoreToRelativePosition();
|
||||
}, DEBOUNCE_WINDOW_RESIZE_HANDLER_MS),
|
||||
[]
|
||||
);
|
||||
|
||||
const clampToScreen = React.useCallback((pos) => {
|
||||
const ESTIMATED_SCROLL_BAR_PX = 50;
|
||||
const FLOATING_PLAYER_CLASS = 'content__viewer--floating';
|
||||
const fpPlayerElem = document.querySelector(`.${FLOATING_PLAYER_CLASS}`);
|
||||
|
||||
if (fpPlayerElem) {
|
||||
if (pos.x + fpPlayerElem.getBoundingClientRect().width > getScreenWidth() - ESTIMATED_SCROLL_BAR_PX) {
|
||||
pos.x = getScreenWidth() - fpPlayerElem.getBoundingClientRect().width - ESTIMATED_SCROLL_BAR_PX;
|
||||
}
|
||||
if (pos.y + fpPlayerElem.getBoundingClientRect().height > getScreenHeight()) {
|
||||
pos.y = getScreenHeight() - fpPlayerElem.getBoundingClientRect().height;
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
|
||||
// Updated 'relativePos' based on persisted 'position':
|
||||
const stringifiedPosition = JSON.stringify(position);
|
||||
// ???
|
||||
useEffect(() => {
|
||||
const jsonPosition = JSON.parse(stringifiedPosition);
|
||||
|
||||
setRelativePos({
|
||||
x: jsonPosition.x / getScreenWidth(),
|
||||
y: jsonPosition.y / getScreenHeight(),
|
||||
});
|
||||
}, [stringifiedPosition]);
|
||||
|
||||
// Ensure player is within screen when 'isFloating' changes.
|
||||
useEffect(() => {
|
||||
const jsonPosition = JSON.parse(stringifiedPosition);
|
||||
|
||||
if (isFloating) {
|
||||
// When the player begins floating, remove the comment source
|
||||
// so that it doesn't try to resize again in case of going back
|
||||
// to the origin's comment section and fail to position correctly
|
||||
if (isComment && playingUri) clearSecondarySource(playingUri.uri);
|
||||
}
|
||||
}, [isFloating, isComment, clearSecondarySource, playingUri]);
|
||||
|
||||
let pos = { x: jsonPosition.x, y: jsonPosition.y };
|
||||
clampToScreen(pos);
|
||||
if (pos.x !== position.x || pos.y !== position.y) {
|
||||
setPosition({ x: pos.x, y: pos.y });
|
||||
// Initial update for relativePosRef:
|
||||
useEffect(() => {
|
||||
relativePosRef.current = calculateRelativePos(position.x, position.y);
|
||||
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
// Ensure player is within screen when 'isFloating' changes.
|
||||
useEffect(() => {
|
||||
if (isFloating) {
|
||||
restoreToRelativePosition();
|
||||
}
|
||||
}
|
||||
}, [
|
||||
clampToScreen,
|
||||
clearSecondarySource,
|
||||
isComment,
|
||||
isFloating,
|
||||
playingUri,
|
||||
position.x,
|
||||
position.y,
|
||||
setPosition,
|
||||
stringifiedPosition,
|
||||
]);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isFloating]);
|
||||
|
||||
// Listen to main-window resizing and adjust the fp position accordingly:
|
||||
useEffect(() => {
|
||||
const handleMainWindowResize = debounce(() => {
|
||||
let newPos = {
|
||||
x: Math.round(relativePos.x * getScreenWidth()),
|
||||
y: Math.round(relativePos.y * getScreenHeight()),
|
||||
};
|
||||
clampToScreen(newPos);
|
||||
setPosition({ x: newPos.x, y: newPos.y });
|
||||
}, DEBOUNCE_WINDOW_RESIZE_HANDLER_MS);
|
||||
|
||||
window.addEventListener('resize', handleMainWindowResize);
|
||||
return () => window.removeEventListener('resize', handleMainWindowResize);
|
||||
|
||||
// 'relativePos' is needed in the dependency list to avoid stale closure.
|
||||
// Otherwise, this could just be changed to a one-time effect.
|
||||
}, [clampToScreen, relativePos.x, relativePos.y, setPosition]);
|
||||
if (isFloating) {
|
||||
window.addEventListener('resize', clampToScreenOnResize);
|
||||
return () => window.removeEventListener('resize', clampToScreenOnResize);
|
||||
}
|
||||
}, [isFloating, clampToScreenOnResize]);
|
||||
|
||||
const handleResize = React.useCallback(() => {
|
||||
const element = mainFilePlaying
|
||||
|
@ -283,25 +291,25 @@ export default function FileRenderFloating(props: Props) {
|
|||
|
||||
function handleDragMove(e, ui) {
|
||||
const { x, y } = position;
|
||||
const newX = x + ui.deltaX;
|
||||
const newY = y + ui.deltaY;
|
||||
const newX = ui.x;
|
||||
const newY = ui.y;
|
||||
|
||||
// Mark as dragging if the position changed and we were not dragging before.
|
||||
if (!wasDragging && (newX !== x || newY !== y)) {
|
||||
setWasDragging(true);
|
||||
}
|
||||
setPosition({
|
||||
x: newX,
|
||||
y: newY,
|
||||
});
|
||||
}
|
||||
|
||||
function handleDragStop(e) {
|
||||
function handleDragStop(e, ui) {
|
||||
if (wasDragging) {
|
||||
setWasDragging(false);
|
||||
setRelativePos({
|
||||
x: position.x / getScreenWidth(),
|
||||
y: position.y / getScreenHeight(),
|
||||
});
|
||||
}
|
||||
|
||||
let newPos = { x: ui.x, y: ui.y };
|
||||
if (newPos.x !== position.x || newPos.y !== position.y) {
|
||||
newPos = clampRectToScreen(newPos.x, newPos.y, getFloatingPlayerRect());
|
||||
setPosition(newPos);
|
||||
relativePosRef.current = calculateRelativePos(newPos.x, newPos.y);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue