我创建了一个动态d3.js图表,用于绘制时间序列。 每一秒我都会添加一个新样本并删除旧样本。 它给人的印象是迷你线从右向左移动,这很好。 但是,我对添加和删除路径的新旧段的方式不太满意。
在此图像上,您可以看到右侧y轴与迷你图之间有一点间隙。随着火花线向左移动,这个间隙变得更大,一旦足够大,就会增加新的迷你线段。这看起来不是很顺利。 我希望绘制新的线段,因为迷你线向左移动(就像手工绘制时一样)。
我正在使用剪辑路径来隐藏我不想要的路径部分(在情节之外),但这似乎并没有给我正确的行为。
clip-path
:
this.container.append('defs')
.append('clipPath')
.attr('id', 'chart-content')
.append('rect')
.attr('height', this.height)
.attr('width', this.width);
使用clip-path
:
group.path = this.paths
.append('g')
.attr('clip-path', 'url(#chart-content)')
.append('path')
.data([group.data])
另一方面,剪辑路径似乎在我平移和缩放时工作..这使我更加困惑!我希望有人可以帮助我!
答案 0 :(得分:0)
我找到了问题的答案.. 这是一个动态折线图的片段!
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.line {
fill: none;
stroke: #000;
stroke-width: 1.5px;
}
#chart{
width: 100%;
height: 500px;
}
</style>
<div id='chart'></div>
<script src="https://d3js.org/d3.v4.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.5.1/moment.min.js"></script>
<script>
var margin = { top: 20, right: 20, bottom: 20, left: 20};
var padding = { top: 20, right: 40, bottom: 20, left: 40};
let width;
let height;
let svg;
let container;
let data;
let xScale;
let yScale;
let now;
let path;
let gX;
let xAxis;
let lineGenerator;
const duration = 500;
render = () => {
this.hostElement = d3.select('#chart');
this.width = this.hostElement.node().getBoundingClientRect().width - this.margin.left - this.margin.right - this.padding.left - this.padding.right;
this.height = this.hostElement.node().getBoundingClientRect().height - this.margin.top - this.margin.bottom - this.padding.bottom - this.padding.top;
this.createSvg();
this.createAxis();
this.createLine();
this.defineBounds();
this.tick();
}
createSvg = () => {
this.svg = this.hostElement
.append('svg')
.attr('transform', 'translate(' + this.margin.left + ',' + this.margin.top + ')')
.attr('width', this.width + this.padding.right + this.padding.left)
.attr('height', this.height + this.padding.top + this.padding.bottom)
this.container = this.svg
.append('g')
.attr('class', 'chart')
.attr('transform', 'translate(' + this.padding.left + ',' + this.padding.top + ')')
.attr('width', this.width)
.attr('height', this.height);
}
createAxis = () => {
// maxY
const maxY = d3.max(this.data, d => d.value);
// minX & maxX
const minX = d3.min(this.data, d => d.date);
const maxX = d3.max(this.data, d => d.date);
this.now = maxX;
const maxXToDisplay = moment(maxX).subtract(1, 's').toDate();
const minXToDisplay = moment(minX).add(2, 's').toDate();
// Update scales
this.xScale = d3.scaleTime()
.domain([minXToDisplay, maxXToDisplay])
.range([0, this.width]);
this.yScale = d3.scaleLinear()
.domain([0, maxY])
.range([this.height, 0]);
// Update axis
this.xAxis = d3.axisBottom(this.xScale);
const yAxis = d3.axisLeft(this.yScale);
// Draws the axis
this.gX = this.container.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(0,' + this.height + ')')
.call(this.xAxis);
const gY = this.container.append('g')
.attr('class', 'y axis')
.call(yAxis);
}
createLine = () => {
this.lineGenerator = d3.line()
.x(d => this.xScale(d.date))
.y(d => this.yScale(d.value))
.curve(d3.curveMonotoneX);
this.path = this.container
.append('g')
.attr('class', 'path-container')
.attr('clip-path', 'url(#chart-content)')
.append('path')
.datum(this.data)
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-width", 1.5)
.attr('d', this.lineGenerator);
}
tick = () => {
this.now = moment(this.now).add(1, 's').toDate();
// Add new values
this.data.push({
value: Math.floor(10 + Math.random() * 15),
date: this.now
});
// Remove old values
this.data.shift();
this.path.attr('d', this.lineGenerator);
const numberSamplesToDisplay = this.data.length - 2;
const minX = moment(this.now).subtract(numberSamplesToDisplay, 's').toDate();
const maxX = this.now;
const maxXToDisplay = moment(maxX).subtract(1, 's').toDate();
const minXToDisplay = moment(minX).add(1, 's').toDate();
// Shift domain
this.xScale.domain([minXToDisplay, maxXToDisplay]);
// Slide x-axis left
const xTransition = this.gX.transition()
.duration(1000)
.ease(d3.easeLinear)
.call(this.xAxis);
// Slide paths left
this.path.attr('transform', null)
.transition()
.duration(1000)
.ease(d3.easeLinear)
.attr('transform', 'translate(' + this.xScale(minX) + ', 0)')
.on('end', this.tick);
}
defineBounds = () => {
this.container.append('defs')
.append('clipPath')
.attr('id', 'chart-content')
.append('rect')
.attr('height', this.height)
.attr('width', this.width);
}
generateData = () => {
const dataset = [
{
value: Math.floor(10 + Math.random() * 15),
date: moment().subtract(60, 'seconds').toDate()
}
];
for (let i = 0; i < 59; i ++) {
dataset.push({
value: Math.floor(10 + Math.random() * 15),
date: moment(dataset[dataset.length - 1].date).add(1, 'seconds').toDate()
})
}
return dataset;
}
this.data = generateData();
render();
</script>
&#13;