2017-12-03 74 views
5

Tôi đang sử dụng d3 v4 (4.12.0).Tạo trục ngang vô tận với d3 v4

Tôi có vùng chứa SVG mà tôi vẽ một trục ngang đơn giản (trục x, thang đo tuyến tính) phản ứng với việc di chuột bằng chuột.

Tôi muốn mô phỏng trục ngang "vô hạn" hoặc "vô tận". Bằng cách này, tôi có nghĩa là tôi chỉ muốn tải và hiển thị một phần nhỏ của tập dữ liệu rất lớn và chỉ vẽ đủ trục hiển thị tập hợp con rất nhỏ các phần tử từ tập hợp lớn này.

Giả sử tôi có trục ngang hiển thị 10 điểm dữ liệu từ một mảng lớn các đối tượng. Tôi giữ một tham số offset bắt đầu từ 0, để hiển thị mười điểm đầu tiên của mảng này.

thủ tục của tôi:

Khi tôi di chuyển trục sang trái đủ xa để hiển thị 11 và điểm dữ liệu tiếp theo, tôi sau đó:

  1. Cập nhật tham số offset để phản ánh bao nhiêu đơn vị tôi đã dịch

  2. cập nhật quy mô trục x, dựa trên các giá trị bù đắp mới

  3. Re vẽ các nhãn trục với phạm vi quy mô được cập nhật của (x_scale)

  4. Dịch các yếu tố nhóm chứa các trục bằng của số điểm ảnh đại diện cho một đơn vị trên trục (scroller_element_width)

nỗ lực của tôi làm việc lên bước 3. Quá trình này có vẻ không thành công ở bước 4, trong đó bản dịch cuối cùng của trục không bao giờ xảy ra.

Toàn bộ trục được di chuyển sang trái và nó có các nhãn mới, nhưng nó không di chuyển sang phải với các nhãn được cập nhật đó - về cơ bản nó rơi khỏi trang.

Tôi muốn hỏi chuyên gia d3 ở đây tại sao bước này không thành công và tôi có thể làm gì để khắc phục sự cố này.

Dưới đây là chức năng thu hút các trục và móc lên sự kiện zoom:

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); 
    } 
    } 

Đây là một chức năng trong một bộ phận Phản ứng. Những thứ Phản ứng không phải là quá phù hợp, nhưng ở đây là render() chức năng của thành phần đó, để hiển thị SVG phụ huynh và nhóm trẻ yếu tố:

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> 
    ); 
    } 

Như đã trình bày, các scrollerContainer ref là SVG có chứa các nguyên tố nhóm scrollerContent. Điều này scrollerContent là những gì có chứa trục ngang.

Khi xoay hoặc cuộn trục x, các phép biến đổi được áp dụng cho scrollerContent.

Để nhận thông số chuyển đổi, tôi đang sử dụng phương thức trợ giúp parseSvg từ d3-interpolate, tức làqua ES6:

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

Để hoàn chỉnh, đây là một đoạn dữ liệu thử nghiệm:

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" 
    } 
]; 

Hy vọng rằng điều này cho thấy tất cả các công việc cần thiết để giải thích vấn đề. Cảm ơn lời khuyên hoặc hướng dẫn.

+3

vui lòng cung cấp axample đang làm việc – KEKUATAN

Trả lời

4

Tôi thấy mã của bạn khó theo dõi mà không có ví dụ sao chép đầy đủ. Vì vậy, tôi đã viết một ví dụ đơn giản về những gì bạn đang cố gắng làm. Có lẽ nó sẽ giúp:

<!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>

Các vấn đề liên quan