refactor(chronometer): improved chronometer precision
This commit is contained in:
		
							parent
							
								
									fda0b0ca25
								
							
						
					
					
						commit
						e48d60b1ed
					
				| @ -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> | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user