@@ -266,43 +266,76 @@ function makePointPath(symbolNumber, r) {
266
266
267
267
var HORZGRADIENT = { x1 : 1 , x2 : 0 , y1 : 0 , y2 : 0 } ;
268
268
var VERTGRADIENT = { x1 : 0 , x2 : 0 , y1 : 1 , y2 : 0 } ;
269
+ var stopFormatter = d3 . format ( '~.1f' ) ;
270
+ var gradientInfo = {
271
+ radial : { node : 'radialGradient' } ,
272
+ radialreversed : { node : 'radialGradient' , reversed : true } ,
273
+ horizontal : { node : 'linearGradient' , attrs : HORZGRADIENT } ,
274
+ horizontalreversed : { node : 'linearGradient' , attrs : HORZGRADIENT , reversed : true } ,
275
+ vertical : { node : 'linearGradient' , attrs : VERTGRADIENT } ,
276
+ verticalreversed : { node : 'linearGradient' , attrs : VERTGRADIENT , reversed : true }
277
+ } ;
278
+
279
+ /**
280
+ * gradient: create and apply a gradient fill
281
+ *
282
+ * @param {object } sel: d3 selection to apply this gradient to
283
+ * You can use `selection.call(Drawing.gradient, ...)`
284
+ * @param {DOM element } gd: the graph div `sel` is part of
285
+ * @param {string } gradientID: a unique (within this plot) identifier
286
+ * for this gradient, so that we don't create unnecessary definitions
287
+ * @param {string } type: 'radial', 'horizontal', or 'vertical', optionally with
288
+ * 'reversed' at the end. Normally radial goes center to edge,
289
+ * horizontal goes right to left, and vertical goes bottom to top
290
+ * @param {array } colorscale: as in attribute values, [[fraction, color], ...]
291
+ * @param {string } prop: the property to apply to, 'fill' or 'stroke'
292
+ */
293
+ drawing . gradient = function ( sel , gd , gradientID , type , colorscale , prop ) {
294
+ var len = colorscale . length ;
295
+ var info = gradientInfo [ type ] ;
296
+ var colorStops = new Array ( len ) ;
297
+ for ( var i = 0 ; i < len ; i ++ ) {
298
+ if ( info . reversed ) {
299
+ colorStops [ len - 1 - i ] = [ stopFormatter ( ( 1 - colorscale [ i ] [ 0 ] ) * 100 ) , colorscale [ i ] [ 1 ] ] ;
300
+ }
301
+ else {
302
+ colorStops [ i ] = [ stopFormatter ( colorscale [ i ] [ 0 ] * 100 ) , colorscale [ i ] [ 1 ] ] ;
303
+ }
304
+ }
305
+
306
+ var fullID = 'g' + gd . _fullLayout . _uid + '-' + gradientID ;
269
307
270
- drawing . gradient = function ( sel , gd , gradientID , type , color1 , color2 ) {
271
308
var gradient = gd . _fullLayout . _defs . select ( '.gradients' )
272
- . selectAll ( '#' + gradientID )
273
- . data ( [ type + color1 + color2 ] , Lib . identity ) ;
309
+ . selectAll ( '#' + fullID )
310
+ . data ( [ type + colorStops . join ( ';' ) ] , Lib . identity ) ;
274
311
275
312
gradient . exit ( ) . remove ( ) ;
276
313
277
314
gradient . enter ( )
278
- . append ( type === 'radial' ? 'radialGradient' : 'linearGradient' )
315
+ . append ( info . node )
279
316
. each ( function ( ) {
280
317
var el = d3 . select ( this ) ;
281
- if ( type === 'horizontal' ) el . attr ( HORZGRADIENT ) ;
282
- else if ( type === 'vertical' ) el . attr ( VERTGRADIENT ) ;
283
-
284
- el . attr ( 'id' , gradientID ) ;
285
-
286
- var tc1 = tinycolor ( color1 ) ;
287
- var tc2 = tinycolor ( color2 ) ;
288
-
289
- el . append ( 'stop' ) . attr ( {
290
- offset : '0%' ,
291
- 'stop-color' : Color . tinyRGB ( tc2 ) ,
292
- 'stop-opacity' : tc2 . getAlpha ( )
293
- } ) ;
294
-
295
- el . append ( 'stop' ) . attr ( {
296
- offset : '100%' ,
297
- 'stop-color' : Color . tinyRGB ( tc1 ) ,
298
- 'stop-opacity' : tc1 . getAlpha ( )
318
+ if ( info . attrs ) el . attr ( info . attrs ) ;
319
+
320
+ el . attr ( 'id' , fullID ) ;
321
+
322
+ var stops = el . selectAll ( 'stop' )
323
+ . data ( colorStops ) ;
324
+ stops . exit ( ) . remove ( ) ;
325
+ stops . enter ( ) . append ( 'stop' ) ;
326
+
327
+ stops . each ( function ( d ) {
328
+ var tc = tinycolor ( d [ 1 ] ) ;
329
+ d3 . select ( this ) . attr ( {
330
+ offset : d [ 0 ] + '%' ,
331
+ 'stop-color' : Color . tinyRGB ( tc ) ,
332
+ 'stop-opacity' : tc . getAlpha ( )
333
+ } ) ;
299
334
} ) ;
300
335
} ) ;
301
336
302
- sel . style ( {
303
- fill : 'url(#' + gradientID + ')' ,
304
- 'fill-opacity' : null
305
- } ) ;
337
+ sel . style ( prop , 'url(#' + fullID + ')' )
338
+ . style ( prop + '-opacity' , null ) ;
306
339
} ;
307
340
308
341
/*
@@ -420,21 +453,29 @@ drawing.singlePointStyle = function(d, sel, trace, fns, gd) {
420
453
if ( gradientType ) perPointGradient = true ;
421
454
else gradientType = markerGradient && markerGradient . type ;
422
455
456
+ // for legend - arrays will propagate through here, but we don't need
457
+ // to treat it as per-point.
458
+ if ( Array . isArray ( gradientType ) ) {
459
+ gradientType = gradientType [ 0 ] ;
460
+ if ( ! gradientInfo [ gradientType ] ) gradientType = 0 ;
461
+ }
462
+
423
463
if ( gradientType && gradientType !== 'none' ) {
424
464
var gradientColor = d . mgc ;
425
465
if ( gradientColor ) perPointGradient = true ;
426
466
else gradientColor = markerGradient . color ;
427
467
428
- var gradientID = 'g' + gd . _fullLayout . _uid + '-' + trace . uid ;
468
+ var gradientID = trace . uid ;
429
469
if ( perPointGradient ) gradientID += '-' + d . i ;
430
470
431
- sel . call ( drawing . gradient , gd , gradientID , gradientType , fillColor , gradientColor ) ;
471
+ drawing . gradient ( sel , gd , gradientID , gradientType ,
472
+ [ [ 0 , gradientColor ] , [ 1 , fillColor ] ] , 'fill' ) ;
432
473
} else {
433
- sel . call ( Color . fill , fillColor ) ;
474
+ Color . fill ( sel , fillColor ) ;
434
475
}
435
476
436
477
if ( lineWidth ) {
437
- sel . call ( Color . stroke , lineColor ) ;
478
+ Color . stroke ( sel , lineColor ) ;
438
479
}
439
480
}
440
481
} ;
0 commit comments