2017-12-03 9 views
5

私はd3 v4(4.12.0)を使用しています。d3 v4で無限水平軸を作成

私はSVGコンテナを持っています。このコンテナには、マウスでパンすることに対応する単純な水平軸(x軸、リニアスケール)が描画されています。

"無限"または "無限"の横軸をシミュレートしたいと思います。

これは、非常に大きなデータセットの小さな部分だけをロードしてレンダリングし、この大きなセットの要素の非常に小さなサブセットを示す軸だけを描画することを意味します。

私は、オブジェクトの大きな配列から10データポイントを示す横軸を持っていると言います。私はoffsetパラメータを保持しています。このパラメータは0から始まり、この配列の最初の10点を表示します。

マイ手順:

私はに軸をスクロールすると十分11日以降のデータポイントを表示するには、その後、私は左:

  1. 更新offsetパラメータが反映させるためにどのように多くのユニットI新しいオフセット値

  2. 再オフに基づいて、

  3. 更新X軸スケールを翻訳しました私の試みは、最大動作します

軸(scroller_element_width)で1つの単位を表す画素数で軸を含むグループ要素を翻訳

  • x_scale)更新規模の範囲で軸ラベルを描きますステップ3に進みます。このプロセスは、ステップ4で失敗したように見えます。軸の最終的な移動は決して起こらないという点です。

    軸全体が左に移動し、新しいラベルがありますが、更新されたラベルでは右に移動しません。基本的にはページ外になります。

    私はd3の専門家にこのステップが失敗している理由とこれを解決するために何ができるかをここで質問したいと思います。

    renderScroller() { 
        console.log("renderScroller called"); 
        if ((this.state.scrollerWidth == 0) || (this.state.scrollerHeight == 0)) return; 
    
        const self = this; 
        const scroller = this.scrollerContainer; 
        const scroller_content = this.scrollerContent; 
        const scroller_width = this.state.scrollerWidth; 
        const scroller_height = this.state.scrollerHeight; 
    
        var offset = 0, 
         limit = 10, 
         current_index = 10; 
    
        var min_translate_x = 0, 
         max_translate_x; 
    
        var scroller_data = Constants.test_data.slice(offset, limit); 
    
        var x_extent = d3.extent(scroller_data, function(d) { return d.window; }); 
        var y_extent = [0, d3.max(scroller_data, function(d) { return d.total; })]; 
    
        var x_scale = d3.scaleLinear(); 
        var y_scale = d3.scaleLinear(); 
    
        var x_axis_call = d3.axisTop(); 
    
        x_scale.domain(x_extent).range([0, scroller_width]); 
        y_scale.domain(y_extent).range([scroller_height, 0]); 
    
        x_axis_call.scale(x_scale); 
    
        d3.select(scroller_content) 
         .append("g") 
         .attr("class", "x axis") 
         .attr("transform", "translate(" + [0, scroller_height] + ")") 
         .call(x_axis_call); 
    
        var scroller_element_width = parseFloat(scroller_width/(x_scale.domain()[1] - x_scale.domain()[0])); 
    
        var pan = d3.zoom() 
         .on("zoom", function() { 
    
         var t = parseSvg(d3.select(scroller_content).attr("transform")); 
         var x_offset = parseFloat((t.translateX + d3.event.transform.x)/scroller_element_width); 
    
         // 
         // lock scale and prevent y-axis pan 
         // 
         d3.event.transform.y = 0; 
         if (d3.event.transform.k == 1) { 
          d3.event.transform.x = (x_offset > 0) ? 0 : d3.event.transform.x; 
         } 
         else { 
          d3.event.transform.k = 1; 
          d3.event.transform.x = t.translateX; 
         } 
         d3.select(scroller_content).attr("transform", d3.event.transform); 
    
         t = parseSvg(d3.select(scroller_content).attr("transform")); 
         x_offset = parseFloat(t.translateX/scroller_element_width); 
    
         var test_offset = Math.abs(parseInt(x_offset)); 
    
         if (test_offset != offset) { 
          scroller_data = updateScrollerData(test_offset); 
          x_extent = d3.extent(scroller_data, function(d) { return d.window; }); 
          y_extent = [0, d3.max(scroller_data, function(d) { return d.total; })]; 
          x_scale.domain(x_extent).range([0, scroller_width]); 
          y_scale.domain(y_extent).range([scroller_height, 0]); 
          x_axis_call.scale(x_scale); 
    
          // 
          // update axis labels 
          // 
          d3.select(scroller_content) 
          .selectAll(".x.axis") 
          .call(x_axis_call); 
    
          // 
          // shift the axis backwards to simulate an endless horizontal axis 
          // 
          var pre_shift = parseSvg(d3.select(scroller_content).attr("transform")); 
          console.log("pre_shift", pre_shift.translateX); 
          console.log("scroller_element_width", scroller_element_width); 
          var expected_post_shift = pre_shift.translateX + scroller_element_width; 
          console.log("(expected) post_shift", expected_post_shift); 
    
          d3.zoom().translateBy(d3.select(scroller_content), expected_post_shift, 0); 
    
          //    
          // observed and expected translate values do not match! 
          // 
          var post_shift = parseSvg(d3.select(scroller_content).attr("transform")); 
          console.log("(observed) post_shift", post_shift.translateX); 
         } 
    
         }); 
    
        d3.select(scroller).call(pan); 
    
        max_translate_x = this.state.scrollerWidth - x_scale(x_extent[1]); 
        d3.zoom().translateBy(d3.select(scroller), max_translate_x, 0); 
    
        // fetch test data 
        function updateScrollerData(updated_offset) { 
         offset = updated_offset; 
         return Constants.test_data.slice(updated_offset - 1, updated_offset + limit - 1); 
        } 
        } 
    

    これが反応コンポーネント内の関数である:ここ

    は、軸を描画し、ズームイベントをフックする機能です。原料に反応それほど関係ありませんが、ここではそのコンポーネントのrender()機能で、親SVGと子グループの要素を表示する:

    render() { 
        return (
         <svg 
         className="scroller" 
         ref={(scroller) => { this.scrollerContainer = scroller; }} 
         width={this.state.scrollerWidth} 
         height={this.state.scrollerHeight}> 
         <g 
          className="scroller-content" 
          ref={(scrollerContent) => { this.scrollerContent = scrollerContent; }} 
         /> 
         </svg> 
        ); 
        } 
    

    示すように、scrollerContainer refは族元素が含まれているSVGありますscrollerContent。このscrollerContentは、水平軸を含むものです。

    x軸をパンまたはスクロールすると、変換はscrollerContentに適用されます。

    変換パラメータを取得するには、私はd3-interpolateparseSvgヘルパーメソッドを使用しています。ES6経由:完全性については

    import * as d3 from 'd3'; 
    import { parseSvg } from "d3-interpolate/src/transform/parse"; 
    

    、ここでのテストデータの抜粋です:

    export const test_data = [ 
        { 
        "total": 29.86, 
        "signal": [ 
         4.842, 
         1.608, 
         1.837, 
         3.052, 
         1.677, 
         0.8041, 
         3.09, 
         1.813, 
         2.106, 
         2.38, 
         1.773, 
         0.8128, 
         2.047, 
         1.658, 
         0.3588 
        ], 
        "window": 0, 
        "chr": "chr1" 
        }, 
        { 
        "total": 35.67, 
        "signal": [ 
         0.6111, 
         1.995, 
         0.5715, 
         2.51, 
         3.318, 
         1.523, 
         3.94, 
         2.743, 
         4.445, 
         0.759, 
         4.938, 
         2.61, 
         3.379, 
         1.27, 
         1.057 
        ], 
        "window": 1, 
        "chr": "chr1" 
        }, 
        { 
        "total": 39.14, 
        "signal": [ 
         0.0589, 
         0.1608, 
         2.426, 
         4.673, 
         3.511, 
         3.912, 
         2.809, 
         4.197, 
         4.648, 
         2.069, 
         2.84, 
         3.878, 
         0.2681, 
         3.622, 
         0.06911 
        ], 
        "window": 2, 
        "chr": "chr1" 
        }, 
        { 
        "total": 37.45, 
        "signal": [ 
         2.688, 
         1.235, 
         2.358, 
         1.994, 
         1.541, 
         1.189, 
         0.8078, 
         4.872, 
         2.287, 
         4.266, 
         2.24, 
         3.349, 
         3.519, 
         1.896, 
         3.21 
        ], 
        "window": 3, 
        "chr": "chr1" 
        }, 
        { 
        "total": 47.17, 
        "signal": [ 
         3.338, 
         3.613, 
         3.872, 
         1.166, 
         1.828, 
         4.24, 
         1.476, 
         4.025, 
         4.144, 
         4.922, 
         2.183, 
         2.701, 
         3.825, 
         4.346, 
         1.494 
        ], 
        "window": 4, 
        "chr": "chr1" 
        }, 
        { 
        "total": 41.7, 
        "signal": [ 
         0.2787, 
         1.74, 
         0.7557, 
         4.236, 
         2.865, 
         4.542, 
         4.113, 
         1.265, 
         4.826, 
         3.731, 
         4.931, 
         2.392, 
         2.014, 
         0.6566, 
         3.352 
        ], 
        "window": 5, 
        "chr": "chr1" 
        }, 
        { 
        "total": 31.43, 
        "signal": [ 
         3.025, 
         4.399, 
         1.001, 
         4.859, 
         0.9173, 
         2.851, 
         2.916, 
         1.821, 
         1.228, 
         1.646, 
         0.1008, 
         2.09, 
         2.502, 
         0.1476, 
         1.924 
        ], 
        "window": 6, 
        "chr": "chr1" 
        }, 
        { 
        "total": 38.23, 
        "signal": [ 
         1.123, 
         1.972, 
         0.5079, 
         4.808, 
         0.5669, 
         4.647, 
         2.598, 
         1.874, 
         0.8699, 
         4.876, 
         3.981, 
         1.503, 
         4.683, 
         2.853, 
         1.366 
        ], 
        "window": 7, 
        "chr": "chr1" 
        }, 
        { 
        "total": 44.2, 
        "signal": [ 
         3.895, 
         0.7457, 
         2.208, 
         1.837, 
         3.219, 
         3.98, 
         3.494, 
         4.225, 
         3.117, 
         3.162, 
         3.171, 
         2.449, 
         0.1419, 
         3.745, 
         4.807 
        ], 
        "window": 8, 
        "chr": "chr1" 
        }, 
        { 
        "total": 36.33, 
        "signal": [ 
         0.3164, 
         2.753, 
         4.094, 
         2.237, 
         4.748, 
         2.483, 
         1.541, 
         4.113, 
         0.1874, 
         3.71, 
         1.313, 
         0.221, 
         2.736, 
         1.208, 
         4.671 
        ], 
        "window": 9, 
        "chr": "chr1" 
        }, 
        { 
        "total": 43.05, 
        "signal": [ 
         1.924, 
         0.4136, 
         3.057, 
         4.686, 
         1.263, 
         0.1333, 
         0.8786, 
         4.715, 
         4.845, 
         4.282, 
         2.112, 
         4.597, 
         3.822, 
         1.322, 
         4.999 
        ], 
        "window": 10, 
        "chr": "chr1" 
        }, 
        { 
        "total": 31.28, 
        "signal": [ 
         4.216, 
         0.6655, 
         2.078, 
         1.235, 
         0.5526, 
         1.556, 
         1.005, 
         3.196, 
         1.907, 
         4.932, 
         0.006601, 
         1.269, 
         3.964, 
         4.608, 
         0.09109 
        ], 
        "window": 11, 
        "chr": "chr1" 
        }, 
        { 
        "total": 48.3, 
        "signal": [ 
         4.469, 
         1.138, 
         3.958, 
         2.801, 
         3.404, 
         4.988, 
         2.649, 
         3.818, 
         3.284, 
         0.9281, 
         3.982, 
         0.496, 
         4.28, 
         3.258, 
         4.845 
        ], 
        "window": 12, 
        "chr": "chr1" 
        }, 
        { 
        "total": 42.1, 
        "signal": [ 
         1.087, 
         3.127, 
         0.493, 
         3.276, 
         4.195, 
         1.561, 
         2.638, 
         4.897, 
         3.675, 
         4.937, 
         0.05847, 
         4.272, 
         2.33, 
         1.776, 
         3.776 
        ], 
        "window": 13, 
        "chr": "chr1" 
        }, 
        { 
        "total": 40.1, 
        "signal": [ 
         1.275, 
         4.574, 
         2.805, 
         1.646, 
         0.8759, 
         4.948, 
         3.637, 
         3.227, 
         2.259, 
         2.983, 
         2.905, 
         4.134, 
         3.133, 
         0.08384, 
         1.617 
        ], 
        "window": 14, 
        "chr": "chr1" 
        }, 
        { 
        "total": 50.31, 
        "signal": [ 
         2.228, 
         0.7037, 
         4.977, 
         1.143, 
         2.506, 
         4.348, 
         4.344, 
         3.998, 
         4.213, 
         2.745, 
         4.374, 
         3.411, 
         4.504, 
         4.417, 
         2.396 
        ], 
        "window": 15, 
        "chr": "chr1" 
        }, 
        { 
        "total": 34.7, 
        "signal": [ 
         2.729, 
         3.891, 
         3.873, 
         2.973, 
         0.1487, 
         1.573, 
         1.781, 
         2.788, 
         2.191, 
         2.912, 
         1.355, 
         2.582, 
         2.374, 
         3.164, 
         0.3641 
        ], 
        "window": 16, 
        "chr": "chr1" 
        }, 
        { 
        "total": 32.89, 
        "signal": [ 
         3.619, 
         2.119, 
         1.854, 
         4.083, 
         0.9916, 
         0.5065, 
         0.8343, 
         4.835, 
         1.723, 
         3.926, 
         2.675, 
         2.281, 
         0.1531, 
         2.239, 
         1.049 
        ], 
        "window": 17, 
        "chr": "chr1" 
        }, 
        { 
        "total": 38.94, 
        "signal": [ 
         1.976, 
         1.587, 
         3.808, 
         0.1173, 
         3.823, 
         4.349, 
         3.652, 
         1.308, 
         3.434, 
         3.855, 
         1.622, 
         0.2916, 
         2.382, 
         3.091, 
         3.647 
        ], 
        "window": 18, 
        "chr": "chr1" 
        }, 
        { 
        "total": 34.18, 
        "signal": [ 
         0.339, 
         3.695, 
         3.108, 
         3.267, 
         0.08282, 
         3.53, 
         2.316, 
         1.11, 
         4.504, 
         4.111, 
         0.007636, 
         0.5581, 
         2.985, 
         1.707, 
         2.857 
        ], 
        "window": 19, 
        "chr": "chr1" 
        }, 
        { 
        "total": 29.62, 
        "signal": [ 
         2.695, 
         0.8477, 
         4.417, 
         3.012, 
         2.454, 
         2.686, 
         0.6529, 
         0.2275, 
         1.052, 
         0.2092, 
         2.968, 
         3.268, 
         0.7144, 
         0.4441, 
         3.973 
        ], 
        "window": 20, 
        "chr": "chr1" 
        } 
    ]; 
    

    がうまくいけば、この問題を説明するために必要なすべての作業を示しています。アドバイスや指導に感謝します。

  • +3

    axample動作するコードを提供してください – KEKUATAN

    答えて

    4

    完全に再現可能な例がなければ、コードを読みにくいことがわかりました。だから私はあなたがしようとしているものの簡単な例をコーディングしました。おそらくそれはお手伝いします:

    <!DOCTYPE html> 
     
    <html> 
     
    
     
    <head> 
     
        <meta charset="utf-8" /> 
     
        <script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script> 
     
        <style> 
     
        .axis path { 
     
         display: none; 
     
        } 
     
        
     
        .axis line { 
     
         stroke-opacity: 0.3; 
     
         shape-rendering: crispEdges; 
     
        } 
     
        
     
        .view { 
     
         fill: url(#gradient); 
     
         stroke: #000; 
     
        } 
     
        
     
        button { 
     
         position: absolute; 
     
         top: 20px; 
     
         left: 20px; 
     
        } 
     
        </style> 
     
    </head> 
     
    
     
    <body> 
     
        <svg width="500" height="500"></svg> 
     
        <script src="//d3js.org/d3.v4.min.js"></script> 
     
        <script> 
     
        
     
        // 10,000 random data points 
     
        var data = d3.range(1, 10000).map(function(d) { 
     
         return { 
     
         i: d, 
     
         x: Math.random() < 0.5 ? Math.random() * 1000 : Math.random() * -1000, 
     
         y: Math.random() < 0.5 ? Math.random() * 1000 : Math.random() * -1000, 
     
         } 
     
        }); 
     
    
     
        var svg = d3.select("svg"), 
     
         margin = { 
     
         top: 10, 
     
         right: 10, 
     
         bottom: 10, 
     
         left: 10 
     
         }, 
     
         width = +svg.attr("width") - margin.left - margin.right, 
     
         height = +svg.attr("height") - margin.top - margin.bottom, 
     
         g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")"); 
     
    
     
        // large "endless" zoom 
     
        var zoom = d3.zoom() 
     
         .scaleExtent([-1e100, 1e100]) 
     
         .translateExtent([ 
     
         [-1e100, -1e100], 
     
         [1e100, 1e100] 
     
         ]) 
     
         .on("zoom", zoomed); 
     
    
     
        var x = d3.scaleLinear() 
     
         .domain([-100, 100]) 
     
         .range([0, width]); 
     
    
     
        var y = d3.scaleLinear() 
     
         .domain([-100, 100]) 
     
         .range([height, 0]); 
     
    
     
        var xAxis = d3.axisBottom(x) 
     
         .ticks((width + 2)/(height + 2) * 10) 
     
         .tickSize(-height); 
     
    
     
        var yAxis = d3.axisRight(y) 
     
         .ticks(10) 
     
         .tickSize(width) 
     
         .tickPadding(8 - width); 
     
    
     
        var gX = svg.append("g") 
     
         .attr("transform", "translate(0," + height + ")") 
     
         .attr("class", "axis axis--x") 
     
         .call(xAxis); 
     
    
     
        var gY = svg.append("g") 
     
         .attr("class", "axis axis--y") 
     
         .call(yAxis); 
     
    
     
        svg.call(zoom); 
     
    
     
        // plot our data initially 
     
        updateData(x, y); 
     
    
     
        function zoomed() { 
     
         var t = d3.event.transform, 
     
         sx = t.rescaleX(x), //<-- rescale the scales 
     
         sy = t.rescaleY(x); 
     
    
     
         // swap out axis 
     
         gX.call(xAxis.scale(sx)); 
     
         gY.call(yAxis.scale(sy)); 
     
    
     
         updateData(sx, sy) 
     
        } 
     
    
     
        // classic enter, update, exit pattern 
     
        function updateData(sx, sy) { 
     
    
     
         // filter are data to those points in range 
     
         var f = data.filter(function(d) { 
     
         return (
     
          d.x > sx.domain()[0] && 
     
          d.x < sx.domain()[1] && 
     
          d.y > sy.domain()[0] && 
     
          d.y < sy.domain()[1] 
     
         ) 
     
         }); 
     
    
     
         var s = g.selectAll(".point") 
     
         .data(f, function(d) { 
     
          return d.i; 
     
         }); 
     
    
     
         // remove those out of range 
     
         s.exit().remove(); 
     
    
     
         // add the new ones in range 
     
         s = s.enter() 
     
         .append('circle') 
     
         .attr('class', 'point') 
     
         .attr('r', 10) 
     
         .style('fill', 'steelblue') 
     
         .merge(s); 
     
    
     
         // update all in range 
     
         s.attr('cx', function(d) { 
     
          return sx(d.x); 
     
         }) 
     
         .attr('cy', function(d) { 
     
          return sy(d.y); 
     
         }); 
     
        } 
     
        </script> 
     
    </body> 
     
    
     
    </html>

    関連する問題