chart = {
const barWidth = 30;
const marginTop = 30;
const marginRight = 10;
const marginBottom = 50;
const marginLeft = 40;
const width = alphabet.length * (barWidth + 10) + marginLeft + marginRight;
const height = 400;
const x = d3.scaleBand()
.domain(alphabet.map(d => d.letter))
.range([marginLeft, width - marginRight])
.padding(0.2);
console.log("x domain:", x.domain());
console.log("x range:", x.range());
const y = d3.scaleLinear()
.domain([0, d3.max(alphabet, d => d.frequency)])
.nice()
.range([height - marginBottom, marginTop]);
console.log("y domain:", y.domain());
console.log("y range:", y.range());
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: 12px sans-serif;");
function roundedRect(x, y, width, height, radius) {
return [
["M", x + radius, y],
["L", x + width - radius, y],
["Q", x + width, y, x + width, y + radius],
["L", x + width, y + height],
["L", x, y + height],
["L", x, y + radius],
["Q", x, y, x + radius, y]
].map(d => d.join(" "));
}
// 添加柱子
svg.append("g")
.attr("fill", "steelblue")
.classed("selected", false)
.selectAll("rect")
.data(alphabet)
.join("rect")
.attr("x", d => x(d.letter))
.attr("y", d => y(d.frequency)) // 柱子顶部位置
.attr("width", x.bandwidth())
.attr("height", d => y(0) - y(d.frequency)) // 保证是正数
// .attr("rx", 5) // 圆角
// .attr("ry", 5) // 圆角
.each(function(d) {
console.log(`Drawing ${d.letter} at x=${x(d.letter)}, y=${y(d.frequency)}, height=${y(marginTop) - y(d.frequency)}`);
})
.on("click", function (event, d) {
// 移除之前的选中状态和动画
// 如果再次点击已选中的柱子,则恢复所有柱子
if (d3.select(this).classed("selected") === true) {
d3.selectAll("rect")
.classed("selected", false)
.transition()
.duration(300)
.style("opacity", 1);
} else {
d3.select(this)
.classed("selected", true)
.style("opacity", 1);
d3.selectAll("rect")
.filter(v => v !== d)
.classed("selected", false)
.transition()
.duration(300)
.style("opacity", 0.3);
}
// 设置当前柱子为选中
// 其他柱子淡出动画
// 其他柱子变透明
// d3.selectAll("rect")
// .filter(v => v !== d)
// .transition()
// .duration(500)
// .style("opacity", 0.3);
})
;
// 添加文本标签
svg.append("g")
.attr("fill", "black")
.attr("text-anchor", "middle")
.selectAll("text")
.data(alphabet)
.join("text")
.attr("x", d => x(d.letter) + x.bandwidth() / 2)
.attr("y", d => y(d.frequency) - 6)
.text(d => d3.format(".1%")(d.frequency));
// 添加底部 x 轴
svg.append("g")
.attr("transform", `translate(0,${height - marginBottom})`)
.call(d3.axisBottom(x));
// 添加左侧 y 轴
svg.append("g")
.attr("transform", `translate(${marginLeft},0)`)
.call(d3.axisLeft(y).ticks(height / 40, "%"));
svg.selectAll("rect")
.append("path")
.attr("d", d => roundedRect(
x(d.letter),
y(d.frequency),
x.bandwidth(),
height - y(d.frequency),
5 // 圆角半径
))
return svg.node();
function pointermoved(event) {
const [xm, ym] = d3.pointer(event);
// const i = d3.leastIndex(points, ([x, y]) => Math.hypot(x - xm, y - ym));
// const [x, y, k] = points[i];
// path.style("stroke", ({z}) => z === k ? null : "#ddd").filter(({z}) => z === k).raise();
// dot.attr("transform", `translate(${x},${y})`);
// dot.select("text").text(k);
// svg.property("value", unemployment[i]).dispatch("input", {bubbles: true});
}
}