Skip to content

Commit 3d38ea0

Browse files
committed
Timer: ensure we never skip a time step
Generalize the idea of not skipping seconds to not skipping minutes, hours, etc. Also make sure now that no steps are skipped when switching from one unit to the smaller unit.
1 parent 40b384b commit 3d38ea0

File tree

1 file changed

+39
-17
lines changed

1 file changed

+39
-17
lines changed

src/components/Timer.vue

Lines changed: 39 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -59,27 +59,25 @@ interface CircleInfo {
5959
strokeWidth: number;
6060
}
6161
62+
const TIME_STEPS = [
63+
{ unit: 'minute', factor: 60 },
64+
{ unit: 'hour', factor: 60 },
65+
{ unit: 'day', factor: 24 },
66+
];
67+
6268
function _toSimplifiedTime(millis: number, includeUnit?: true): string;
6369
function _toSimplifiedTime(millis: number, includeUnit: false): number;
6470
function _toSimplifiedTime(millis: number, includeUnit: boolean = true): number | string {
6571
// find appropriate unit, starting with second
6672
let resultTime = millis / 1000;
6773
let resultUnit = 'second';
68-
const timeSteps = [
69-
{ unit: 'minute', factor: 60 },
70-
{ unit: 'hour', factor: 60 },
71-
{ unit: 'day', factor: 24 },
72-
];
73-
for (const { unit, factor } of timeSteps) {
74-
if (resultTime / factor < 1) {
75-
break;
76-
} else {
77-
resultTime /= factor;
78-
resultUnit = unit;
79-
}
74+
for (const { unit, factor } of TIME_STEPS) {
75+
if (resultTime / factor < 1) break;
76+
resultTime /= factor;
77+
resultUnit = unit;
8078
}
8179
82-
resultTime = Math.round(resultTime);
80+
resultTime = Math.floor(resultTime);
8381
if (!includeUnit) {
8482
return resultTime;
8583
} else {
@@ -215,10 +213,34 @@ class Timer extends Vue {
215213
const circleLengthPixels = this.fullCircleLength * scaleFactor;
216214
const steps = circleLengthPixels * 3; // update every .33 pixel change for smooth transitions
217215
const minInterval = 1000 / 60; // up to 60 fps
218-
const maxInterval = (this.detailsShown || this.alwaysShowTime) && this._timeLeft < 60000
219-
? 500 // when counting down seconds update more regularly
220-
: Number.POSITIVE_INFINITY;
221-
return Math.max(minInterval, Math.min(maxInterval, this._totalTime / steps));
216+
// Constrain interval such that we don't skip time steps in the countdown for the respective time unit.
217+
const timeLeft = this._timeLeft;
218+
const totalTime = this._totalTime;
219+
const updatesPerTimeStep = 2; // multiple updates per time step to avoid skipping a step by a delayed interval
220+
let timeStep = 1000; // starting with seconds
221+
let maxInterval = timeStep / updatesPerTimeStep;
222+
for (const { factor } of TIME_STEPS) {
223+
const nextTimeStep = timeStep * factor;
224+
const nextMaxInterval = nextTimeStep / updatesPerTimeStep;
225+
const nextInterval = Math.min(nextMaxInterval, Math.max(minInterval, totalTime / steps));
226+
if ((timeLeft - nextInterval) / nextTimeStep < 1) {
227+
// If the time left after nextInterval can't be expressed in nextTimeStep as a value >=1, stop. We check
228+
// for the time after the next interval to avoid jumping for example from 70s (displayed as 1 minute)
229+
// directly to 50s if the interval is 20s. Note that the behavior here resembles the one in
230+
// _toSimplifiedTime.
231+
if (timeLeft / nextTimeStep > 1) {
232+
// If the value before the interval is still >1 in the next time unit still allow a larger jump than
233+
// at the smaller time unit but set the maxInterval such that we jump no further than where the
234+
// switch to the smaller unit happens, for example jump from 70s to 60s if the interval is 20s.
235+
maxInterval = timeLeft - nextTimeStep;
236+
}
237+
break;
238+
}
239+
timeStep = nextTimeStep;
240+
maxInterval = nextMaxInterval;
241+
}
242+
243+
return Math.min(maxInterval, Math.max(minInterval, this._totalTime / steps));
222244
}
223245
224246
@Watch('detailsShown', { immediate: true })

0 commit comments

Comments
 (0)