1
- import { cross , difference , groups , InternMap , select } from "d3" ;
1
+ import { bisectLeft , cross , difference , groups , InternMap , select } from "d3" ;
2
2
import { Axes , autoAxisTicks , autoScaleLabels } from "./axes.js" ;
3
3
import { Channel , Channels , channelDomain , valueObject } from "./channel.js" ;
4
4
import { Context , create } from "./context.js" ;
@@ -124,6 +124,9 @@ export function plot(options = {}) {
124
124
125
125
autoScaleLabels ( channelsByScale , scaleDescriptors , axes , dimensions , options ) ;
126
126
127
+ // Aggregate and sort time channels.
128
+ const times = aggregateTimes ( stateByMark ) ;
129
+
127
130
// Compute value objects, applying scales as needed.
128
131
for ( const state of stateByMark . values ( ) ) {
129
132
state . values = valueObject ( state . channels , scales ) ;
@@ -213,11 +216,31 @@ export function plot(options = {}) {
213
216
}
214
217
} ) ;
215
218
} else {
219
+ const timeMarks = [ ] ;
216
220
for ( const [ mark , { channels, values, facets} ] of stateByMark ) {
217
221
const facet = facets ? mark . filter ( facets [ 0 ] , channels , values ) : null ;
218
- const node = mark . render ( facet , scales , values , dimensions , context ) ;
222
+ const index = channels . time ? [ ] : facet ;
223
+ const node = mark . render ( index , scales , values , dimensions , context ) ;
224
+ if ( channels . time ) timeMarks . push ( { mark, node} ) ;
219
225
if ( node != null ) svg . appendChild ( node ) ;
220
226
}
227
+ if ( timeMarks . length ) {
228
+ let timeIndex = - 1 ;
229
+ requestAnimationFrame ( function tick ( ) {
230
+ if ( ++ timeIndex >= times . length ) return ;
231
+ const time = times [ timeIndex ] ;
232
+ for ( const timeMark of timeMarks ) {
233
+ const { mark, node} = timeMark ;
234
+ const { channels, values, facets} = stateByMark . get ( mark ) ;
235
+ const facet = facets ? mark . filter ( facets [ 0 ] , channels , values ) : null ;
236
+ const index = facet . filter ( i => channels . time . value [ i ] <= time ) ;
237
+ const timeNode = mark . render ( index , scales , values , dimensions , context ) ;
238
+ node . replaceWith ( timeNode ) ;
239
+ timeMark . node = timeNode ;
240
+ }
241
+ requestAnimationFrame ( tick ) ;
242
+ } ) ;
243
+ }
221
244
}
222
245
223
246
// Wrap the plot in a figure with a caption, if desired.
@@ -257,7 +280,7 @@ export function plot(options = {}) {
257
280
258
281
export class Mark {
259
282
constructor ( data , channels = { } , options = { } , defaults ) {
260
- const { facet = "auto" , sort, dx, dy, clip, channels : extraChannels } = options ;
283
+ const { facet = "auto" , sort, time , dx, dy, clip, channels : extraChannels } = options ;
261
284
this . data = data ;
262
285
this . sort = isDomainSort ( sort ) ? sort : null ;
263
286
this . initializer = initializer ( options ) . initializer ;
@@ -266,6 +289,7 @@ export class Mark {
266
289
channels = maybeNamed ( channels ) ;
267
290
if ( extraChannels !== undefined ) channels = { ...maybeNamed ( extraChannels ) , ...channels } ;
268
291
if ( defaults !== undefined ) channels = { ...styles ( this , options , defaults ) , ...channels } ;
292
+ if ( time != null ) channels = { time : { value : time } , ...channels } ;
269
293
this . channels = Object . fromEntries ( Object . entries ( channels ) . filter ( ( [ name , { value, optional} ] ) => {
270
294
if ( value != null ) return true ;
271
295
if ( optional ) return false ;
@@ -367,6 +391,21 @@ function addScaleChannels(channelsByScale, stateByMark, filter = yes) {
367
391
return channelsByScale ;
368
392
}
369
393
394
+ function aggregateTimes ( stateByMark ) {
395
+ const times = [ ] ;
396
+ for ( const { channels : { time} } of stateByMark . values ( ) ) {
397
+ if ( time ) {
398
+ for ( let t of time . value ) {
399
+ if ( t == null || isNaN ( t = + t ) ) continue ;
400
+ const i = bisectLeft ( times , t ) ;
401
+ if ( times [ i ] === t ) continue ;
402
+ times . splice ( i , 0 , t ) ;
403
+ }
404
+ }
405
+ }
406
+ return times ;
407
+ }
408
+
370
409
// Derives a copy of the specified axis with the label disabled.
371
410
function nolabel ( axis ) {
372
411
return axis === undefined || axis . label === undefined
0 commit comments