Javascript D3移动时电刷滞后
使用react绘制D3图表。我已经在d3中实现了一个笔刷 当我滚动画笔时,它可以很好地工作,但它是滞后的。有趣的是,如果我删除下面(在NavChart.js中)标记为problemble的行,笔刷就会开始正常工作(没有延迟)。但是,这一行很重要,因为我使用react hook更新ChartWrapper.js(称为“selection”)中的变量,我需要将图表与nav连接起来。 代码如下: ChartWrapper.js->Javascript D3移动时电刷滞后,javascript,reactjs,d3.js,Javascript,Reactjs,D3.js,使用react绘制D3图表。我已经在d3中实现了一个笔刷 当我滚动画笔时,它可以很好地工作,但它是滞后的。有趣的是,如果我删除下面(在NavChart.js中)标记为problemble的行,笔刷就会开始正常工作(没有延迟)。但是,这一行很重要,因为我使用react hook更新ChartWrapper.js(称为“selection”)中的变量,我需要将图表与nav连接起来。 代码如下: ChartWrapper.js-> import React, {useRef, useEffect, u
import React, {useRef, useEffect, useState, useImperativeHandle} from 'react';
import {select, selectAll, extent, axisBottom, axisLeft, scaleLinear, scaleTime, curve, line, curveCardinal, set} from 'd3';
import Chart from './Chart';
import NavChart from './NavChart';
import UsePrevious from './UsePrevious';
const MARGIN = {TOP: 50, BOTTOM: 50, LEFT: 50, RIGHT: 50};
const maxData = 10;
export default function ChartWrapper(props) {
let [selection, setSelection] = useState([props.data[0].time, props.data[1].time]);
const previousSelection = UsePrevious(selection);
let [first, setFirst] = useState(true);
const svgRef = useRef();
let chart = null;
let nav = null;
const navRef = useRef();
useEffect(
() => {
console.log(`selection is : ${selection}`);
//Chart component
chart = new Chart(MARGIN, svgRef, props.data, props.xAxisText, props.yAxisText);
// navigation component (Both aren't connected right now)
nav = new NavChart(MARGIN, navRef, props.data, previousSelection.current, selection, setSelection, first);
setFirst(false);
}, [props.data, previousSelection, selection, first]
);
return (
<div>
<svg ref = {svgRef} width = {700} height = {400}></svg>
<br /><br/>
<svg ref = {navRef} width = {700} height = {75}></svg>
</div>
);
}
import-React,{useRef,useffect,useState,useImperialiveHandle}来自'React';
从'd3'导入{select,selectAll,extent,axisBottom,axisLeft,scaleLinear,scaleTime,curve,line,curveCardinal,set};
从“./Chart”导入图表;
从“/NavChart”导入NavChart;
从“/UsePrevious”导入UsePrevious;
常量边距={TOP:50,BOTTOM:50,LEFT:50,RIGHT:50};
常数maxData=10;
导出默认函数ChartWrapper(道具){
让[selection,setSelection]=useState([props.data[0]。时间,props.data[1]。时间];
const previousSelection=使用先前(选择);
let[first,setFirst]=useState(true);
const svgRef=useRef();
设chart=null;
设nav=null;
const navRef=useRef();
使用效果(
() => {
log(`selection is:${selection}`);
//图表组件
图表=新图表(边距、svgRef、props.data、props.xAxisText、props.yAxisText);
//导航组件(两个组件目前未连接)
导航=新导航图表(边距、导航参考、道具数据、上一个选择、当前选择、设置选择、第一);
setFirst(假);
},[props.data,上一个选择,选择,第一个]
);
返回(
);
}
NavChart.js->
import {select,
selectAll,
extent,
axisBottom,
axisLeft,
scaleLinear,
scaleTime,
line,
brushX,
event
} from 'd3';
const PADDING = {TOP: 10, BOTTOM: 30};
export default class NavChart {
constructor(MARGIN, svgRef, data, previousSelection, selection, setSelection, firstTime) {
this.svg = select(svgRef.current);
this.MARGIN = MARGIN;
this.data = data;
this.chartWidth = this.svg.attr('width') - MARGIN.LEFT - MARGIN.RIGHT;
this.chartHeight = this.svg.attr('height') - PADDING.TOP - PADDING.BOTTOM;
this.xScale = scaleTime()
.domain(extent(
this.data.map((d) => d.time)
))
.range([0, this.chartWidth]);
this.yScale = scaleLinear()
.domain(extent(
this.data.map((d) => d.value)
))
.range([this.chartHeight, 0]);
this.myLine = line()
.x((d) => this.xScale(d.time))
.y((d) => this.yScale(d.value));
this.chartArea = this.svg.append('g')
.attr('transform', `translate(${MARGIN.LEFT}, ${PADDING.TOP})`);
this.chartArea.append('rect')
.attr('x', 0)
.attr('y', 0)
.attr('width', this.chartWidth)
.attr('height', this.chartHeight)
.attr('fill', '#f2f2f2');
this.pathsG = this.chartArea.append('g')
.attr('class', 'pathsG')
.attr('transform', `translate(0,0)`);
this.xAxis = axisBottom(this.xScale);
this.xAxisG = this.chartArea.append('g')
.attr('transform', `translate(0, ${this.chartHeight})`);
this.xAxisG.call(this.xAxis);
this.pathsG.selectAll('path')
.data([this.data])
.join('path')
.attr('d', (value) => this.myLine(value))
.attr('fill', 'none')
.attr('stroke', '#D073BA')
.attr('stroke-width', '1.5');
this.circlesVar = this.pathsG.selectAll('.circle')
.data(data);
this.circlesVar.enter().append('circle')
.attr('class', 'circle')
.attr('r', '3')/*(value) =>
value.time > selection[0] && value.time <= selection[1] ? 4 : 2)*/
.style('fill', '#D073BA')
.attr('cx', (d) => this.xScale(d.time))
.attr('cy', (d) => this.yScale(d.value));
this.brushG = this.chartArea.append('g')
.attr('class', 'brush');
this.brush = brushX().extent([
[0, 0],
[this.chartWidth, this.chartHeight]
]).on('start brush end', () => {
if (event.selection && (event.sourceEvent != null)) {
const timeSelection = event.selection.map(this.xScale.invert);
setSelection(timeSelection); // This line is problematic
}
});
this.brushG.call(this.brush)
.call(this.brush.move, selection.map(this.xScale));
}
}
import{select,
选择全部,
范围,
在底部,
左,,
标线,
缩放时间,
行,,
灌木丛,
事件
}从'd3'开始;
常量填充={TOP:10,BOTTOM:30};
导出默认类导航图{
构造函数(边距、svgRef、数据、以前的选择、选择、设置选择、第一次){
this.svg=select(svgRef.current);
这个。保证金=保证金;
这个数据=数据;
this.chartWidth=this.svg.attr('width')-MARGIN.LEFT-MARGIN.RIGHT;
this.chartHeight=this.svg.attr('height')-PADDING.TOP-PADDING.BOTTOM;
this.xScale=scaleTime()
.域(范围)(
this.data.map((d)=>d.time)
))
.范围([0,此.chartWidth]);
this.yScale=scaleLinear()
.域(范围)(
this.data.map((d)=>d.value)
))
.范围([this.chart height,0]);
this.myLine=line()
.x((d)=>此.xScale(d.time))
.y((d)=>该.yScale(d.value));
this.charterea=this.svg.append('g'))
.attr('transform','translate(${MARGIN.LEFT},${PADDING.TOP})`);
this.chartArea.append('rect')
.attr('x',0)
.attr('y',0)
.attr('width',this.chartWidth)
.attr('height',this.chartHeight)
.attr('fill','#f2f2');
this.pathsG=this.chartArea.append('g'))
.attr('class','pathsG')
.attr('transform','translate(0,0)`);
this.xAxis=axisBottom(this.xScale);
this.xAxisG=this.chartArea.append('g'))
.attr('transform','translate(0,${this.chartHeight})`);
this.xAxis.call(this.xAxis);
this.pathsG.selectAll('path'))
.data([this.data])
.join('路径')
.attr('d',(值)=>此.myLine(值))
.attr('填充','无')
.attr('stroke','#D073BA')
.attr('stroke-width','1.5');
this.circlesVar=this.pathsG.selectAll(“.circle”)
.数据(数据);
this.circlesVar.enter().append('circle'))
.attr('class','circle')
.attr('r','3')/*(值)=>
value.time>选择[0]&&value.time此.xScale(d.time))
.attr('cy',(d)=>此.yScale(d.value));
this.brushG=this.chartArea.append('g'))
.attr('class','brush');
this.brush=brushX().extent([
[0, 0],
[this.chartWidth,this.chartHeight]
]).on('开始画笔结束',()=>{
if(event.selection&(event.sourceEvent!=null)){
const timeSelection=event.selection.map(this.xScale.invert);
setSelection(timeSelection);//此行有问题
}
});
this.brush.call(this.brush)
.call(this.brush.move,selection.map(this.xScale));
}
}
我知道对选择变量的更改(通过NavChart.js中的setSelection方法)将触发ChartWrapper.js中的useEffect()钩子,但我不知道为什么会导致延迟。我需要向笔刷添加过渡吗
编辑:链接到代码在线->'当前代码侦听重叠事件,因此并行响应多次 听
end
事件就足够了,而不是一起听start
、brush
和end
:
this.brush = brushX().extent([
[0, 0],
[this.chartWidth, this.chartHeight]
]).on('end', () => {
if (event.selection && (event.sourceEvent != null)) {
const timeSelection = event.selection.map(this.xScale.invert);
setSelection(timeSelection);
}
});
当前代码侦听重叠的事件,因此并行响应多次 听
end
事件就足够了,而不是一起听start
、brush
和end
:
this.brush = brushX().extent([
[0, 0],
[this.chartWidth, this.chartHeight]
]).on('end', () => {
if (event.selection && (event.sourceEvent != null)) {
const timeSelection = event.selection.map(this.xScale.invert);
setSelection(timeSelection);
}
});
谢谢你的解决方案!!谢谢你的解决方案!!