@@ -177,8 +177,9 @@ drawing.dashStyle = function(dash, lineWidth) {
177177 return dash ;
178178} ;
179179
180- function setFillStyle ( sel , trace , gd ) {
180+ function setFillStyle ( sel , trace , gd , forLegend ) {
181181 var markerPattern = trace . fillpattern ;
182+ var fillgradient = trace . fillgradient ;
182183 var patternShape = markerPattern && drawing . getPatternAttr ( markerPattern . shape , 0 , '' ) ;
183184 if ( patternShape ) {
184185 var patternBGColor = drawing . getPatternAttr ( markerPattern . bgcolor , 0 , null ) ;
@@ -192,6 +193,55 @@ function setFillStyle(sel, trace, gd) {
192193 undefined , markerPattern . fillmode ,
193194 patternBGColor , patternFGColor , patternFGOpacity
194195 ) ;
196+ } else if ( fillgradient && fillgradient . type !== 'none' ) {
197+ var direction = fillgradient . type ;
198+ var gradientID = 'scatterfill-' + trace . uid ;
199+ if ( forLegend ) {
200+ gradientID = 'legendfill-' + trace . uid ;
201+ }
202+
203+ if ( ! forLegend && ( fillgradient . start !== undefined || fillgradient . stop !== undefined ) ) {
204+ var start , stop ;
205+ if ( direction === 'horizontal' ) {
206+ start = {
207+ x : fillgradient . start ,
208+ y : 0 ,
209+ } ;
210+ stop = {
211+ x : fillgradient . stop ,
212+ y : 0 ,
213+ } ;
214+ } else if ( direction === 'vertical' ) {
215+ start = {
216+ x : 0 ,
217+ y : fillgradient . start ,
218+ } ;
219+ stop = {
220+ x : 0 ,
221+ y : fillgradient . stop ,
222+ } ;
223+ }
224+
225+ start . x = trace . _xA . c2p (
226+ ( start . x === undefined ) ? trace . _extremes . x . min [ 0 ] . val : start . x , true
227+ ) ;
228+ start . y = trace . _yA . c2p (
229+ ( start . y === undefined ) ? trace . _extremes . y . min [ 0 ] . val : start . y , true
230+ ) ;
231+
232+ stop . x = trace . _xA . c2p (
233+ ( stop . x === undefined ) ? trace . _extremes . x . max [ 0 ] . val : stop . x , true
234+ ) ;
235+ stop . y = trace . _yA . c2p (
236+ ( stop . y === undefined ) ? trace . _extremes . y . max [ 0 ] . val : stop . y , true
237+ ) ;
238+ sel . call ( gradientWithBounds , gd , gradientID , 'linear' , fillgradient . colorscale , 'fill' , start , stop , true , false ) ;
239+ } else {
240+ if ( direction === 'horizontal' ) {
241+ direction = direction + 'reversed' ;
242+ }
243+ sel . call ( drawing . gradient , gd , gradientID , direction , fillgradient . colorscale , 'fill' ) ;
244+ }
195245 } else if ( trace . fillcolor ) {
196246 sel . call ( Color . fill , trace . fillcolor ) ;
197247 }
@@ -202,17 +252,17 @@ drawing.singleFillStyle = function(sel, gd) {
202252 var node = d3 . select ( sel . node ( ) ) ;
203253 var data = node . data ( ) ;
204254 var trace = ( ( data [ 0 ] || [ ] ) [ 0 ] || { } ) . trace || { } ;
205- setFillStyle ( sel , trace , gd ) ;
255+ setFillStyle ( sel , trace , gd , false ) ;
206256} ;
207257
208- drawing . fillGroupStyle = function ( s , gd ) {
258+ drawing . fillGroupStyle = function ( s , gd , forLegend ) {
209259 s . style ( 'stroke-width' , 0 )
210260 . each ( function ( d ) {
211261 var shape = d3 . select ( this ) ;
212262 // N.B. 'd' won't be a calcdata item when
213263 // fill !== 'none' on a segment-less and marker-less trace
214264 if ( d [ 0 ] . trace ) {
215- setFillStyle ( shape , d [ 0 ] . trace , gd ) ;
265+ setFillStyle ( shape , d [ 0 ] . trace , gd , forLegend ) ;
216266 }
217267 } ) ;
218268} ;
@@ -294,16 +344,14 @@ function makePointPath(symbolNumber, r, t, s) {
294344 return drawing . symbolFuncs [ base ] ( r , t , s ) + ( symbolNumber >= 200 ? DOTPATH : '' ) ;
295345}
296346
297- var HORZGRADIENT = { x1 : 1 , x2 : 0 , y1 : 0 , y2 : 0 } ;
298- var VERTGRADIENT = { x1 : 0 , x2 : 0 , y1 : 1 , y2 : 0 } ;
299347var stopFormatter = numberFormat ( '~f' ) ;
300348var gradientInfo = {
301- radial : { node : 'radialGradient ' } ,
302- radialreversed : { node : 'radialGradient ' , reversed : true } ,
303- horizontal : { node : 'linearGradient ' , attrs : HORZGRADIENT } ,
304- horizontalreversed : { node : 'linearGradient ' , attrs : HORZGRADIENT , reversed : true } ,
305- vertical : { node : 'linearGradient ' , attrs : VERTGRADIENT } ,
306- verticalreversed : { node : 'linearGradient ' , attrs : VERTGRADIENT , reversed : true }
349+ radial : { type : 'radial ' } ,
350+ radialreversed : { type : 'radial ' , reversed : true } ,
351+ horizontal : { type : 'linear ' , start : { x : 1 , y : 0 } , stop : { x : 0 , y : 0 } } ,
352+ horizontalreversed : { type : 'linear ' , start : { x : 1 , y : 0 } , stop : { x : 0 , y : 0 } , reversed : true } ,
353+ vertical : { type : 'linear ' , start : { x : 0 , y : 1 } , stop : { x : 0 , y : 0 } } ,
354+ verticalreversed : { type : 'linear ' , start : { x : 0 , y : 1 } , stop : { x : 0 , y : 0 } , reversed : true }
307355} ;
308356
309357/**
@@ -321,8 +369,57 @@ var gradientInfo = {
321369 * @param {string } prop: the property to apply to, 'fill' or 'stroke'
322370 */
323371drawing . gradient = function ( sel , gd , gradientID , type , colorscale , prop ) {
324- var len = colorscale . length ;
325372 var info = gradientInfo [ type ] ;
373+ return gradientWithBounds (
374+ sel , gd , gradientID , info . type , colorscale , prop , info . start , info . stop , false , info . reversed
375+ ) ;
376+ } ;
377+
378+ /**
379+ * gradient_with_bounds: create and apply a gradient fill for defined start and stop positions
380+ *
381+ * @param {object } sel: d3 selection to apply this gradient to
382+ * You can use `selection.call(Drawing.gradient, ...)`
383+ * @param {DOM element } gd: the graph div `sel` is part of
384+ * @param {string } gradientID: a unique (within this plot) identifier
385+ * for this gradient, so that we don't create unnecessary definitions
386+ * @param {string } type: 'radial' or 'linear'. Radial goes center to edge,
387+ * horizontal goes as defined by start and stop
388+ * @param {array } colorscale: as in attribute values, [[fraction, color], ...]
389+ * @param {string } prop: the property to apply to, 'fill' or 'stroke'
390+ * @param {object } start: start point for linear gradients, { x: number, y: number }.
391+ * Ignored if type is 'radial'.
392+ * @param {object } stop: stop point for linear gradients, { x: number, y: number }.
393+ * Ignored if type is 'radial'.
394+ * @param {boolean } inUserSpace: If true, start and stop give absolute values in the plot.
395+ * If false, start and stop are fractions of the traces extent along each axis.
396+ * @param {boolean } reversed: If true, the gradient is reversed between normal start and stop,
397+ * i.e., the colorscale is applied in order from stop to start for linear, from edge
398+ * to center for radial gradients.
399+ */
400+ function gradientWithBounds ( sel , gd , gradientID , type , colorscale , prop , start , stop , inUserSpace , reversed ) {
401+ var len = colorscale . length ;
402+
403+ var info ;
404+ if ( type === 'linear' ) {
405+ info = {
406+ node : 'linearGradient' ,
407+ attrs : {
408+ x1 : start . x ,
409+ y1 : start . y ,
410+ x2 : stop . x ,
411+ y2 : stop . y ,
412+ gradientUnits : inUserSpace ? 'userSpaceOnUse' : 'objectBoundingBox' ,
413+ } ,
414+ reversed : reversed ,
415+ } ;
416+ } else if ( type === 'radial' ) {
417+ info = {
418+ node : 'radialGradient' ,
419+ reversed : reversed ,
420+ } ;
421+ }
422+
326423 var colorStops = new Array ( len ) ;
327424 for ( var i = 0 ; i < len ; i ++ ) {
328425 if ( info . reversed ) {
@@ -368,7 +465,7 @@ drawing.gradient = function(sel, gd, gradientID, type, colorscale, prop) {
368465 . style ( prop + '-opacity' , null ) ;
369466
370467 sel . classed ( 'gradient_filled' , true ) ;
371- } ;
468+ }
372469
373470/**
374471 * pattern: create and apply a pattern fill
0 commit comments