d3js 使折线图缩放工作
d3js make zoom work on line chart
我正在尝试在 Angular 4 应用程序中构建 d3js V4 图表。该图表将几组数据显示为单独的线条。
我不知道如何使缩放正常工作(仅在 X 轴上)。
在 zoomed() 函数中是我已经尝试过的所有内容,并附有解释为什么它不起作用的注释。
createChart() {
const element = this.chartContainer.nativeElement;
this.width = element.offsetWidth - this.margin.left - this.margin.right;
this.height = element.offsetHeight - this.margin.top - this.margin.bottom;
// canvas
const svg = d3.select(element).append('svg')
.attr('width', element.offsetWidth)
.attr('height', element.offsetHeight);
// chart plot area
this.chart = svg.append('g')
.attr('transform', 'translate(' + this.margin.left + ',' + this.margin.top + ')');
// domains
const xDomain = [this.from, this.to];
const yDomain = [0, 100];
// scales
this.xScale = d3.scaleTime().domain(xDomain).range([0, this.width]);
this.yScale = d3.scaleLinear().domain(yDomain).range([this.height, 0]);
// "data" function to draw lines
this.valueline = d3.line<TimeValueObject>()
.curve(d3.curveStepBefore)
.x((d) => this.xScale(d.time))
.y((d) => this.yScale(d.value));
// X Axis
this.xAxis = this.chart.append('g')
.attr('class', 'xaxis')
.attr('transform', 'translate(0,' + this.height + ')')
.call(d3.axisBottom(this.xScale)
.ticks(this.xTicks)
.tickFormat(function (d) { return moment.utc(d).format(this.timeFormat); }));
// Y Axis
this.yAxis = this.chart.append('g')
.attr('class', 'yaxis')
.call(d3.axisLeft(this.yScale)
.ticks(this.yTicks));
// zoom
const zoom = d3.zoom()
.on('zoom', this.zoomed.bind(this));
this.chart.call(zoom);
}
addData(myDataObject) {
// Add a data valueline path
this.chart.append('path')
.attr('class', 'line')
.attr('stroke', myDataObject.strokeStyle)
.attr('id', myDataObject.name)
.attr('d', this.valueline(myDataObject.data));
}
zoomed() {
//KO: Zoom applied to whole graph in every direction.
//this.chart.attr('transform', d3.event.transform);
//KO: Zoom applied to whole graph
// this.chart
// .attr('transform', 'translate(' + d3.event.transform.x + ',0) scale(' + d3.event.transform.k + ',1)');
//KO: Zoom applied to data but data not redrawn. Shows pixellated garbage at high zoom
//Besides: when zoomed, the data expands beyond chart limits (Y axis on the left and chart right edge)
//for (let [key, value] of this.dataObjects) {
// this.chart.select('#' + key + '')
// .attr('transform', 'translate(' + d3.event.transform.x + ',0) scale(' + d3.event.transform.k + ',1)');
//}
//KO: zoom is "elastic":
// - On first mousewheel, nothing happens
// - On second mousewheel, graph zooms in the direction of previous mousewheel
// - Example: If I 'mousewheelup' 3 times in a row, I will have to 'mousewheeldown' 8 times to get back to zero zoom. The graph will keep zooming instead of unzooming on the 3 first 'mousewheeldown'
//Besides: when zoomed, the data expands beyond chart limits (Y axis on the left and chart right edge)
//for (let [key, value] of this.dataObjects) {
// this.chart.select('#' + key + '')
// .attr('d', this.valueline(value.data));
//}
//X Axis zoom
const t = d3.event.transform;
this.xScale = t.rescaleX(this.xScale); //if I comment this line, no zoom occurs whatsoever
//this.xAxis.call(d3.axisBottom(this.xScale)); //will cause 'elastic' axis zoom
}
不同的代码尝试基于 "official" 示例:
https://github.com/d3/d3-zoom
终于找到解决办法了。
诀窍在于 d3 缩放事件值始终针对初始缩放,而不是针对之前的缩放。
所以,首先,必须调整比例,通过转换原始比例:
const t = d3.event.transform;
this.xScale.domain(t.rescaleX(this.xScaleOriginal).domain());
→ 必须创建原始比例的副本
然后,可以重新绘制 xAxis :
this.xAxisElement.call(this.xAxis);
→ 必须将项目存储在单独的变量中:d3.axisBottom() 函数结果的 xAxis 和附加轴元素的 xAxisElement
最后,可以重新绘制数据线:
for (let [key, value] of this.dataObjects) {
this.chart.select('#' + key + '')
.attr('d', this.valueline(value.data));
}
→ 这是未修改的
我正在尝试在 Angular 4 应用程序中构建 d3js V4 图表。该图表将几组数据显示为单独的线条。
我不知道如何使缩放正常工作(仅在 X 轴上)。
在 zoomed() 函数中是我已经尝试过的所有内容,并附有解释为什么它不起作用的注释。
createChart() {
const element = this.chartContainer.nativeElement;
this.width = element.offsetWidth - this.margin.left - this.margin.right;
this.height = element.offsetHeight - this.margin.top - this.margin.bottom;
// canvas
const svg = d3.select(element).append('svg')
.attr('width', element.offsetWidth)
.attr('height', element.offsetHeight);
// chart plot area
this.chart = svg.append('g')
.attr('transform', 'translate(' + this.margin.left + ',' + this.margin.top + ')');
// domains
const xDomain = [this.from, this.to];
const yDomain = [0, 100];
// scales
this.xScale = d3.scaleTime().domain(xDomain).range([0, this.width]);
this.yScale = d3.scaleLinear().domain(yDomain).range([this.height, 0]);
// "data" function to draw lines
this.valueline = d3.line<TimeValueObject>()
.curve(d3.curveStepBefore)
.x((d) => this.xScale(d.time))
.y((d) => this.yScale(d.value));
// X Axis
this.xAxis = this.chart.append('g')
.attr('class', 'xaxis')
.attr('transform', 'translate(0,' + this.height + ')')
.call(d3.axisBottom(this.xScale)
.ticks(this.xTicks)
.tickFormat(function (d) { return moment.utc(d).format(this.timeFormat); }));
// Y Axis
this.yAxis = this.chart.append('g')
.attr('class', 'yaxis')
.call(d3.axisLeft(this.yScale)
.ticks(this.yTicks));
// zoom
const zoom = d3.zoom()
.on('zoom', this.zoomed.bind(this));
this.chart.call(zoom);
}
addData(myDataObject) {
// Add a data valueline path
this.chart.append('path')
.attr('class', 'line')
.attr('stroke', myDataObject.strokeStyle)
.attr('id', myDataObject.name)
.attr('d', this.valueline(myDataObject.data));
}
zoomed() {
//KO: Zoom applied to whole graph in every direction.
//this.chart.attr('transform', d3.event.transform);
//KO: Zoom applied to whole graph
// this.chart
// .attr('transform', 'translate(' + d3.event.transform.x + ',0) scale(' + d3.event.transform.k + ',1)');
//KO: Zoom applied to data but data not redrawn. Shows pixellated garbage at high zoom
//Besides: when zoomed, the data expands beyond chart limits (Y axis on the left and chart right edge)
//for (let [key, value] of this.dataObjects) {
// this.chart.select('#' + key + '')
// .attr('transform', 'translate(' + d3.event.transform.x + ',0) scale(' + d3.event.transform.k + ',1)');
//}
//KO: zoom is "elastic":
// - On first mousewheel, nothing happens
// - On second mousewheel, graph zooms in the direction of previous mousewheel
// - Example: If I 'mousewheelup' 3 times in a row, I will have to 'mousewheeldown' 8 times to get back to zero zoom. The graph will keep zooming instead of unzooming on the 3 first 'mousewheeldown'
//Besides: when zoomed, the data expands beyond chart limits (Y axis on the left and chart right edge)
//for (let [key, value] of this.dataObjects) {
// this.chart.select('#' + key + '')
// .attr('d', this.valueline(value.data));
//}
//X Axis zoom
const t = d3.event.transform;
this.xScale = t.rescaleX(this.xScale); //if I comment this line, no zoom occurs whatsoever
//this.xAxis.call(d3.axisBottom(this.xScale)); //will cause 'elastic' axis zoom
}
不同的代码尝试基于 "official" 示例: https://github.com/d3/d3-zoom
终于找到解决办法了。
诀窍在于 d3 缩放事件值始终针对初始缩放,而不是针对之前的缩放。
所以,首先,必须调整比例,通过转换原始比例:
const t = d3.event.transform;
this.xScale.domain(t.rescaleX(this.xScaleOriginal).domain());
→ 必须创建原始比例的副本
然后,可以重新绘制 xAxis :
this.xAxisElement.call(this.xAxis);
→ 必须将项目存储在单独的变量中:d3.axisBottom() 函数结果的 xAxis 和附加轴元素的 xAxisElement
最后,可以重新绘制数据线:
for (let [key, value] of this.dataObjects) {
this.chart.select('#' + key + '')
.attr('d', this.valueline(value.data));
}
→ 这是未修改的