Reputation: 61
simple d3 line chart... need the line to span the whole svg and the labels to be anchored to the path line itself. bonus points if there's optional code to add markers as well. thanks in advance.
<html>
<head>
<script src="https://d3js.org/d3.v5.min.js"></script>
<script>
function createLineChart(data,id) {
var svg = d3.select("body").append("svg")
.attr("width", 800)
.attr("height", 600);
var line = d3.line()
.x(function(d, i) { return i * 50 + 50; })
.y(function(d) { return 300 - d; });
svg.append("path")
.datum(data)
.attr("d", line)
.attr("stroke", "black")
.attr("stroke-width", 2)
.attr("fill", "none");
data.forEach(function(d, i) {
if (i > 0) {
var percentChange = (d - data[i - 1]) / data[i - 1] * 100;
var color = percentChange >= 0 ? "green" : "red";
var y = percentChange >= 0 ? d - 15 : d + 15;
svg.append("text")
.text(percentChange.toFixed(1) + "%")
.attr("x", i * 50 + 50)
.attr("y", y)
.attr("text-anchor", "middle")
.attr("fill", color);
}
});
}
</script>
</head>
<body>
</body>
<script>
var data = [10, 20, 15, 40, 50, 60];
createLineChart(data);
</script>
</html>
Upvotes: 1
Views: 554
Reputation: 4964
I guess the main things you need to read up on are d3 scales and selections (particularly joins). In particular:
w
and height h
of the SVG and then defining scales to fit the path into the SVG.createLineChart([10, 20, 15, 40, 50, 60], { mark_points: true })
function createLineChart(data, opts = {}) {
let { w = 800, h = 500, mark_points = false } = opts;
let pad = 50;
let x_scale = d3
.scaleLinear()
.domain([0, data.length - 1])
.range([pad, w - pad]);
let [ymin, ymax] = d3.extent(data);
let y_scale = d3
.scaleLinear()
.domain([ymin, ymax])
.range([h - pad, pad]);
let svg = d3.select('#container')
.append("svg")
.attr("width", w)
.attr("height", h)
.style("border", "solid 1px black");
let line = d3
.line()
.x(function (d, i) {
return x_scale(i);
})
.y(function (d) {
return y_scale(d);
});
svg
.append("path")
.datum(data)
.attr("d", line)
.attr("stroke", "black")
.attr("stroke-width", 2)
.attr("fill", "none");
svg
.selectAll("circle")
.data(data)
.join("circle")
.attr("cx", (_, i) => x_scale(i))
.attr("cy", (d) => y_scale(d))
.attr("r", 5)
.attr("fill", "black");
if (mark_points) {
svg
.selectAll("text")
.data(data)
.join("text")
.attr("x", (_, i) => x_scale(i))
.attr("y", (d) => y_scale(d))
.attr("dx", -3)
.attr("dy", 12)
.attr("font-size", 16)
.attr("text-anchor", "end")
.attr("fill", (_, i) =>
data[i] < data[i - 1]
? "red"
: data[i] == data[i - 1]
? "blue"
: "green"
)
.text(function (_, i) {
if (i > 0) {
let change = (data[i] - data[i - 1]) / data[i - 1];
return d3.format("0.2%")(change);
} else {
return "";
}
});
}
svg
.append("g")
.attr("transform", `translate(0, ${h - pad})`)
.call(d3.axisBottom(x_scale).ticks(data.length));
svg
.append("g")
.attr("transform", `translate(${pad})`)
.call(d3.axisLeft(y_scale).ticks(5));
}
<script src="https://d3js.org/d3.v7.min.js"></script>
<div id="container"></div>
Upvotes: 1