Javascript 域更改后的平移缩放

Javascript 域更改后的平移缩放,javascript,d3.js,Javascript,D3.js,我有一些域发生变化的时间序列数据:我可以取过去6个月、去年、过去2年等等。我创建了一个只显示数据的D3图表 但是,您也可以缩放此图表,但当您缩放然后更改域时,缩放“重置”,但在单击时再次工作 当域发生变化时,我希望保持当前的缩放:因为它是timeseries数据,所以我希望它处于相同的位置。我怎样才能做到这一点 过去6个月 去年 过去两年 //随机数据 函数randomData(){ 函数randn_bm(){ var u=0,v=0; 而(u==0)u=Math.random(); 而(v

我有一些域发生变化的时间序列数据:我可以取过去6个月、去年、过去2年等等。我创建了一个只显示数据的D3图表

但是,您也可以缩放此图表,但当您缩放然后更改域时,缩放“重置”,但在单击时再次工作

当域发生变化时,我希望保持当前的缩放:因为它是timeseries数据,所以我希望它处于相同的位置。我怎样才能做到这一点


过去6个月
去年
过去两年
//随机数据
函数randomData(){
函数randn_bm(){
var u=0,v=0;
而(u==0)u=Math.random();
而(v==0)v=Math.random();
返回Math.sqrt(-2.0*Math.log(u))*Math.cos(2.0*Math.PI*v);
}
让天数=[]
让endDate=新日期(2020年1月0日)
对于(var d=新日期(2018,0,0);d({
日期:d,
值:randn_bm()
}))
}
//图表
常数高度=600
常数宽度=800
常量边距={top:20,right:0,bottom:30,left:40}
设x;
让y;
常量缩放=(事件)=>{
设xz=event.transform.rescaleX(x);
调用(xAxis,xz);
gLine.selectAll(“路径”)
.数据([数据])
.join(“路径”)
.attr(“填充”、“无”)
.attr(“笔划”、“钢蓝”)
.attr(“笔划宽度”,1.5)
.attr(“d”,d3.line()
.x(d=>xz(d.date))
.y(d=>y(d.value)))
}
const zoom=d3.zoom()
.scaleExtent([1,32])
.范围([[margin.left,0],[width-margin.right,height]]
.translateExtent([[margin.left,-Infinity],[width-margin.right,Infinity]]
。打开(“缩放”,缩放);
const svg=d3.select(“body”).append(“svg”)
.attr(“视图框”[0,0,宽度,高度]);
调用(缩放)
const gLine=svg.append(“g”).attr(“class”,“series”).attr(“clip path”,“url(#clip)”)
const gX=svg.append(“g”).attr(“类”,“x轴”)
const gY=svg.append(“g”).attr(“类”,“y轴”)
常数xAxis=(g,x)=>g
.attr(“transform”,`translate(0,${height-margin.bottom})`)
.call(d3.axisBottom(x.ticksizeout(0))
常数yAxis=(g,y)=>g
.attr(“transform”,translate(${margin.left},0)`)
.呼叫(d3.左(y))
.call(g=>g.select(“.domain”).remove()
svg.append(“clipPath”)
.attr(“id”、“剪辑”)
.append(“rect”)
.attr(“x”,左边距)
.attr(“y”,页边距。顶部)
.attr(“宽度”,宽度-边距。左侧-边距。右侧)
.attr(“高度”,高度-边距。顶部-边距。底部);
函数renderChart(数据){
x=d3.scaleTime()
.域(d3.范围(数据,d=>d.date))
.范围([margin.left,width-margin.right])
y=d3.scaleLinear()
.domain(d3.extent(data,d=>d.value)).nice()
.范围([高度-边距.底部,边距.顶部])
调用(xAxis,x);
gY.call(yAxis,y);
gLine.selectAll(“路径”)
.数据([数据])
.join(“路径”)
.attr(“填充”、“无”)
.attr(“笔划”、“钢蓝”)
.attr(“笔划宽度”,1.5)
.attr(“d”,d3.line()
.x(d=>x(d.date))
.y(d=>y(d.value)))
}
//钮扣
常量数据=随机数据()
const lastDataDate=新日期(2020,1,0)
常量按钮=d3。选择(“.按钮”)
.选择全部(“按钮”)
.数据([6,12,24])
.join(“按钮”)
.on(“点击”),(uu,月)=>{
const startDate=新日期(lastDataDate)
startDate.setMonth(startDate.getMonth()-months)
常量filteredData=data.filter(d=>d.date>startDate)
renderChart(过滤数据)
})
renderChart(数据)
一种方法是将最后一个缩放事件存储在变量中,如果存在,则使用最后一个事件调用
zoomed()
,而不是从头开始重新绘制轴

编辑:我现在更了解你的问题了。我所做的如下:

  • 每当单击按钮时,首先获取缩放的域
    xz
  • 然后看看我们是否需要钳制它,使域成为新数据的子集
    xz.domain()
    必须始终属于
    x.domain()
  • 如果是这种情况,请计算比例因子和视口中心的点
  • 完全重新绘制图表
  • 要求
    d3
    使用先前计算的比例缩放至正确的比例,然后要求其使用先前计算的中心点平移至正确的位置
  • 此外,我将
    y
    -域更改为始终使用整个数据集进行计算。这可以确保当按下任何按钮时,线不会垂直跳转

    除非您的视口覆盖了在单击按钮后不再可用的数据,否则不会围绕
    x
    -轴跳跃

    测试用例

    对于所有这些,视图应保持不变:

    • 点击“去年”,然后点击“2年”
    • 单击“2年”,然后放大到2020年11月1月的范围。点击“6个月”
    • 单击“去年”,缩放和平移直到2019年2月至4月。点击“2年”
    • 单击“6个月”,然后单击“去年”,然后单击“2年”
    视图应随所有这些更改而更改:

    • 单击“2年”,完全缩小,然后单击“6个月”
    • 单击“2年”,然后放大到2020年2月至1月的范围。点击“6个月”
    • 单击“去年”,缩放和平移直到2019年2月至4月。点击“6个月”
    //随机数据
    函数randomData(){
    函数randn_bm(){
    var u=0,
    v=0;
    而(u==0)u=Math.random();
    而(v==0)v=Math.random();
    返回Math.sqrt(-2.0*Math.log(u))*Math.cos(2.0*Math.PI*v);
    }
    让天数=[]
    让endDate=新日期(2020年1月0日)
    对于(var d=新日期(2018,0,0);d({
    日期:d,
    价值:兰特
    
    const zoomed = (event) => {
      xScale.domain(event.transform.rescaleX(xReference).domain());
      draw(data);
    }
    
        const endDate = lastDataDate;
        const startDate = d3.timeMonth.offset(endDate,-months);
    
        // k = width of range needed for data set / width of range needed for area of interest         
        const k = (xReference.range()[1] - xReference.range()[0]) / (xReference(endDate) - xReference(startDate))\
        // translate to account for starting point of area of interest.
        const tx = xReference(startDate); 
        
        // let the zoom handle it.
        svg.call(zoom.transform, d3.zoomIdentity
            .scale(k)
            .translate(-tx+margin.left/k, 0) // margin.left/k : account for scale range not starting at 0.
            );