还可以继续去改造options,类似echarts的配置图。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Line Tension</title>
<script src="https://d3js.org/d3.v5.min.js"></script>
</head>
<body>
<div class="control-group">
<button onclick="update()">Update</button>
</div>
</body>
<script type="text/javascript">
const { ceil, round, sqrt, random: _random } = Math;
const range = (min, max) => (max - min) * _random() + min;
const random = (n = 10) => round(range(1, n));
const randomColor = () => `#${(~~(Math.random() * 0x1000000)).toString(16)}`;
class Chart {
constructor(options) {
this.data = Array.from({ length: 2 }, _ => Array.from({ length: random(10) }, (_, i) => ({ x: i, y: random(10) })));
this.width = 600;
this.height = 300;
this.margin = { top: 30, left: 30, right: 30, bottom: 30 };
this.colors = d3.scaleOrdinal(d3.schemeCategory10);
this.scaleX = d3.scaleLinear().domain([0, 10])
this.scaleY = d3.scaleLinear().domain([0, 10])
this.xStart = this.margin.left;
this.yStart = this.height - this.margin.bottom;
this.xEnd = this.width - this.margin.right;
this.yEnd = this.margin.top;
this.quadrantWidth = this.xEnd - this.xStart;
this.quadrantHeight = this.yStart - this.yEnd;
this.render();
}
render(data) {
const { width, height, svg, xStart, yEnd } = this;
if (!svg) {
this.svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
this.bodyG = this.svg.append("g")
.attr("class", "body")
.attr("transform", `translate(${xStart},${yEnd})`)
.attr("clip-path", "url(#body-clip)");
this.renderAxes();
this.defineBodyClip();
}
data && this.update(data);
this.renderBody();
}
update(data) {
this.data = data;
}
renderAxes() {
const { svg } = this;
var axesG = svg.append("g").attr("class", "axes");
this.renderXAxis(axesG);
this.renderYAxis(axesG);
}
renderXAxis(axes) {
const { scaleX, quadrantWidth, quadrantHeight, xStart, yStart } = this;
const xAxis = d3.axisBottom().scale(scaleX.range([0, quadrantWidth]));
axes.append("g")
.attr("class", "x axis")
.attr("transform", `translate(${xStart},${yStart})`)
.call(xAxis);
d3.selectAll("g.x g.tick")
.append("line")
.attr("stroke", "black")
.attr("shape-rendering", "crispEdges")
.attr("stroke-opacity", .2)
.attr("x1", 0)
.attr("y1", 0)
.attr("x2", 0)
.attr("y2", - quadrantHeight);
}
renderYAxis(axes) {
const { scaleY, quadrantWidth, quadrantHeight, xStart, yEnd } = this;
const yAxis = d3.axisLeft().scale(scaleY.range([quadrantHeight, 0]));
axes.append("g")
.attr("class", "y axis")
.attr("transform", `translate(${xStart},${yEnd})`)
.call(yAxis);
d3.selectAll("g.y g.tick")
.append("line")
.attr("stroke", "black")
.attr("shape-rendering", "crispEdges")
.attr("stroke-opacity", .2)
.attr("x1", 0)
.attr("y1", 0)
.attr("x2", quadrantWidth)
.attr("y2", 0);
}
defineBodyClip() {
const { svg, quadrantWidth, quadrantHeight } = this;
const padding = 5;
svg.append("defs")
.append("clipPath")
.attr("id", "body-clip")
.append("rect")
.attr("x", 0 - padding)
.attr("y", 0)
.attr("width", quadrantWidth + 2 * padding)
.attr("height", quadrantHeight);
}
renderBody() {
this.renderLines();
this.renderDots();
}
renderLines() {
const { bodyG, data, scaleX, scaleY, colors } = this;
const line = d3.line()
.x(d => scaleX(d.x))
.y(d => scaleY(d.y));
const pathLines = bodyG.selectAll("path.line").data(data);
pathLines
.enter()
.append("path")
.merge(pathLines)
.style("stroke", (d, i) => colors(i))
.attr("fill", "none")
.attr("stroke-width", 2)
.attr("class", "line")
.transition()
.attr("d", d => line(d));
}
renderDots() {
const { bodyG, scaleX, scaleY, data, colors } = this;
data.forEach((list, i) => {
const circle = bodyG.selectAll("circle._" + i).data(list);
circle.enter()
.append("circle")
.merge(circle)
.attr("class", "dot _" + i)
.style("stroke", d => colors(i))
.transition()
.attr("cx", d => scaleX(d.x))
.attr("cy", d => scaleY(d.y))
.attr("r", 4.5);
});
}
}
const numberOfSeries = 2;
const numberOfDataPoint = 11;
const data = Array.from({ length: numberOfSeries }, _ => Array.from({ length: numberOfDataPoint }, (_, i) => ({ x: i, y: random() })));
const chart = new Chart();
function update() {
data.forEach(item => item.forEach((_, i) => _.y = random(10)));
chart.render(data);
}
</script>
</html>