chart2 = {
const width = 1800;
const height = 2060;
const format = d3.format(",.0f");
const svg = d3
.create("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", [0, 0, width, height])
.attr(
"style",
"max-width: 100%; height: auto; font: 15px 'Noto Sans JP', sans-serif; font-weight: bold;"
);
const marginLeft = 250;
const marginRight = 180;
const effectiveWidth = width - marginLeft - marginRight;
const sankey = d3
.sankey()
.nodeId((d) => d.name)
.nodeAlign(d3[nodeAlign])
.nodeWidth(15)
.nodePadding(10)
.extent([
[1 + marginLeft, 5],
[effectiveWidth - 1, height - 5]
]);
const { nodes, links } = sankey({
nodes: data2.nodes.map((d) => {
// valueを追加
d.value = data2.links
.filter((link) => link.source === d.name)
.reduce((acc, curr) => acc + curr.value, 0);
return Object.assign({}, d);
}),
links: data2.links.map((d) => Object.assign({}, d))
});
const color = d3.scaleOrdinal(d3.schemeCategory10); // カテゴリーカラースケール
const rect = svg
.append("g") // ノードを含むグループを追加
.attr("stroke", "#000") // ストロークカラー
.selectAll()
.data(nodes)
.join("rect") // ノードを描画
.attr("x", (d) => d.x0) // x座標の開始位置
.attr("y", (d) => d.y0) // y座標の開始位置
.attr("height", (d) => d.y1 - d.y0) // 高さ
.attr("width", (d) => d.x1 - d.x0) // 幅
.attr("fill", (d) => color(d.category)); // ノードのカラー
// ノードにタイトルを追加
rect
// .attr("stroke", "white") // ストロークカラー
.attr("stroke-width", 0) // ストロークの太さを0に設定
.append("title"); // タイトルを追加
// .text((d) => `${d.name}\n${format(d.value)} 回`); // タイトルのテキスト
// .text((d) => `${d.category}\n${format(d.value)} 回`); // タイトルのテキスト
const tooltip = d3
.select("body")
.append("div")
.attr("class", "tooltip")
.style("position", "absolute")
.style("z-index", "10")
.style("visibility", "hidden")
.style("background", "white")
.style("border", "solid 1px #ccc")
.style("border-radius", "5px")
.style("padding", "10px")
.style("opacity", 0.8)
.style("font", "16px 'Noto Sans JP', sans-serif");
const link = svg
.append("g") // リンクを含むグループを追加
.attr("fill", "none")
.attr("stroke-opacity", 0.5)
.selectAll()
.data(links)
.join("g") // リンクを描画
.style("mix-blend-mode", "multiply")
.on("mouseover", function (event, d) {
link.style("stroke-opacity", function (linkData) {
return linkData === d ? 0.9 : 0.2;
});
tooltip
.html(`${d.source.name} → ${d.target.name}<br/>${format(d.value)} 回`)
.style("visibility", "visible");
})
.on("mousemove", function (event) {
tooltip
.style("top", event.pageY - tooltip.node().offsetHeight - 10 + "px")
.style("left", event.pageX - tooltip.node().offsetWidth / 2 + "px");
})
.on("mouseout", function () {
tooltip.style("visibility", "hidden");
link.style("stroke-opacity", 0.5);
});
const targetTotals = {};
links.forEach((link) => {
if (!targetTotals[link.target.name]) {
targetTotals[link.target.name] = 0;
}
targetTotals[link.target.name] += link.value;
});
if (linkColor === "source-target") {
const gradient = link
.append("linearGradient") // 線形グラデーションを追加
.attr("id", (d) => (d.uid = DOM.uid("link")).id)
.attr("gradientUnits", "userSpaceOnUse")
.attr("x1", (d) => d.source.x1)
.attr("x2", (d) => d.target.x0);
gradient
.append("stop") // グラデーションストップを追加
.attr("offset", "0%")
.attr("stop-color", (d) => color(d.source.category));
gradient
.append("stop") // グラデーションストップを追加
.attr("offset", "100%")
.attr("stop-color", (d) => color(d.target.category));
}
link
.append("path") // リンクのパスを追加
.attr("d", d3.sankeyLinkHorizontal())
.attr("stroke", (d) =>
linkColor === "none"
? "#aaa"
: linkColor === "path"
? color(d.names.join(" → "))
: linkColor === "input"
? color(d.source.category)
: linkColor === "output"
? color(d.target.category)
: d.uid
)
.attr("stroke-width", (d) => Math.max(1, d.width));
// // ノードにラベルを追加
// svg
// .append("g") // ラベルを含むグループを追加
// .selectAll("text")
// .data2(nodes)
// .join("text") // ラベルを描画
// .attr("x", (d) => (d.x0 < width / 2 ? d.x1 - 25 : d.x0 + 25))
// .attr("y", (d) => (d.y1 + d.y0) / 2)
// .attr("dy", "0.35em")
// .attr("text-anchor", (d) => (d.x0 < width / 2 ? "end" : "start"))
// .attr("fill", (d) => color(d.category))
// .style("font-size", (d) => `${Math.sqrt(d.y1 - d.y0) * 3.5 + 5}px`)
// .text((d) => {
// if (targetTotals[d.category] && d.x0 >= width / 2) {
// return `${d.category} ${format(targetTotals[d.category])} 回`;
// }
// return d.category;
// });
// ステップ2: ノードに対するラベル表示の部分を変更;
svg
.append("g") // ラベルを含むグループを追加
.selectAll("text")
.data(nodes)
.join("text") // ラベルを描画
.attr("x", (d) => (d.x0 < width / 2 ? d.x1 - 25 : d.x0 + 25))
.attr("y", (d) => (d.y1 + d.y0) / 2)
.attr("dy", "0.35em")
.attr("text-anchor", (d) => (d.x0 < width / 2 ? "end" : "start"))
.attr("fill", (d) => color(d.category))
// .style("font-size", (d) => `${Math.sqrt(d.y1 - d.y0) * 3.5 + 5}px`)
// .text((d) => {
// if (targetTotals[d.category] && d.x0 >= width / 2) {
// return `${d.category} ${format(targetTotals[d.category])}`;
// }
// // ノードのラベルを表示
// return `${d.category} ${format(d.value)}`;
// });
.text(function (d) {
if (targetTotals[d.category] && d.x0 >= width / 2) {
return `${d.category} ${format(targetTotals[d.category])}`;
}
// ノードのラベルを表示
return `${d.category} ${format(d.value)}`;
})
// .each(function (d) {
// var text = d3.select(this);
// var lines = text.text().split(" ");
// text.text("");
// for (var i = 0; i < lines.length; i++) {
// text
// .append("tspan")
// .attr("x", text.attr("x"))
// .attr("dy", i ? "1.2em" : 0)
// .text(lines[i]);
// }
// });
.each(function (d) {
var text = d3.select(this);
var lines = text.text().split(" ");
text.text("");
for (var i = 0; i < lines.length; i++) {
text
.append("tspan")
.attr("x", text.attr("x"))
.attr("dy", i ? "1.2em" : 0)
.style(
"font-size",
`${Math.sqrt(d.y1 - d.y0) * (i == 0 ? 4 : 0.875) + 5}px`
) // フォントサイズを設定
.text(lines[i]);
}
});
return svg.node();
}