@@ -266,43 +266,76 @@ function makePointPath(symbolNumber, r) {
266266
267267var HORZGRADIENT = { x1 : 1 , x2 : 0 , y1 : 0 , y2 : 0 } ;
268268var 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 ;
269307
270- drawing . gradient = function ( sel , gd , gradientID , type , color1 , color2 ) {
271308 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 ) ;
274311
275312 gradient . exit ( ) . remove ( ) ;
276313
277314 gradient . enter ( )
278- . append ( type === 'radial' ? 'radialGradient' : 'linearGradient' )
315+ . append ( info . node )
279316 . each ( function ( ) {
280317 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+ } ) ;
299334 } ) ;
300335 } ) ;
301336
302- sel . style ( {
303- fill : 'url(#' + gradientID + ')' ,
304- 'fill-opacity' : null
305- } ) ;
337+ sel . style ( prop , 'url(#' + fullID + ')' )
338+ . style ( prop + '-opacity' , null ) ;
306339} ;
307340
308341/*
@@ -420,21 +453,29 @@ drawing.singlePointStyle = function(d, sel, trace, fns, gd) {
420453 if ( gradientType ) perPointGradient = true ;
421454 else gradientType = markerGradient && markerGradient . type ;
422455
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+
423463 if ( gradientType && gradientType !== 'none' ) {
424464 var gradientColor = d . mgc ;
425465 if ( gradientColor ) perPointGradient = true ;
426466 else gradientColor = markerGradient . color ;
427467
428- var gradientID = 'g' + gd . _fullLayout . _uid + '-' + trace . uid ;
468+ var gradientID = trace . uid ;
429469 if ( perPointGradient ) gradientID += '-' + d . i ;
430470
431- sel . call ( drawing . gradient , gd , gradientID , gradientType , fillColor , gradientColor ) ;
471+ drawing . gradient ( sel , gd , gradientID , gradientType ,
472+ [ [ 0 , gradientColor ] , [ 1 , fillColor ] ] , 'fill' ) ;
432473 } else {
433- sel . call ( Color . fill , fillColor ) ;
474+ Color . fill ( sel , fillColor ) ;
434475 }
435476
436477 if ( lineWidth ) {
437- sel . call ( Color . stroke , lineColor ) ;
478+ Color . stroke ( sel , lineColor ) ;
438479 }
439480 }
440481} ;
0 commit comments