mirror of
https://github.com/cpvalente/ontime.git
synced 2026-07-26 10:38:55 +00:00
fix(ui): improve progress bar with local interpolation
Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com>
This commit is contained in:
@@ -38,8 +38,6 @@ $progress-bar-br: 3px;
|
||||
.multiprogress-bar__indicator-bar {
|
||||
background-color: var(--background-color-override, $ui-black);
|
||||
opacity: 0.8;
|
||||
transition: 1s linear;
|
||||
transition-property: width;
|
||||
|
||||
.multiprogress-bar--ignore-css-override & {
|
||||
background-color: $ui-black;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { MaybeNumber } from 'ontime-types';
|
||||
|
||||
import { useAnimatedProgress } from '../../hooks/useAnimatedProgress';
|
||||
import { getProgress } from '../../utils/getProgress';
|
||||
import { cx } from '../../utils/styleUtils';
|
||||
|
||||
@@ -34,7 +35,7 @@ export default function MultiPartProgressBar(props: MultiPartProgressBar) {
|
||||
className = '',
|
||||
} = props;
|
||||
|
||||
const percentRemaining = 100 - getProgress(now, complete);
|
||||
const percentRemaining = 100 - useAnimatedProgress(now, complete);
|
||||
const dangerWidth = danger ? 100 - getProgress(danger, complete) : 0;
|
||||
const warningWidth = warning ? 100 - dangerWidth - getProgress(warning, complete) : 0;
|
||||
const isOvertime = now !== null && now < 0;
|
||||
|
||||
@@ -14,6 +14,4 @@ $progress-bar-br: 3px;
|
||||
.progress-bar__indicator {
|
||||
height: $progress-bar-size;
|
||||
background-color: var(--timer-progress-override, $accent-color);
|
||||
transition: 1s linear;
|
||||
transition-property: width;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { MaybeNumber } from 'ontime-types';
|
||||
|
||||
import { getProgress } from '../../utils/getProgress';
|
||||
import { useAnimatedProgress } from '../../hooks/useAnimatedProgress';
|
||||
|
||||
import './ProgressBar.scss';
|
||||
|
||||
@@ -12,7 +12,7 @@ interface ProgressBarProps {
|
||||
|
||||
export default function ProgressBar(props: ProgressBarProps) {
|
||||
const { current, duration, className } = props;
|
||||
const progress = getProgress(current, duration);
|
||||
const progress = useAnimatedProgress(current, duration);
|
||||
|
||||
return (
|
||||
<div className={`progress-bar__bg ${className}`}>
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import { MaybeNumber, Playback } from 'ontime-types';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
|
||||
import { getProgress } from '../utils/getProgress';
|
||||
import { usePlayback } from './useSocket';
|
||||
|
||||
/**
|
||||
* Returns the live completion percentage (0–100) of a countdown, interpolated locally.
|
||||
*/
|
||||
export function useAnimatedProgress(current: MaybeNumber, duration: MaybeNumber): number {
|
||||
const playback = usePlayback();
|
||||
const isRunning = playback === Playback.Play || playback === Playback.Roll;
|
||||
|
||||
const baseline = useRef({ current, at: performance.now() });
|
||||
const [, setTick] = useState(0);
|
||||
|
||||
// there is only something to animate while a running timer is counting down towards 0
|
||||
const shouldAnimate = isRunning && current !== null && current > 0 && duration !== null;
|
||||
|
||||
// re-anchor to the authoritative value whenever the server pushes a new timer update
|
||||
useEffect(() => {
|
||||
baseline.current = { current, at: performance.now() };
|
||||
}, [current, duration, playback]);
|
||||
|
||||
// while counting down, re-render every animation frame so the derived progress stays smooth
|
||||
useEffect(() => {
|
||||
if (!shouldAnimate) {
|
||||
return;
|
||||
}
|
||||
let frame = requestAnimationFrame(function tick() {
|
||||
setTick((value) => value + 1);
|
||||
frame = requestAnimationFrame(tick);
|
||||
});
|
||||
return () => cancelAnimationFrame(frame);
|
||||
}, [shouldAnimate]);
|
||||
|
||||
// derive from the anchor plus elapsed time at render; frozen to the anchor when not running
|
||||
const anchored = baseline.current.current;
|
||||
const value = isRunning && anchored !== null ? anchored - (performance.now() - baseline.current.at) : anchored;
|
||||
return getProgress(value, duration);
|
||||
}
|
||||
-2
@@ -3,7 +3,5 @@
|
||||
width: 0;
|
||||
border-radius: 1px 0 0 1px;
|
||||
|
||||
transition: 1s linear;
|
||||
transition-property: width;
|
||||
background-color: $gray-200;
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,12 +1,12 @@
|
||||
import { useAnimatedProgress } from '../../../../common/hooks/useAnimatedProgress';
|
||||
import { useTimer } from '../../../../common/hooks/useSocket';
|
||||
import { getProgress } from '../../../../common/utils/getProgress';
|
||||
|
||||
import style from './RundownEventProgressBar.module.scss';
|
||||
|
||||
export default function RundownEventProgressBar() {
|
||||
const timer = useTimer();
|
||||
|
||||
const progress = getProgress(timer.current, timer.duration);
|
||||
const progress = useAnimatedProgress(timer.current, timer.duration);
|
||||
|
||||
return <div className={style.progressBar} style={{ width: `${progress}%` }} />;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Day } from 'ontime-types';
|
||||
import { CSSProperties, RefObject } from 'react';
|
||||
|
||||
import { useAnimatedProgress } from '../../common/hooks/useAnimatedProgress';
|
||||
import { useExpectedStartData, useTimer } from '../../common/hooks/useSocket';
|
||||
import { getProgress } from '../../common/utils/getProgress';
|
||||
import { alpha, cx } from '../../common/utils/styleUtils';
|
||||
import { formatDuration, formatTime, getExpectedTimesFromExtendedEvent } from '../../common/utils/time';
|
||||
import { useTranslation } from '../../translation/TranslationProvider';
|
||||
@@ -169,7 +169,7 @@ function TimelineEntryStatus({
|
||||
/** Generates a block level progress bar */
|
||||
function ActiveBlock() {
|
||||
const { current, duration } = useTimer();
|
||||
const progress = getProgress(current, duration);
|
||||
const progress = useAnimatedProgress(current, duration);
|
||||
return (
|
||||
<div data-status='live' className={style.timelineBlock} style={{ '--progress': `${progress}%` } as CSSProperties} />
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user