Axis Tips #4
Tick(ラベル)をの中で改行をおこなう。
青枠はドラッグしてサイズを変更することができます。
license: mit |
<!DOCTYPE html> | |
<html lang="jp"> | |
<head> | |
<style> | |
html, body { | |
margin: 0px; | |
padding: 0px; | |
width: 100%; | |
height: 100%; | |
} | |
#chart { | |
width: 80%; | |
height: 80%; | |
border: 8px dashed gray; | |
border-left: none; | |
border-top:none; | |
cursor:all-scroll; | |
} | |
#chart svg{ | |
width: 100%; | |
height: 100%; | |
cursor: default; | |
} | |
.grid .tick line { | |
stroke-dasharray:1; | |
} | |
.bar { | |
stroke:gray; | |
stroke-width:1; | |
fill:skyblue; | |
} | |
</style> | |
<script src="//unpkg.com/babel-standalone@6.26.0/babel.min.js"></script> | |
<script src="//unpkg.com/d3@4.12.2/build/d3.min.js"></script> | |
</head> | |
<body> | |
<div id="chart"> | |
<svg></svg> | |
</div> | |
<script type="text/babel"> | |
const data = [ | |
{title:"アメリカ合衆国の歴代\nうんちゃら\nかんちゃら比率\nあれこれ", value:80}, | |
{title:"日本の歴代\nうんちゃら\nかんちゃら比率", value:40}, | |
{title:"中国の歴代\nうんちゃら\nかんちゃら比率", value:60}, | |
] | |
const svg = d3.select("#chart").select("svg"); | |
const grid = svg.append("g").classed("grid", true); | |
const plot = svg.append("g").classed("plot", true); | |
const axis = svg.append("g").classed("axis", true); | |
const yScale = d3.scaleBand().domain(data.map(d => d.title)); | |
const xScale = d3.scaleLinear().domain([0, 100]).nice(); | |
render(); | |
function render(){ | |
const m = {top:30, left:110, right:30, bottom:30}; | |
const w = svg.node().clientWidth || svg.node().parentNode.clientWidth; | |
const h = svg.node().clientHeight || svg.node().parentNode.clientHeight; | |
const pw = w - (m.left + m.right); | |
const ph = h - (m.top + m.bottom); | |
yScale.range([0, ph]).paddingInner(0.2); | |
xScale.range([0, pw]); | |
//axis layer | |
axis.attr("transform", `translate(${m.left}, ${m.top})`); | |
//y axis | |
const yAxisUpdate = axis.selectAll(".yAxis").data([null]); | |
const yAxisEnter = yAxisUpdate.enter().append("g").classed("yAxis", true); | |
const yAxis = yAxisUpdate.merge(yAxisEnter).call( d3.axisLeft().scale(yScale).tickSizeOuter(0) ); //ラベルを内向きにする | |
yAxis.selectAll(".tick line").remove(); | |
yAxis.selectAll(".tick text") | |
.html(null) //元のtickラベルを削除 | |
.attr("text-anchor", "start") | |
.attr("dominant-baseline", "middle") | |
.each((text, i, nodes) => { | |
//ラベルに適用するテキストを改行コードを基準に分割 | |
const st = text.split("\n"); | |
//ラベルを右揃えにするために、分割したテキストの中でもっとも長い値を取得「 | |
const maxLength = d3.max(st, d => d.length ); | |
//行間を設定 | |
const lineHight = 0.6; | |
//分割したテキストの数から、Y座標を求めるためのスケールを設定 | |
const ypos = d3.scaleLinear().domain([0, st.length]).range([-st.length * lineHight, st.length * lineHight]); | |
//分割したテキストをtspan要素で括って、もとのノードにアペンドする | |
const tspanUpdate = d3.select(nodes[i]).selectAll("tspan").data(st); | |
const tspanEnter = tspanUpdate.enter().append("tspan"); | |
//tspanに改行のための情報を付加する | |
tspanEnter | |
.attr("dy", "1em") | |
.attr("y", (d,i) => ypos(i) + "em") | |
.attr("dx", "-" + (maxLength + 0.5) + "em") | |
.attr("x", (d,i) => { const l = maxLength - d.length; return l + "em"}) | |
.text(d => d) | |
}); | |
//x axis | |
const xAxisUpdate = axis.selectAll(".xAxis").data([null]); | |
const xAxisEnter = xAxisUpdate.enter().append("g").classed("xAxis", true); | |
const renderAxis = d3.axisBottom().scale(xScale); | |
const xAxis = xAxisUpdate.merge(xAxisEnter).call(renderAxis) | |
.attr("transform", `translate(0, ${ph})`); | |
xAxis.select(".domain").remove(); | |
xAxis.selectAll(".tick line").remove(); | |
//grid layer | |
grid.attr("transform", `translate(${m.left}, ${m.top})`); | |
//x grid | |
const xGridUpdate = grid.selectAll(".xGrid").data([null]); | |
const xGridEnter = xGridUpdate.enter().append("g").classed("xGrid", true); | |
const xGrid = xGridUpdate.merge(xGridEnter) | |
.call( d3.axisBottom().scale(xScale).tickSizeInner(-ph).tickFormat(() => null ) ) | |
.attr("transform", `translate(0, ${ph})`); | |
xGrid.select(".domain").remove(); | |
//plot layer | |
plot.attr("transform", `translate(${m.left}, ${m.top})`); | |
//bar | |
const barsUpdate = plot.selectAll(".bar").data(data); | |
const barsEnter = barsUpdate.enter().append("rect").classed("bar", true); | |
const bars = barsUpdate.merge(barsEnter) | |
.attr("height", yScale.bandwidth()) | |
.attr("width", d => xScale(d.value)) | |
.attr("y", d => yScale(d.title)) | |
.attr("x", 0.5) //storke-width分のズレを修正 | |
; | |
} | |
//divエレメントをドラッグでリサイズできるようにする。 | |
const dispatch = d3.dispatch("resize"); | |
dispatch.on("resize", render); | |
setResizeControler(); | |
function setResizeControler(){ | |
const drag = d3.drag() | |
.on("drag", resized) | |
d3.select("#chart") | |
.call(drag); | |
function resized(e){ | |
const s = d3.event.sourceEvent; | |
const w = (s.pageX < 300) ? 300 : s.pageX; | |
const h = (s.pageY < 200) ? 200 : s.pageY; | |
console.log(h) | |
d3.select(this) | |
.style("width", `${w}px`) | |
.style("height", `${h}px`) | |
.attr("data-test", "test") | |
dispatch.call("resize"); | |
} | |
} | |
</script> | |
</body> | |
</html> |