Skip to content

Commit e3af5eb

Browse files
committed
link gradient to the point, not the color combo
1 parent d1c6b51 commit e3af5eb

File tree

3 files changed

+81
-44
lines changed

3 files changed

+81
-44
lines changed

src/components/drawing/index.js

+31-15
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ drawing.symbolNumber = function(v) {
203203
return Math.floor(Math.max(v, 0));
204204
};
205205

206-
function singlePointStyle(d, sel, trace, markerScale, lineScale, marker, markerLine, gd) {
206+
function singlePointStyle(d, sel, trace, markerScale, lineScale, marker, markerLine, gd, i) {
207207
// only scatter & box plots get marker path and opacity
208208
// bars, histograms don't
209209
if(Registry.traceIs(trace, 'symbols')) {
@@ -238,6 +238,8 @@ function singlePointStyle(d, sel, trace, markerScale, lineScale, marker, markerL
238238
});
239239
}
240240

241+
var perPointGradient = false;
242+
241243
// 'so' is suspected outliers, for box plots
242244
var fillColor,
243245
lineColor,
@@ -257,8 +259,12 @@ function singlePointStyle(d, sel, trace, markerScale, lineScale, marker, markerL
257259
else if(Array.isArray(markerLine.color)) lineColor = Color.defaultLine;
258260
else lineColor = markerLine.color;
259261

262+
if(Array.isArray(marker.color)) {
263+
fillColor = Color.defaultLine;
264+
perPointGradient = true;
265+
}
266+
260267
if('mc' in d) fillColor = d.mcc = markerScale(d.mc);
261-
else if(Array.isArray(marker.color)) fillColor = Color.defaultLine;
262268
else fillColor = marker.color || 'rgba(0,0,0,0)';
263269
}
264270

@@ -275,10 +281,20 @@ function singlePointStyle(d, sel, trace, markerScale, lineScale, marker, markerL
275281
sel.style('stroke-width', lineWidth + 'px');
276282

277283
var markerGradient = marker.gradient;
278-
var gradientColor = markerGradient && (d.mgc || markerGradient.color);
279-
var gradientType = markerGradient && (d.mgt || markerGradient.type);
284+
285+
var gradientType = d.mgt;
286+
if(gradientType) perPointGradient = true;
287+
else gradientType = markerGradient && markerGradient.type;
288+
280289
if(gradientType && gradientType !== 'none') {
281-
sel.call(drawing.gradient, gd, gradientType, fillColor, gradientColor);
290+
var gradientColor = d.mgc;
291+
if(gradientColor) perPointGradient = true;
292+
else gradientColor = markerGradient.color;
293+
294+
var gradientID = 'g' + gd._fullLayout._uid + '-' + trace.uid;
295+
if(perPointGradient) gradientID += '-' + i;
296+
297+
sel.call(drawing.gradient, gd, gradientID, gradientType, fillColor, gradientColor);
282298
}
283299
else {
284300
sel.call(Color.fill, fillColor);
@@ -293,12 +309,12 @@ function singlePointStyle(d, sel, trace, markerScale, lineScale, marker, markerL
293309
var HORZGRADIENT = {x1: 1, x2: 0, y1: 0, y2: 0};
294310
var VERTGRADIENT = {x1: 0, x2: 0, y1: 1, y2: 0};
295311

296-
drawing.gradient = function(sel, gd, type, color1, color2) {
297-
var fullLayout = gd._fullLayout;
298-
var gradientID = (
299-
type + fullLayout._uid + '-' + color1 + '-' + color2
300-
).replace(/[^\w\-]+/g, '_');
301-
var gradient = fullLayout._defs.select('.gradients').selectAll('#' + gradientID).data([0]);
312+
drawing.gradient = function(sel, gd, gradientID, type, color1, color2) {
313+
var gradient = gd._fullLayout._defs.select('.gradients')
314+
.selectAll('#' + gradientID)
315+
.data([type + color1 + color2], Lib.identity);
316+
317+
gradient.exit().remove();
302318

303319
gradient.enter()
304320
.append(type === 'radial' ? 'radialGradient' : 'linearGradient')
@@ -345,10 +361,10 @@ drawing.initGradients = function(gd) {
345361
gradientsGroup.selectAll('linearGradient,radialGradient').remove();
346362
};
347363

348-
drawing.singlePointStyle = function(d, sel, trace, markerScale, lineScale, gd) {
364+
drawing.singlePointStyle = function(d, sel, trace, markerScale, lineScale, gd, i) {
349365
var marker = trace.marker;
350366

351-
singlePointStyle(d, sel, trace, markerScale, lineScale, marker, marker.line, gd);
367+
singlePointStyle(d, sel, trace, markerScale, lineScale, marker, marker.line, gd, i);
352368

353369
};
354370

@@ -362,8 +378,8 @@ drawing.pointStyle = function(s, trace) {
362378
var lineScale = drawing.tryColorscale(marker, 'line');
363379
var gd = Lib.getPlotDiv(s.node());
364380

365-
s.each(function(d) {
366-
drawing.singlePointStyle(d, d3.select(this), trace, markerScale, lineScale, gd);
381+
s.each(function(d, i) {
382+
drawing.singlePointStyle(d, d3.select(this), trace, markerScale, lineScale, gd, i);
367383
});
368384
};
369385

src/traces/scatter/plot.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -430,11 +430,11 @@ function plotOne(gd, idx, plotinfo, cdscatter, cdscatterAll, element, transition
430430
var markerScale = showMarkers && Drawing.tryColorscale(trace.marker, '');
431431
var lineScale = showMarkers && Drawing.tryColorscale(trace.marker, 'line');
432432

433-
join.each(function(d) {
433+
join.each(function(d, i) {
434434
var el = d3.select(this);
435435
var sel = transition(el);
436436
Drawing.translatePoint(d, sel, xa, ya);
437-
Drawing.singlePointStyle(d, sel, trace, markerScale, lineScale, gd);
437+
Drawing.singlePointStyle(d, sel, trace, markerScale, lineScale, gd, i);
438438

439439
if(trace.customdata) {
440440
el.classed('plotly-customdata', d.data !== null && d.data !== undefined);

test/jasmine/tests/drawing_test.js

+48-27
Original file line numberDiff line numberDiff line change
@@ -325,20 +325,31 @@ describe('gradients', function() {
325325

326326
afterEach(destroyGraphDiv);
327327

328-
function checkGradientIds(uid, specs) {
329-
var sortedExpected = specs.map(function(spec) {
330-
return (spec[0] + uid + '-' + spec[1] + '-' + spec[2]).replace(/[^\w\-]+/g, '_');
331-
}).sort();
328+
function checkGradientIds(ids, types, c1, c2) {
329+
var expected = ids.map(function(id) {
330+
return 'g' + gd._fullLayout._uid + '-' + gd._fullData[0].uid + id;
331+
});
332332

333333
var gids = [];
334+
var typesOut = [];
335+
var c1Out = [];
336+
var c2Out = [];
334337
var gradients = d3.select(gd).selectAll('radialGradient,linearGradient');
335-
gradients.each(function() { gids.push(this.id); });
338+
gradients.each(function() {
339+
gids.push(this.id);
340+
typesOut.push(this.nodeName.replace('Gradient', ''));
341+
c1Out.push(d3.select(this).select('stop[offset="100%"]').attr('stop-color'));
342+
c2Out.push(d3.select(this).select('stop[offset="0%"]').attr('stop-color'));
343+
});
336344
gids.sort();
337345

338-
expect(gids.length).toBe(sortedExpected.length);
346+
expect(gids.length).toBe(expected.length);
339347

340-
for(var i = 0; i < Math.min(gids.length, sortedExpected.length); i++) {
341-
expect(gids[i]).toBe(sortedExpected[i]);
348+
for(var i = 0; i < Math.min(gids.length, expected.length); i++) {
349+
expect(gids[i]).toBe(expected[i]);
350+
expect(typesOut[i]).toBe(types[i]);
351+
expect(c1Out[i]).toBe(c1[i]);
352+
expect(c2Out[i]).toBe(c2[i]);
342353
}
343354
}
344355

@@ -355,41 +366,51 @@ describe('gradients', function() {
355366
}
356367
}])
357368
.then(function() {
358-
checkGradientIds(gd._fullLayout._uid, [
359-
['radial', '#123', '#fff'],
360-
['radial', '#123', '#eee'],
361-
['radial', '#123', '#ddd']
362-
]);
369+
checkGradientIds(
370+
['-0', '-1', '-2'],
371+
['radial', 'radial', 'radial'],
372+
['rgb(17, 34, 51)', 'rgb(17, 34, 51)', 'rgb(17, 34, 51)'],
373+
['rgb(255, 255, 255)', 'rgb(238, 238, 238)', 'rgb(221, 221, 221)']);
363374

364375
return Plotly.restyle(gd, {'marker.color': '#456'});
365376
})
366377
.then(function() {
367378
// simple scalar restyle doesn't trigger a full replot, so
368379
// doesn't clear the old gradients
369-
checkGradientIds(gd._fullLayout._uid, [
370-
['radial', '#123', '#fff'],
371-
['radial', '#123', '#eee'],
372-
['radial', '#123', '#ddd'],
373-
['radial', '#456', '#fff'],
374-
['radial', '#456', '#eee'],
375-
['radial', '#456', '#ddd']
376-
]);
380+
checkGradientIds(
381+
['-0', '-1', '-2'],
382+
['radial', 'radial', 'radial'],
383+
['rgb(68, 85, 102)', 'rgb(68, 85, 102)', 'rgb(68, 85, 102)'],
384+
['rgb(255, 255, 255)', 'rgb(238, 238, 238)', 'rgb(221, 221, 221)']);
377385

378386
return Plotly.restyle(gd, {'marker.gradient.type': [['horizontal', 'vertical', 'radial']]});
379387
})
380388
.then(function() {
381389
// array restyle does replot
382-
checkGradientIds(gd._fullLayout._uid, [
383-
['horizontal', '#456', '#fff'],
384-
['vertical', '#456', '#eee'],
385-
['radial', '#456', '#ddd']
386-
]);
390+
checkGradientIds(
391+
['-0', '-1', '-2'],
392+
['linear', 'linear', 'radial'],
393+
['rgb(68, 85, 102)', 'rgb(68, 85, 102)', 'rgb(68, 85, 102)'],
394+
['rgb(255, 255, 255)', 'rgb(238, 238, 238)', 'rgb(221, 221, 221)']);
395+
396+
return Plotly.restyle(gd, {
397+
'marker.gradient.type': 'vertical',
398+
'marker.gradient.color': '#abc'
399+
});
400+
})
401+
.then(function() {
402+
// down to a single gradient because they're all the same
403+
checkGradientIds(
404+
[''],
405+
['linear'],
406+
['rgb(68, 85, 102)'],
407+
['rgb(170, 187, 204)']);
387408

388409
return Plotly.restyle(gd, {'mode': 'lines'});
389410
})
390411
.then(function() {
391412
// full replot and no resulting markers at all -> no gradients
392-
checkGradientIds(gd._fullLayout._uid, []);
413+
checkGradientIds([], [], [], []);
393414
})
394415
.catch(fail)
395416
.then(done);

0 commit comments

Comments
 (0)