我有一个用例,我想在视觉的不同部分附加一小段或中段的文字。作为默认行为,这看起来非常难看,因为svg文本只是被添加到了一起。因此,经过一番研究,我发现Mike Bostock创建了一种巧妙的方式来处理svg文本中的较长字符串,可以在here中看到。我试图使此功能适应我的特定视觉效果,但效果并不理想。这是代码段:
var margins = {top:20, left:50, bottom:100, right:20};
var width = 1200;
var height = 500;
var totalWidth = width+margins.left+margins.right;
var totalHeight = height+margins.top+margins.bottom;
var svg = d3.select('body')
.append('svg')
.attr('width', totalWidth)
.attr('height', totalHeight);
var graphGroup = svg.append('g')
.attr('transform', "translate("+margins.left+","+margins.top+")");
var rawData = [
{'date':'Dec-02-2018', 'regulator':'CBIRC', 'penalty':false, 'summary':'Finalized bank wealth management subsidiary rules allow equity investments'},
{'date':'Nov-28-2018', 'regulator':'CSRC', 'penalty':false, 'summary':"Brokerage's retail-targeted, pooled asset management products required to follow mutual fund standards"},
{'date':'Dec-14-2018', 'regulator':'CSRC', 'penalty':false, 'summary':'Regulators issue window guidance to stop FMCs from promoting short-term performance of pension funds'},
{'date':'Dec-19-2018', 'regulator':'CSRC', 'penalty':false, 'summary':'CSRC issues information technology magement rules'},
{'date':'Dec-25-2018', 'regulator':'AMAC', 'penalty':false, 'summary':'AMAC issues guidelines on bond-trading'},
{'date':'Jan-11-2019', 'regulator':'SZSE', 'penalty':false, 'summary':'SZSE revises trading rules for certain ETFs'},
{'date':'Jan-18-2019', 'regulator':'CSRC', 'penalty':false, 'summary':'CSRC issues guidelines on mutual fund investment info credit derivatives, while AMAC issues affiliated valuation guidelines'},
{'date':'Jan-26-2019', 'regulator':'CSRC', 'penalty':false, 'summary':'Yi Huiman appointed as CSRC party secretary and chairman'},
{'date':'Jan-28-2019', 'regulator':'CSRC', 'penalty':false, 'summary':'CSRC publishes draft rules for the new technology innovation board, which will be paired with a registration-based IPO system'},
{'date':'Jan-22-2019', 'regulator':'CSRC', 'penalty':true, 'summary':'Several third-party fund distribution institutions punished by CSRC for incompliant distribution and reporting'},
{'date':'Jan-31-2019', 'regulator':'PBoC', 'penalty':true, 'summary':'ICBC Credit Suisse punished by PBoC for mishandling customer information'}
];
var parseDate = d3.timeParse("%b-%d-%Y");
var formatTime = d3.timeFormat("%b %d, %Y");
var data = rawData.map(function(d) {
return {date:parseDate(d.date), regulator:d.regulator, penalty:d.penalty, summary:d.summary}
});
data.sort(function(x, y){
return d3.ascending(x.date, y.date);
});
//var earliest = d3.min(data.map(d=>d.date));
//var latest = d3.max(data.map(d=>d.date));
var dateMin = d3.min(data, function(d){
return d3.timeDay.offset(d.date, -10);
});
var dateMax = d3.max(data, function(d){
return d3.timeDay.offset(d.date, +10);
});
var timeScale = d3.scaleTime()
.domain([dateMin, dateMax])
.range([0, width]);
var colorMap = {
'CSRC':'#003366',
'CBIRC':'#e4a733',
'AMAC':'#95b3d7',
'SZSE':'#b29866',
'PBoC':'#366092'
};
var defs = svg.append('svg:defs');
var fillURL = "Fills/gray-1-crosshatch.svg";
defs.append("svg:pattern")
.attr("id", "gray_hatch")
.attr("width", 10)
.attr("height", 10)
.attr("patternUnits", "userSpaceOnUse")
.append("svg:image")
.attr("xlink:href", fillURL)
.attr("width", 10)
.attr("height", 10)
.attr("x", 0)
.attr("y", 0);
graphGroup.append('rect')
.attr('width', width)
.attr('height', 80)
.attr('x', 0)
.attr('y', height*.75)
.style('fill', "url(#gray_hatch)");
graphGroup.append('rect')
.attr('width', width)
.attr('height', 20)
.attr('x', 0)
.attr('y', height*.75+30)
.style('fill', "#a6a6a6");
graphGroup.append('rect')
.attr('width',8)
.attr('height',80)
.attr('x',0)
.attr('y',height*.75)
.style('fill', "#a6a6a6");
graphGroup.append('rect')
.attr('width',8)
.attr('height',80)
.attr('x',width)
.attr('y',height*.75)
.style('fill', "#a6a6a6");
graphGroup.selectAll('circle')
.data(data)
.enter()
.append('circle')
.attr('cx', function(d) {return timeScale(d.date)})
.attr('cy', height*.75+40)
.attr('r', 10)
.style('fill', function(d) {return colorMap[d.regulator]});
graphGroup.selectAll('line')
.data(data.filter(function(d) {return d.penalty==false}))
.enter()
.append('line')
.attr('x1', function(d) {return timeScale(d.date)})
.attr('x2', function(d) {return timeScale(d.date)})
.attr('y1', function(d) {return height*.75+40})
.attr('y2', function(d,i) {
if (i%2) {
return 50;
} else {
return height/2;
}
})
.style('stroke', function(d) {return colorMap[d.regulator]})
.style('stroke-width', '2px');
graphGroup.selectAll('.labelRects')
.data(data.filter(function(d) {return d.penalty==false}))
.attr('class', 'labelRects')
.enter()
.append('rect')
.attr('width', 125)
.attr('height', 10)
.attr('x', function(d) {return timeScale(d.date)-125})
.attr('y', function(d,i) {
if (i%2) {
return 50;
} else {
return height/2;
}
})
.style('fill', function(d) { return colorMap[d.regulator]});
graphGroup.selectAll('text')
.data(data.filter(function(d) {return d.penalty==false}))
.enter()
.append('text')
.attr('x', function(d) {return timeScale(d.date)-125})
.attr('y', function(d,i) {
if (i%2) {
return 50-5;
} else {
return height/2-5;
}
})
.text(function(d) {return formatTime(d.date)})
//.attr('text-anchor','middle')
.attr('class', 'date');
function wrap(text, width) {
text.each(function() {
var text = d3.select(this),
words = text.text().split(/\s+/).reverse(),
word,
line = [],
lineNumber = 0,
lineHeight = 1.1, // ems
y = text.attr("y"),
dy = parseFloat(text.attr("dy")),
tspan = text.text(null).append("tspan").attr("x", 0).attr("y", y).attr("dy", dy + "em");
while (word = words.pop()) {
line.push(word);
tspan.text(line.join(" "));
if (tspan.node().getComputedTextLength() > width) {
line.pop();
tspan.text(line.join(" "));
line = [word];
tspan = text.append("tspan").attr("x", 0).attr("y", y).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word);
}
}
});
}
graphGroup.selectAll('.labelText')
.data(data.filter(function(d) {return d.penalty==false}))
.attr('class', 'labelText')
.enter()
.append('text')
.attr('x', function(d) {return timeScale(d.date)-125})
.attr('y', function(d,i) {
if (i%2) {
return 50+20;
} else {
return height/2+20;
}
})
.text(function(d) {return d.summary})
.style('font-size','12px');
d3.selectAll('.labelText')
.call(wrap, 120);
text {
font-family: Tw Cen MT;
}
.date {
font-size: 18px;
paint-order: stroke;
stroke: #fff;
stroke-width: 3px;
stroke-linecap: butt;
stroke-linejoin: miter;
font-weight: 800;
}
<script src="https://d3js.org/d3.v5.min.js"></script>
对我来说,从概念上讲,我做了Bostock在示例中所做的一切(我认为),但是文本似乎并没有正确地通过函数,也没有发生换行-也没有错误。 / p>
是否有可能使Bostock的tspan wrap函数适合一般情况?如果是这样,如果它没有选择文本并调用函数并设置所需的宽度,该怎么办?
进一步的说明:
奖励积分:
答案 0 :(得分:3)
这里的主要问题是您要在输入方法之前设置类:
graphGroup.selectAll('.labelText')
.data(data.filter(function(d) {return d.penalty==false}))
.attr('class', 'labelText')
.enter()
.append('text')
//etc...
应该是:
graphGroup.selectAll('.labelText')
.data(data.filter(function(d) {
return d.penalty == false
}))
.enter()
.append('text')
.attr('class', 'labelText')
//etc...
因此,您的d3.selectAll('.labelText')
为空(即其size()
为零)。
然后,我们必须做一些小的调整才能使用wrap
函数:
text-anchor
设置为end
,并在x
位置删除填充; 在wrap
函数中,获取文本的x
位置...
x = text.attr("x")
并在tspan中使用它们:
.attr("x", x)
这是您更新的代码段:
var margins = {
top: 20,
left: 50,
bottom: 100,
right: 20
};
var width = 1200;
var height = 500;
var totalWidth = width + margins.left + margins.right;
var totalHeight = height + margins.top + margins.bottom;
var svg = d3.select('body')
.append('svg')
.attr('width', totalWidth)
.attr('height', totalHeight);
var graphGroup = svg.append('g')
.attr('transform', "translate(" + margins.left + "," + margins.top + ")");
var rawData = [{
'date': 'Dec-02-2018',
'regulator': 'CBIRC',
'penalty': false,
'summary': 'Finalized bank wealth management subsidiary rules allow equity investments'
},
{
'date': 'Nov-28-2018',
'regulator': 'CSRC',
'penalty': false,
'summary': "Brokerage's retail-targeted, pooled asset management products required to follow mutual fund standards"
},
{
'date': 'Dec-14-2018',
'regulator': 'CSRC',
'penalty': false,
'summary': 'Regulators issue window guidance to stop FMCs from promoting short-term performance of pension funds'
},
{
'date': 'Dec-19-2018',
'regulator': 'CSRC',
'penalty': false,
'summary': 'CSRC issues information technology magement rules'
},
{
'date': 'Dec-25-2018',
'regulator': 'AMAC',
'penalty': false,
'summary': 'AMAC issues guidelines on bond-trading'
},
{
'date': 'Jan-11-2019',
'regulator': 'SZSE',
'penalty': false,
'summary': 'SZSE revises trading rules for certain ETFs'
},
{
'date': 'Jan-18-2019',
'regulator': 'CSRC',
'penalty': false,
'summary': 'CSRC issues guidelines on mutual fund investment info credit derivatives, while AMAC issues affiliated valuation guidelines'
},
{
'date': 'Jan-26-2019',
'regulator': 'CSRC',
'penalty': false,
'summary': 'Yi Huiman appointed as CSRC party secretary and chairman'
},
{
'date': 'Jan-28-2019',
'regulator': 'CSRC',
'penalty': false,
'summary': 'CSRC publishes draft rules for the new technology innovation board, which will be paired with a registration-based IPO system'
},
{
'date': 'Jan-22-2019',
'regulator': 'CSRC',
'penalty': true,
'summary': 'Several third-party fund distribution institutions punished by CSRC for incompliant distribution and reporting'
},
{
'date': 'Jan-31-2019',
'regulator': 'PBoC',
'penalty': true,
'summary': 'ICBC Credit Suisse punished by PBoC for mishandling customer information'
}
];
var parseDate = d3.timeParse("%b-%d-%Y");
var formatTime = d3.timeFormat("%b %d, %Y");
var data = rawData.map(function(d) {
return {
date: parseDate(d.date),
regulator: d.regulator,
penalty: d.penalty,
summary: d.summary
}
});
data.sort(function(x, y) {
return d3.ascending(x.date, y.date);
});
//var earliest = d3.min(data.map(d=>d.date));
//var latest = d3.max(data.map(d=>d.date));
var dateMin = d3.min(data, function(d) {
return d3.timeDay.offset(d.date, -10);
});
var dateMax = d3.max(data, function(d) {
return d3.timeDay.offset(d.date, +10);
});
var timeScale = d3.scaleTime()
.domain([dateMin, dateMax])
.range([0, width]);
var colorMap = {
'CSRC': '#003366',
'CBIRC': '#e4a733',
'AMAC': '#95b3d7',
'SZSE': '#b29866',
'PBoC': '#366092'
};
var defs = svg.append('svg:defs');
var fillURL = "Fills/gray-1-crosshatch.svg";
defs.append("svg:pattern")
.attr("id", "gray_hatch")
.attr("width", 10)
.attr("height", 10)
.attr("patternUnits", "userSpaceOnUse")
.append("svg:image")
.attr("xlink:href", fillURL)
.attr("width", 10)
.attr("height", 10)
.attr("x", 0)
.attr("y", 0);
graphGroup.append('rect')
.attr('width', width)
.attr('height', 80)
.attr('x', 0)
.attr('y', height * .75)
.style('fill', "url(#gray_hatch)");
graphGroup.append('rect')
.attr('width', width)
.attr('height', 20)
.attr('x', 0)
.attr('y', height * .75 + 30)
.style('fill', "#a6a6a6");
graphGroup.append('rect')
.attr('width', 8)
.attr('height', 80)
.attr('x', 0)
.attr('y', height * .75)
.style('fill', "#a6a6a6");
graphGroup.append('rect')
.attr('width', 8)
.attr('height', 80)
.attr('x', width)
.attr('y', height * .75)
.style('fill', "#a6a6a6");
graphGroup.selectAll('circle')
.data(data)
.enter()
.append('circle')
.attr('cx', function(d) {
return timeScale(d.date)
})
.attr('cy', height * .75 + 40)
.attr('r', 10)
.style('fill', function(d) {
return colorMap[d.regulator]
});
graphGroup.selectAll('line')
.data(data.filter(function(d) {
return d.penalty == false
}))
.enter()
.append('line')
.attr('x1', function(d) {
return timeScale(d.date)
})
.attr('x2', function(d) {
return timeScale(d.date)
})
.attr('y1', function(d) {
return height * .75 + 40
})
.attr('y2', function(d, i) {
if (i % 2) {
return 50;
} else {
return height / 2;
}
})
.style('stroke', function(d) {
return colorMap[d.regulator]
})
.style('stroke-width', '2px');
graphGroup.selectAll('.labelRects')
.data(data.filter(function(d) {
return d.penalty == false
}))
.attr('class', 'labelRects')
.enter()
.append('rect')
.attr('width', 125)
.attr('height', 10)
.attr('x', function(d) {
return timeScale(d.date) - 125
})
.attr('y', function(d, i) {
if (i % 2) {
return 50;
} else {
return height / 2;
}
})
.style('fill', function(d) {
return colorMap[d.regulator]
});
graphGroup.selectAll('text')
.data(data.filter(function(d) {
return d.penalty == false
}))
.enter()
.append('text')
.attr('x', function(d) {
return timeScale(d.date) - 125
})
.attr('y', function(d, i) {
if (i % 2) {
return 50 - 5;
} else {
return height / 2 - 5;
}
})
.text(function(d) {
return formatTime(d.date)
})
//.attr('text-anchor','middle')
.attr('class', 'date');
function wrap(text, width) {
text.each(function() {
var text = d3.select(this),
words = text.text().split(/\s+/).reverse(),
word,
line = [],
lineNumber = 0,
lineHeight = 1.1, // ems
y = text.attr("y"),
x = text.attr("x"),
dy = parseFloat(text.attr("dy")),
tspan = text.text(null).append("tspan").attr("x", x).attr("y", y).attr("dy", dy + "em");
while (word = words.pop()) {
line.push(word);
tspan.text(line.join(" "));
if (tspan.node().getComputedTextLength() > width) {
line.pop();
tspan.text(line.join(" "));
line = [word];
tspan = text.append("tspan").attr("y", y).attr("x", x).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word);
}
}
});
}
graphGroup.selectAll('.labelText')
.data(data.filter(function(d) {
return d.penalty == false
}))
.enter()
.append('text')
.attr('class', 'labelText')
.attr('x', function(d) {
return timeScale(d.date) - 4
})
.attr('y', function(d, i) {
if (i % 2) {
return 50 + 20;
} else {
return height / 2 + 20;
}
})
.attr("dy", 0)
.attr("text-anchor", "end")
.text(function(d) {
return d.summary
})
.style('font-size', '12px');
d3.selectAll('.labelText')
.call(wrap, 120);
text {
font-family: Tw Cen MT;
}
.date {
font-size: 18px;
paint-order: stroke;
stroke: #fff;
stroke-width: 3px;
stroke-linecap: butt;
stroke-linejoin: miter;
font-weight: 800;
}
<script src="https://d3js.org/d3.v5.min.js"></script>