mirror of
				https://github.com/CorentinTh/it-tools.git
				synced 2025-11-03 21:43:21 +00:00 
			
		
		
		
	refactor(chronometer): improved chronometer precision
This commit is contained in:
		@@ -1,12 +1,13 @@
 | 
				
			|||||||
import { describe, expect, it } from 'vitest';
 | 
					import { describe, expect, it } from 'vitest';
 | 
				
			||||||
import { formatChronometerTime } from './chronometer.service';
 | 
					import { formatMs } from './chronometer.service';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('chronometer', () => {
 | 
					describe('chronometer', () => {
 | 
				
			||||||
  describe('formatChronometerTime', () => {
 | 
					  describe('formatChronometerTime', () => {
 | 
				
			||||||
    it('format the elapsed time', () => {
 | 
					    it('format the elapsed time', () => {
 | 
				
			||||||
      expect(formatChronometerTime({ elapsed: 123456 })).toEqual('02:03.456');
 | 
					      expect(formatMs(0)).toEqual('00:00.000');
 | 
				
			||||||
      expect(formatChronometerTime({ elapsed: 123456, msPerUnit: 100 })).toEqual('03:25:45.600');
 | 
					      expect(formatMs(1)).toEqual('00:00.001');
 | 
				
			||||||
      expect(formatChronometerTime({ elapsed: 12345600 })).toEqual('03:25:45.600');
 | 
					      expect(formatMs(123456)).toEqual('02:03.456');
 | 
				
			||||||
 | 
					      expect(formatMs(12345600)).toEqual('03:25:45.600');
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,10 +1,8 @@
 | 
				
			|||||||
export function formatChronometerTime({ elapsed, msPerUnit = 1 }: { elapsed: number; msPerUnit?: number }) {
 | 
					export function formatMs(msTotal: number) {
 | 
				
			||||||
  const elapsedMs = elapsed * msPerUnit;
 | 
					  const ms = msTotal % 1000;
 | 
				
			||||||
 | 
					  const secs = ((msTotal - ms) / 1000) % 60;
 | 
				
			||||||
  const ms = elapsedMs % 1000;
 | 
					  const mins = (((msTotal - ms) / 1000 - secs) / 60) % 60;
 | 
				
			||||||
  const secs = ((elapsedMs - ms) / 1000) % 60;
 | 
					  const hrs = (((msTotal - ms) / 1000 - secs) / 60 - mins) / 60;
 | 
				
			||||||
  const mins = (((elapsedMs - ms) / 1000 - secs) / 60) % 60;
 | 
					 | 
				
			||||||
  const hrs = (((elapsedMs - ms) / 1000 - secs) / 60 - mins) / 60;
 | 
					 | 
				
			||||||
  const hrsString = hrs > 0 ? `${hrs.toString().padStart(2, '0')}:` : '';
 | 
					  const hrsString = hrs > 0 ? `${hrs.toString().padStart(2, '0')}:` : '';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return `${hrsString}${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}.${ms
 | 
					  return `${hrsString}${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}.${ms
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,11 +1,11 @@
 | 
				
			|||||||
<template>
 | 
					<template>
 | 
				
			||||||
  <div>
 | 
					  <div>
 | 
				
			||||||
    <n-card>
 | 
					    <n-card>
 | 
				
			||||||
      <div class="duration">{{ formatChronometerTime({ elapsed: counter, msPerUnit }) }}</div>
 | 
					      <div class="duration">{{ formatMs(counter) }}</div>
 | 
				
			||||||
    </n-card>
 | 
					    </n-card>
 | 
				
			||||||
    <br />
 | 
					    <br />
 | 
				
			||||||
    <n-space justify="center">
 | 
					    <n-space justify="center">
 | 
				
			||||||
      <n-button v-if="!isActive" secondary type="primary" @click="resume">Start</n-button>
 | 
					      <n-button v-if="!isRunning" secondary type="primary" @click="resume">Start</n-button>
 | 
				
			||||||
      <n-button v-else secondary type="warning" @click="pause">Stop</n-button>
 | 
					      <n-button v-else secondary type="warning" @click="pause">Stop</n-button>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      <n-button secondary @click="counter = 0">Reset</n-button>
 | 
					      <n-button secondary @click="counter = 0">Reset</n-button>
 | 
				
			||||||
@@ -14,12 +14,33 @@
 | 
				
			|||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script setup lang="ts">
 | 
					<script setup lang="ts">
 | 
				
			||||||
import { useInterval } from '@vueuse/core';
 | 
					import { useRafFn } from '@vueuse/core';
 | 
				
			||||||
import { formatChronometerTime } from './chronometer.service';
 | 
					import { ref } from 'vue';
 | 
				
			||||||
 | 
					import { formatMs } from './chronometer.service';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const msPerUnit = 10;
 | 
					const isRunning = ref(false);
 | 
				
			||||||
 | 
					const counter = ref(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const { counter, pause, resume, isActive } = useInterval(msPerUnit, { controls: true, immediate: false });
 | 
					let previousRafDate = Date.now();
 | 
				
			||||||
 | 
					const { pause: pauseRaf, resume: resumeRaf } = useRafFn(
 | 
				
			||||||
 | 
					  () => {
 | 
				
			||||||
 | 
					    const deltaMs = Date.now() - previousRafDate;
 | 
				
			||||||
 | 
					    previousRafDate = Date.now();
 | 
				
			||||||
 | 
					    counter.value += deltaMs;
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  { immediate: false },
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function resume() {
 | 
				
			||||||
 | 
					  previousRafDate = Date.now();
 | 
				
			||||||
 | 
					  resumeRaf();
 | 
				
			||||||
 | 
					  isRunning.value = true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function pause() {
 | 
				
			||||||
 | 
					  pauseRaf();
 | 
				
			||||||
 | 
					  isRunning.value = false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<style lang="less" scoped>
 | 
					<style lang="less" scoped>
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user