@@ -5,6 +5,11 @@ import type { ExecutionTask } from '@midscene/core';
55import { useTheme } from '@midscene/visualizer' ;
66import { useAllCurrentTasks , useExecutionDump } from '../store' ;
77import { buildTimelineScreenshots } from './build-timeline-screenshots' ;
8+ import {
9+ DEFAULT_TIMELINE_MAX_TIME_MS ,
10+ createTimelineScale ,
11+ formatTimelineTime ,
12+ } from './timeline-scale' ;
813
914interface TimelineItem {
1015 id : string ;
@@ -65,7 +70,7 @@ const TimelineWidget = (props: {
6570 const { isDarkMode } = useTheme ( ) ;
6671
6772 const allScreenshots = props . screenshots || [ ] ;
68- let maxTime = 500 ;
73+ let maxTime = DEFAULT_TIMELINE_MAX_TIME_MS ;
6974 if ( allScreenshots . length >= 2 ) {
7075 maxTime = Math . max (
7176 allScreenshots [ allScreenshots . length - 1 ] . timeOffset ,
@@ -125,30 +130,12 @@ const TimelineWidget = (props: {
125130 const { clientWidth } = domRef . current ;
126131 const canvasWidth = clientWidth * sizeRatio ;
127132 const canvasHeight = BASE_HEIGHT * sizeRatio ;
128-
129- // Grid calculations
130- let singleGridWidth = 100 * sizeRatio ;
131- let gridCount = Math . floor ( canvasWidth / singleGridWidth ) ;
132- const stepCandidate = [
133- 50 , 100 , 200 , 300 , 500 , 1000 , 2000 , 3000 , 5000 , 6000 , 8000 , 9000 , 10000 ,
134- 20000 , 30000 , 40000 , 60000 , 90000 , 12000 , 300000 ,
135- ] ;
136- let timeStep = stepCandidate [ 0 ] ;
137- for ( let i = stepCandidate . length - 1 ; i >= 0 ; i -- ) {
138- if ( gridCount * stepCandidate [ i ] >= maxTime ) {
139- timeStep = stepCandidate [ i ] ;
140- }
141- }
142- const gridRatio = maxTime / ( gridCount * timeStep ) ;
143- if ( gridRatio <= 0.8 ) {
144- singleGridWidth = Math . floor ( singleGridWidth * ( 1 / gridRatio ) * 0.9 ) ;
145- gridCount = Math . floor ( canvasWidth / singleGridWidth ) ;
146- }
147-
148- const leftForTimeOffset = ( t : number ) =>
149- Math . floor ( ( singleGridWidth * t ) / timeStep ) ;
150- const timeOffsetForLeft = ( l : number ) =>
151- Math . floor ( ( l * timeStep ) / singleGridWidth ) ;
133+ const { timeStep, visibleMaxTime, leftForTimeOffset, timeOffsetForLeft } =
134+ createTimelineScale ( {
135+ canvasWidth,
136+ maxTime,
137+ sizeRatio,
138+ } ) ;
152139
153140 // Create canvas
154141 const canvas = document . createElement ( 'canvas' ) ;
@@ -162,11 +149,6 @@ const TimelineWidget = (props: {
162149 const screenshotMaxHeight =
163150 canvasHeight - screenshotTop - commonPadding * 1.5 ;
164151
165- const formatTime = ( num : number ) => {
166- const s = num / 1000 ;
167- return s % 1 === 0 ? `${ s } s` : `${ s . toFixed ( 1 ) } s` ;
168- } ;
169-
170152 // Viewport-aware lazy loading: downsample by pixel position, then load rest
171153 const { imgCache } = stateRef . current ;
172154 let isMounted = true ;
@@ -266,12 +248,12 @@ const TimelineWidget = (props: {
266248
267249 // Grid lines + time labels
268250 ctx . font = `${ timeContentFontSize } px sans-serif` ;
269- for ( let i = 1 ; i <= gridCount ; i ++ ) {
270- const x = leftForTimeOffset ( i * timeStep ) ;
251+ for ( let tickMs = timeStep ; tickMs < visibleMaxTime ; tickMs += timeStep ) {
252+ const x = leftForTimeOffset ( tickMs ) ;
271253 ctx . fillStyle = hexToCSS ( gridLineColor ) ;
272254 ctx . fillRect ( x , 0 , sizeRatio , canvasHeight ) ;
273255
274- const label = formatTime ( i * timeStep ) ;
256+ const label = formatTimelineTime ( tickMs ) ;
275257 const tw = ctx . measureText ( label ) . width ;
276258 ctx . fillStyle = hexToCSS ( gridTextColor ) ;
277259 ctx . fillText (
@@ -354,7 +336,7 @@ const TimelineWidget = (props: {
354336 }
355337
356338 // Time label at cursor
357- const label = formatTime ( timeOffsetForLeft ( hoverX ) ) ;
339+ const label = formatTimelineTime ( timeOffsetForLeft ( hoverX ) ) ;
358340 const tw = ctx . measureText ( label ) . width ;
359341 ctx . fillStyle = hexToCSS ( titleBg ) ;
360342 ctx . fillRect ( hoverX + 5 , timeTextTop , tw + 10 , timeContentFontSize + 4 ) ;
0 commit comments