Skip to content

Commit 97b9378

Browse files
committed
fixup sankey text
1 parent d8863d8 commit 97b9378

File tree

4 files changed

+62
-97
lines changed

4 files changed

+62
-97
lines changed

src/traces/sankey/constants.js

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,6 @@ module.exports = {
1616
sankeyNodeSet: 'sankey-node-set',
1717
sankeyNode: 'sankey-node',
1818
nodeRect: 'node-rect',
19-
nodeCapture: 'node-capture',
20-
nodeCentered: 'node-entered',
21-
nodeLabelGuide: 'node-label-guide',
22-
nodeLabel: 'node-label',
23-
nodeLabelTextPath: 'node-label-text-path'
19+
nodeLabel: 'node-label'
2420
}
2521
};

src/traces/sankey/render.js

Lines changed: 51 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,31 @@
11
'use strict';
22

3-
var c = require('./constants');
3+
var d3Force = require('d3-force');
4+
var interpolateNumber = require('d3-interpolate').interpolateNumber;
45
var d3 = require('@plotly/d3');
6+
var d3Sankey = require('@plotly/d3-sankey');
7+
var d3SankeyCircular = require('@plotly/d3-sankey-circular');
8+
9+
var c = require('./constants');
510
var tinycolor = require('tinycolor2');
611
var Color = require('../../components/color');
712
var Drawing = require('../../components/drawing');
8-
var d3Sankey = require('@plotly/d3-sankey');
9-
var d3SankeyCircular = require('@plotly/d3-sankey-circular');
10-
var d3Force = require('d3-force');
1113
var Lib = require('../../lib');
1214
var strTranslate = Lib.strTranslate;
15+
var strRotate = Lib.strRotate;
1316
var gup = require('../../lib/gup');
1417
var keyFun = gup.keyFun;
1518
var repeat = gup.repeat;
1619
var unwrap = gup.unwrap;
17-
var interpolateNumber = require('d3-interpolate').interpolateNumber;
1820
var svgTextUtils = require('../../lib/svg_text_utils');
1921

2022
var Registry = require('../../registry');
2123

24+
var alignmentConstants = require('../../constants/alignment');
25+
var CAP_SHIFT = alignmentConstants.CAP_SHIFT;
26+
var LINE_SPACING = alignmentConstants.LINE_SPACING;
27+
var TEXTPAD = 3;
28+
2229
// view models
2330

2431
function sankeyModel(layout, d, traceIndex) {
@@ -547,22 +554,6 @@ function sankeyTransform(d) {
547554
return offset + (d.horizontal ? 'matrix(1 0 0 1 0 0)' : 'matrix(0 1 1 0 0 0)');
548555
}
549556

550-
function nodeCentering(d) {
551-
return strTranslate(d.horizontal ? 0 : d.labelY, d.horizontal ? d.labelY : 0);
552-
}
553-
554-
function textGuidePath(d) {
555-
return d3.svg.line()([
556-
[d.horizontal ? (d.left ? -d.sizeAcross : d.visibleWidth + c.nodeTextOffsetHorizontal) : c.nodeTextOffsetHorizontal, 0],
557-
[d.horizontal ? (d.left ? - c.nodeTextOffsetHorizontal : d.sizeAcross) : d.visibleHeight - c.nodeTextOffsetHorizontal, 0]
558-
]);
559-
}
560-
561-
function sankeyInverseTransform(d) {return d.horizontal ? 'matrix(1 0 0 1 0 0)' : 'matrix(0 1 1 0 0 0)';}
562-
function textFlip(d) {return d.horizontal ? 'scale(1 1)' : 'scale(-1 1)';}
563-
function nodeTextColor(d) {return d.darkBackground && !d.horizontal ? 'rgb(255,255,255)' : 'rgb(0,0,0)';}
564-
function nodeTextOffset(d) {return d.horizontal && d.left ? '100%' : '0%';}
565-
566557
// event handling
567558

568559
function attachPointerEvents(selection, sankey, eventSet) {
@@ -970,88 +961,57 @@ module.exports = function(gd, svg, calcData, layout, callbacks) {
970961
.ease(c.ease).duration(c.duration)
971962
.call(sizeNode);
972963

973-
var nodeCapture = sankeyNode.selectAll('.' + c.cn.nodeCapture)
974-
.data(repeat);
975-
976-
nodeCapture.enter()
977-
.append('rect')
978-
.classed(c.cn.nodeCapture, true)
979-
.style('fill-opacity', 0);
980-
981-
nodeCapture
982-
.attr('x', function(d) {return d.zoneX;})
983-
.attr('y', function(d) {return d.zoneY;})
984-
.attr('width', function(d) {return d.zoneWidth;})
985-
.attr('height', function(d) {return d.zoneHeight;});
986-
987-
var nodeCentered = sankeyNode.selectAll('.' + c.cn.nodeCentered)
988-
.data(repeat);
989-
990-
nodeCentered.enter()
991-
.append('g')
992-
.classed(c.cn.nodeCentered, true)
993-
.attr('transform', nodeCentering);
994-
995-
nodeCentered
996-
.transition()
997-
.ease(c.ease).duration(c.duration)
998-
.attr('transform', nodeCentering);
999-
1000-
var nodeLabelGuide = nodeCentered.selectAll('.' + c.cn.nodeLabelGuide)
1001-
.data(repeat);
1002-
1003-
nodeLabelGuide.enter()
1004-
.append('path')
1005-
.classed(c.cn.nodeLabelGuide, true)
1006-
.attr('id', function(d) {return d.uniqueNodeLabelPathId;})
1007-
.attr('d', textGuidePath)
1008-
.attr('transform', sankeyInverseTransform);
1009-
1010-
nodeLabelGuide
1011-
.transition()
1012-
.ease(c.ease).duration(c.duration)
1013-
.attr('d', textGuidePath)
1014-
.attr('transform', sankeyInverseTransform);
1015-
1016-
var nodeLabel = nodeCentered.selectAll('.' + c.cn.nodeLabel)
964+
var nodeLabel = sankeyNode.selectAll('.' + c.cn.nodeLabel)
1017965
.data(repeat);
1018966

1019967
nodeLabel.enter()
1020968
.append('text')
1021969
.classed(c.cn.nodeLabel, true)
1022-
.attr('transform', textFlip)
1023-
.style('cursor', 'default')
1024-
.style('fill', 'black');
970+
.style('cursor', 'default');
1025971

1026972
nodeLabel
973+
.text(function(d) { return d.node.label; })
974+
.each(function(d) {
975+
var e = d3.select(this);
976+
Drawing.font(e, d.textFont);
977+
svgTextUtils.convertToTspans(e, gd);
978+
})
1027979
.style('text-shadow', function(d) {
1028980
return d.horizontal ? svgTextUtils.makeTextShadow(gd._fullLayout.paper_bgcolor) : 'none';
1029981
})
1030-
.each(function(d) {Drawing.font(nodeLabel, d.textFont);});
1031-
1032-
nodeLabel
1033-
.transition()
1034-
.ease(c.ease).duration(c.duration)
1035-
.attr('transform', textFlip);
1036-
1037-
var nodeLabelTextPath = nodeLabel.selectAll('.' + c.cn.nodeLabelTextPath)
1038-
.data(repeat);
982+
.attr('text-anchor', function(d) {
983+
return (d.horizontal && d.left) ? 'end' : 'start';
984+
})
985+
.attr('transform', function(d) {
986+
var e = d3.select(this);
987+
// how much to shift a multi-line label to center it vertically.
988+
var nLines = svgTextUtils.lineCount(e);
989+
var blockHeight = d.textFont.size * (
990+
nLines === 1 ? -CAP_SHIFT : (nLines - 1) * LINE_SPACING
991+
);
992+
993+
// TODO: handle MathJax
994+
995+
var posX;
996+
var posY;
997+
if(d.horizontal) {
998+
posX = d.left ? 0 : d.visibleWidth;
999+
posX += (d.left ? -1 : 1) * (d.nodeLineWidth / 2 + TEXTPAD);
1000+
1001+
posY = (d.visibleHeight - blockHeight) / 2;
1002+
} else {
1003+
posY = TEXTPAD;
1004+
posX = (d.visibleWidth - blockHeight) / 2;
1005+
}
10391006

1040-
nodeLabelTextPath.enter()
1041-
.append('textPath')
1042-
.classed(c.cn.nodeLabelTextPath, true)
1043-
.attr('alignment-baseline', 'middle')
1044-
.attr('xlink:href', function(d) {return '#' + d.uniqueNodeLabelPathId;})
1045-
.attr('startOffset', nodeTextOffset)
1046-
.style('fill', nodeTextColor);
1007+
var flipText = d.horizontal ? '' : (
1008+
'scale(-1,1)' + strRotate(90)
1009+
);
10471010

1048-
nodeLabelTextPath
1049-
.text(function(d) {return d.horizontal || d.node.dy > 5 ? d.node.label : '';})
1050-
.attr('text-anchor', function(d) {return d.horizontal && d.left ? 'end' : 'start';});
1011+
return strTranslate(posX, posY) + flipText;
1012+
});
10511013

1052-
nodeLabelTextPath
1014+
nodeLabel
10531015
.transition()
1054-
.ease(c.ease).duration(c.duration)
1055-
.attr('startOffset', nodeTextOffset)
1056-
.style('fill', nodeTextColor);
1016+
.ease(c.ease).duration(c.duration);
10571017
};

test/image/mocks/sankey_energy_dark.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,7 @@
366366
"height": 1000,
367367
"paper_bgcolor": "rgba(0,0,0,1)",
368368
"font": {
369+
"color": "white",
369370
"size": 10
370371
},
371372
"updatemenus": [

test/image/mocks/sankey_subplots.json

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,23 @@
11
{
22
"data": [{
3+
"textfont": {
4+
"color": "red"
5+
},
36
"domain": {
47
"x": [0, 0.45]
58
},
69
"type": "sankey",
710
"orientation": "h",
811
"node": {
912
"line": {
13+
"width": 8,
1014
"color": "black"
1115
},
12-
"label": ["el1", "el2", "el3"]
16+
"label": [
17+
"<i>Rien ne se perd,<br>rien ne se crée,<br>tout se <b>transforme</b>.</i><sub>",
18+
"el2",
19+
"<b>e<sup>iπ</sup> = cos i + i sin π</b>"
20+
]
1321
},
1422
"link": {
1523
"source": [0, 2],

0 commit comments

Comments
 (0)