2013-07-09 42 views
33

Tôi đang làm việc trên biểu đồ chuỗi thời gian cho phép người dùng cuộn lùi lại từ hiện tại. Tôi có thể tìm thấy các hướng dẫn về các biểu đồ d3.js thời gian thực, tôi có thể tìm các hướng dẫn về phóng to và thu nhỏ, và tôi có thể tìm thấy các hướng dẫn về cách sử dụng các nguồn dữ liệu bên ngoài. Tôi đang gặp khó khăn khi đưa tất cả kiến ​​thức này lại với nhau.d3.js chuỗi thời gian cuộn vô hạn

Đây là hành vi mà tôi đang tìm kiếm:

  • Biểu đồ thể pan lạc hậu trong thời gian (nghĩa là các đường, các điểm dữ liệu, và các trục di chuyển với cách kéo chuột hoặc ngón tay)
  • Việc quét chỉ nên thực hiện trục x và không cần phóng to.
  • Khi người dùng kéo biểu đồ, tải nhiều dữ liệu hơn, mang lại trải nghiệm di chuyển vô hạn
  • Tôi định đệm ít nhất một giá trị "trang" bổ sung để người dùng cuộn vào (đã nhận phần này tìm ra)
  • tôi không nghĩ rằng tôi cần phải chuyển tiếp, vì panning của biểu đồ sẽ suôn sẻ đã dịch nó

Đây là những gì tôi đã làm việc cho đến nay:

// set up a zoom handler only for panning 
    // by limiting the scaleExtent  
    var zoom = d3.behavior.zoom() 
    .x(x) 
    .y(y) 
    .scaleExtent([1, 1]) 
    .on("zoom", pan); 

    var loadedPage = 1; // begin with one page of data loaded 
    var nextPage = 2; // next page will be page 2 
    var panX = 0; 

    function pan() 
    { 
    if (d3.event) 
    { 
     panX = d3.event ? d3.event.translate[0] : 0; 

     // is there a better way to determine when 
     // to load the next page? 
     nextPage = panX/(width + margin.left + margin.right) + 2; 
     nextPage = Math.floor(nextPage); 

     // if we haven't loaded in the next page's data 
     // load it in so that the user can scroll into it 
     if (nextPage > loadedPage) { 

      console.log("Load a new page"); 
      loadedPage += 1; 

      // load more data 
      Chart.query(/*params will be here*/).then(
      function(response) { 

       // append the new data onto the front of the array 
       data = data.concat(response); 
       console.log(data.length); 

       // I need to add the new data into the line chart 
       // but how do I make that work with the pan 
       // logic from zoom? 

     } 
     ); 
    } 
     // is this where I update the axes and scroll the chart? 
     // What's the best way to do that? 

     } 
    } 

Trong mã này, tôi có thể biết khi nào cần lấy thêm dữ liệu từ máy chủ, nhưng tôi không chắc chắn cách chèn dữ liệu vào biểu đồ theo cách hoạt động với chênh lệch pan. Tôi có sử dụng biến đổi dịch hay tôi có thể cập nhật giá trị d của đường dẫn của đường không?

Bất kỳ đề xuất nào cũng sẽ được hoan nghênh ..., nếu có ai biết về bất kỳ trình diễn nào đã hiển thị panning vô hạn qua dữ liệu chuỗi thời gian, điều đó sẽ được đánh giá cao.

+0

Những phương pháp bạn sử dụng phụ thuộc vào yêu cầu của bạn. Sử dụng 'transform' sẽ làm cho việc phóng to và thu nhỏ dễ dàng hơn (vì bạn chỉ cần cập nhật một thuộc tính đó), nhưng có thể trở thành vấn đề về bộ nhớ. Tôi không biết về bất kỳ trình diễn panning vô hạn nào, nhưng bạn có thể làm việc với một trong nhiều bản trình diễn cho chuỗi thời gian, panning, v.v. –

+0

@EmptyArray - bạn có làm việc này không? Tôi xây dựng một cái gì đó tương tự và bị mắc kẹt trên cùng một tính năng. Mọi cập nhật đều được đánh giá cao. – DeBraid

+0

Tôi có một nguyên mẫu làm việc, mặc dù mã không đẹp. Sau khi ghép nối dữ liệu mới, tôi đã cập nhật tên miền, cập nhật "d" của đường dẫn, và đã dịch một đường dịch chuyển của dòng và các điểm. Vấn đề chính là nó chỉ ghép nối mãi mãi, vì vậy bạn sẽ cần phải hạn chế số lượng các điểm dữ liệu, hoặc các trang, nạp, và "dỡ bỏ" dữ liệu đó là đủ xa offscreen. Sau đó, bạn sẽ theo dõi số minLoadedPage ở bên trái những gì người dùng nhìn thấy và maxLoadedPage ở bên phải. cái đó có giúp ích không? – EmptyArray

Trả lời

1

Điều này đã quá muộn, nhưng chỉ trả lời trong trường hợp ai đó cần lại. Tôi đã có hầu hết các mã đã sẵn sàng cho scatterplot của tôi để tải lên đó. Hy vọng nó sẽ giúp bạn. Mã được tạo ra như một thử nghiệm khi tôi đang học các tính năng này. Vì vậy, hãy kiểm tra trước khi bạn sử dụng.

Lưu ý: D3js panning được triển khai với hành vi thu phóng, thu nhỏ bị vô hiệu hóa với tỷ lệExtent, Y panning restricted. Dữ liệu được tải khi đạt tới cực đại. Vui lòng kiểm tra Plunkr link

// Code goes here 
 

 
window.chartBuilder = {}; 
 
(function(ns) { 
 

 
    function getMargin() { 
 
    var margin = { 
 
     top: 20, 
 
     right: 15, 
 
     bottom: 60, 
 
     left: 60 
 
    }; 
 
    var width = 960 - margin.left - margin.right; 
 
    var height = 500 - margin.top - margin.bottom; 
 
    return { 
 
     margin: margin, 
 
     width: width, 
 
     height: height 
 
    }; 
 
    } 
 

 
    function getData() { 
 
    var data = [ 
 
     [5, 3], 
 
     [10, 17], 
 
     [15, 4], 
 
     [2, 8] 
 
    ]; 
 
    return data; 
 
    } 
 

 
    //function defineScales(data, width, height) { 
 
    // var x = d3.scale.linear() 
 
    //  .domain([0, d3.max(data, function (d) { 
 
    //   return d[0]; 
 
    //  })]) 
 
    //  .range([0, width]); 
 
    // 
 
    // var y = d3.scale.linear() 
 
    //  .domain([0, d3.max(data, function (d) { 
 
    //   return d[1]; 
 
    //  })]) 
 
    //  .range([height, 0]); 
 
    // return {x: x, y: y}; 
 
    //} 
 
    function defineYScale(data, domain, range) { 
 
    var domainArr = domain; 
 
    if (!domain || domain.length == 0) { 
 
     domainArr = [0, d3.max(data, function(d) { 
 
     return d[1]; 
 
     })]; 
 
    } 
 
    var y = d3.scale.linear() 
 
     .domain(domainArr) 
 
     .range(range); 
 

 
    return y; 
 
    } 
 

 
    function defineXScale(data, domain, range) { 
 
    var domainArr = domain; 
 
    if (!domain || domain.length == 0) { 
 
     domainArr = [d3.min(data, function(d) { 
 
     return d[0]; 
 
     }), d3.max(data, function(d) { 
 
     return d[0]; 
 
     })]; 
 
    } 
 

 
    var x = d3.scale.linear() 
 
     .domain(domainArr) 
 
     .range(range); 
 
    return x; 
 
    } 
 

 
    function getSvg(width, margin, height) { 
 
    var chart = d3.select('body') 
 
     .append('svg:svg') 
 
     .attr('width', width + margin.right + margin.left) 
 
     .attr('height', height + margin.top + margin.bottom) 
 
     .attr('class', 'chart'); 
 
    return chart; 
 
    } 
 

 
    function getContainerGroup(chart, margin, width, height) { 
 
    var main = chart.append('g') 
 
     .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')') 
 
     .attr('width', width) 
 
     .attr('height', height) 
 
     .attr('class', 'main'); 
 
    return main; 
 
    } 
 

 
    function renderXAxis(x, main, height) { 
 
    var xAxis = d3.svg.axis() 
 
     .scale(x) 
 

 
    .orient('bottom'); 
 
    var xAxisElement = main.select('.x.axis'); 
 
    if (xAxisElement.empty()) { 
 
     xAxisElement = main.append('g') 
 
     .attr('transform', 'translate(0,' + height + ')') 
 
     .attr('class', 'x axis') 
 
    } 
 
    xAxisElement.call(xAxis); 
 

 
    return xAxis; 
 
    } 
 

 
    function renderYAxis(y, main) { 
 
    var yAxis = d3.svg.axis() 
 
     .scale(y) 
 
     .orient('left'); 
 
    var yAxisElement = main.select('.y.axis'); 
 
    if (yAxisElement.empty()) { 
 

 
     yAxisElement = main.append('g') 
 
     .attr('transform', 'translate(0,0)') 
 
     .attr('class', 'y axis'); 
 
    } 
 
    yAxisElement.call(yAxis); 
 
    return yAxis; 
 
    } 
 

 
    function renderScatterplot(main, data, scales) { 
 
    var g = main.append("svg:g"); 
 
    var divTooltip = d3.select('.tooltip1'); 
 
    if (divTooltip.empty()) { 
 
     divTooltip = d3.select('body').append('div') 
 
     .attr('class', 'tooltip1') 
 
     .style('opacity', 0); 
 
    } 
 

 
    g.selectAll("scatter-dots") 
 
     .data(data, function(d, i) { 
 
     return i; 
 
     }) 
 
     .enter().append("svg:circle") 
 
     .attr("cx", function(d, i) { 
 
     return scales.x(d[0]); 
 
     }) 
 
     .attr("cy", function(d) { 
 
     return scales.y(d[1]); 
 
     }) 
 
     .on('click', function(d) { 
 

 
     // log(d.toString()); 
 

 

 
     }) 
 

 
    .attr("r", 8); 
 
    } 
 

 
    function addZoomRect(main, scales, zoom) { 
 
    var zoomRect = main.append('rect') 
 
     .attr('width', function() { 
 
     return scales.x(d3.max(scales.x.domain())); 
 
     }) 
 
     .attr('height', function() { 
 
     return scales.y(d3.min(scales.y.domain())); 
 
     }) 
 
     .attr('x', 0) 
 
     .attr('y', 0) 
 
     .attr('fill', 'transparent') 
 
     .attr('stroke', 'red'); 
 
    if (zoom) { 
 
     zoomRect.call(zoom); 
 
    } 
 
    return zoomRect; 
 
    } 
 

 
    function restrictYPanning(zoom) { 
 
    var zoomTranslate = this.translate(); 
 
    this.translate([zoomTranslate[0], 0]); 
 
    } 
 

 
    function addXScrollEndEvent(scales, direction, data) { 
 
    var zoomTranslate = this.translate(); 
 
    var condition; 
 
    var currentDomainMax = d3.max(scales.x.domain()); 
 
    var dataMax = d3.max(data, function(d) { 
 
     return d[0]; 
 
    }); 
 
    var currentDomainMin = d3.min(scales.x.domain()); 
 
    var dataMin = 
 
     d3.min(data, function(d) { 
 
     return d[0]; 
 
     }); 
 
    if (currentDomainMax > dataMax && direction === 'right') { 
 
     //log('currentDomainMax ', currentDomainMax); 
 
     //log('dataMax ', dataMax); 
 
     //log('----------------'); 
 
     condition = true; 
 
    } 
 

 
    if (dataMin > currentDomainMin && direction === 'left') { 
 
     //log('currentDomainMin ', currentDomainMin); 
 
     //log('dataMin ', dataMin); 
 
     //log('----------------'); 
 
     condition = true; 
 
    } 
 
    //var xRightLimit, xTranslate; 
 
    //if (direction === 'right') { 
 
    // xRightLimit = scales.x(d3.max(scales.x.domain())) - (getMargin().width + 60); 
 
    // 
 
    // xTranslate = 0 - zoomTranslate[0];// + scales.x(d3.min(scales.x.domain())); 
 
    // 
 
    // condition = xTranslate > xRightLimit; 
 
    //} else { 
 
    // xRightLimit = scales.x(d3.min(scales.x.domain())); 
 
    // 
 
    // xTranslate = zoomTranslate[0];// + scales.x(d3.min(scales.x.domain())); 
 
    // 
 
    // condition = xTranslate > xRightLimit; 
 
    //} 
 
    return condition; 
 
    } 
 

 
    function onZoom(zoom, main, xAxis, yAxis, scales, data) { 
 
    //var xAxis = d3.svg.axis() 
 
    // .scale(scales.x) 
 
    // .orient('bottom'); 
 
    //var yAxis = d3.svg.axis() 
 
    // .scale(scales.y) 
 
    // .orient('left'); 
 
    //alert(data); 
 
    var translate = zoom.translate(); 
 
    var direction = ''; 
 

 
    if (translate[0] < ns.lastTranslate[0]) { 
 
     direction = 'right'; 
 
    } else { 
 
     direction = 'left'; 
 
    } 
 
    ns.lastTranslate = translate; //d3.transform(main.attr('transform')).translate ; 
 
    // log('zoom translate', ns.lastTranslate); 
 
    // log('d3 Event translate', d3.event.translate); 
 
    window.scales = scales; 
 
    window.data = data; 
 

 

 
    // ns.lastTranslate = translate; 
 

 
    var divTooltip = d3.select('.tooltip1'); 
 
    if (divTooltip.empty()) { 
 
     divTooltip = d3.select('body').append('div') 
 
     .attr('class', 'tooltip1') 
 
     .style('opacity', 0); 
 
    } 
 

 

 
    restrictYPanning.call(zoom); 
 
    var xScrollEndCondition = addXScrollEndEvent.call(zoom, scales, direction, data); 
 
    if (xScrollEndCondition) { 
 
     if (zoom.onXScrollEnd) { 
 

 
     zoom.onXScrollEnd.call(this, { 
 
      'translate': translate, 
 
      'direction': direction 
 

 
     }); 
 
     } 
 
    } 
 

 

 
    main.select(".x.axis").call(xAxis); 
 
    main.select(".y.axis").call(yAxis); 
 
    var dataElements = main.selectAll("circle") 
 
     .data(data, function(d, i) { 
 
     return i; 
 
     }); 
 

 
    dataElements.attr("cx", function(d, i) { 
 
     return scales.x(d[0]); 
 
     }) 
 
     .attr("cy", function(d) { 
 
     return scales.y(d[1]); 
 
     }).attr("r", 8); 
 

 
    dataElements.enter().append("svg:circle") 
 
     .attr("cx", function(d, i) { 
 
     return scales.x(d[0]); 
 
     }) 
 
     .attr("cy", function(d) { 
 
     return scales.y(d[1]); 
 
     }).on('click', function(d) { 
 

 
     // log(d.toString()); 
 

 

 
     }) 
 

 
    .attr("r", 8); 
 
    // log(direction); 
 

 

 

 
    } 
 

 
    //var xRangeMax; 
 
    //var xRangeMin; 
 
    ns.lastTranslate = [0, 0]; 
 

 
    /** 
 
    * Created by Lenovo on 7/4/2015. 
 
    */ 
 
    function log(titlee, msgg) { 
 
    var msg = msgg; 
 

 
    var title; 
 
    if (titlee) { 
 
     title = titlee + ':-->'; 
 
    } 
 

 
    if (!msgg) { 
 
     msg = titlee; 
 
     title = ''; 
 
    } else { 
 
     if (Array.isArray(msgg)) { 
 
     msg = msgg.toString(); 
 
     } 
 
     if ((typeof msg === "object") && (msg !== null)) { 
 
     msg = JSON.stringify(msg); 
 
     } 
 
    } 
 

 
    var tooltip = d3.select('.tooltip1'); 
 
    var earlierMsg = tooltip.html(); 
 
    var num = tooltip.attr('data-serial') || 0; 
 
    num = parseInt(num) + 1; 
 

 
    msg = '<div style="border-bottom:solid 1px green"><span style="color:white">' + num + ')</span><strong>' + title + '</strong> ' + decodeURIComponent(msg) + ' </div>'; 
 
    tooltip.html('<br>' + msg + '<br>' + earlierMsg).style({ 
 
     'color': 'lightGray', 
 
     'background': 'darkGray', 
 
     'font-family': 'courier', 
 
     'opacity': 1, 
 
     'max-height': '200px', 
 
     'overflow': 'auto' 
 
     }) 
 
     .attr('data-serial', num); 
 
    } 
 

 
    function addLoggerDiv() { 
 
    var divTooltip = d3.select('.tooltip1'); 
 
    if (divTooltip.empty()) { 
 
     divTooltip = d3.select('body').append('div') 
 
     .attr('class', 'tooltip1') 
 
     .style({ 
 
      'opacity': 0, 
 
      'position': 'relative' 
 
     }); 
 

 
     d3.select('body').append('div') 
 
     .text('close') 
 
     .style({ 
 
      'top': 0, 
 
      'right': 0, 
 
      'position': 'absolute', 
 
      'background': 'red', 
 
      'color': 'white', 
 
      'cursor': 'pointer' 
 
     }) 
 
     .on('click', function() { 
 
      var thisItem = divTooltip; 
 
      var txt = thisItem.text(); 
 
      var display = 'none'; 
 
      if (txt === 'close') { 
 
      thisItem.text('open'); 
 
      display = 'none'; 
 
      } else { 
 
      thisItem.text('close'); 
 
      display = 'block'; 
 
      } 
 
      devTooltip.style('display', display); 
 

 
     }); 
 

 
     d3.select('body').append('div') 
 
     .text('clear') 
 
     .style({ 
 
      'top': 0, 
 
      'right': 20, 
 
      'position': 'absolute', 
 
      'background': 'red', 
 
      'color': 'white', 
 
      'cursor': 'pointer' 
 
     }) 
 
     .on('click', function() { 
 
      divTooltip.html(''); 
 
      divTooltip.attr('data-serial', '0'); 
 
     }); 
 
    } 
 
    } 
 

 

 

 
    $(document).ready(function() { 
 
    var data = getData(); 
 
    var __ret = getMargin(); 
 
    var margin = __ret.margin; 
 
    var width = __ret.width; 
 
    var height = __ret.height; 
 
    var scales = {}; 
 
    var xRangeMax = width; 
 
    scales.x = defineXScale(data, [], [0, xRangeMax]); 
 
    scales.y = defineYScale(data, [], [height, 0]); 
 
    addLoggerDiv(); 
 
    var svg = getSvg(width, margin, height); 
 
    var main = getContainerGroup(svg, margin, width, height); 
 
    // draw the x axis 
 
    var xAxis = renderXAxis(scales.x, main, height); 
 
    // draw the y axis 
 
    var yAxis = renderYAxis(scales.y, main); 
 

 
    var thisobj = this; 
 
    var zoom = d3.behavior.zoom().x(scales.x).y(scales.y).scaleExtent([1, 1]).on('zoom', function() { 
 
     onZoom.call(null, zoom, main, xAxis, yAxis, scales, data); 
 
    }); 
 
    zoom.onXScrollEnd = function(e) { 
 
     var maxX = d3.max(data, function(d) { 
 
     return d[0]; 
 
     }); 
 
     var minX = d3.min(data, function(d) { 
 
     return d[0]; 
 
     }); 
 
     var incrementX = Math.floor((Math.random() * 3) + 1); 
 
     var maxY = d3.max(data, function(d) { 
 
     return d[1]; 
 
     }) 
 
     var minY = d3.min(data, function(d) { 
 
     return d[1]; 
 
     }) 
 
     var incrementY = Math.floor((Math.random() * 1) + 16); 
 
     var xRangeMin1, xRangeMax1, dataPoint; 
 
     if (e.direction === 'left') { 
 
     incrementX = incrementX * -1; 
 
     dataPoint = minX + incrementX; 
 
     // log('dataPoint ', dataPoint); 
 

 
     //xRangeMin1 = d3.min(scales.x.range()) - Math.abs(scales.x(minX) - scales.x(dataPoint)); 
 
     xRangeMin1 = scales.x(dataPoint); 
 
     xRangeMax1 = d3.max(scales.x.range()); 
 
     } else { 
 
     dataPoint = maxX + incrementX; 
 
     // log('dataPoint ', dataPoint); 
 

 
     //xRangeMax1 = d3.max(scales.x.range()) + (scales.x(dataPoint) - scales.x(maxX)); 
 
     xRangeMax1 = d3.max(scales.x.range()) + 20; //scales.x(dataPoint); 
 
     xRangeMin1 = d3.min(scales.x.range()) //e.translate[0]; 
 

 
     } 
 
     data.push([dataPoint, incrementY]); 
 

 
     //scales = defineScales(data, width + incrementX, height); 
 
     //    scales.x = defineXScale(data, [], [xRangeMin1, xRangeMax1]); 
 
     //    scales.y = defineYScale(data, [], [height, 0]); 
 

 
     scales.x.domain(d3.extent(data, function(d) { 
 
     return d[0]; 
 
     })); 
 
     x = scales.x; 
 
     y = scales.y; 
 
     xAxis = renderXAxis(scales.x, main, height); 
 
     // draw the y axis 
 
     yAxis = renderYAxis(scales.y, main); 
 
     zoom.x(scales.x).y(scales.y); 
 

 

 
    } 
 
    var zoomRect = addZoomRect(main, scales, zoom); 
 

 

 
    renderScatterplot(main, data, scales); 
 

 
    }); 
 
})(window.chartBuilder);
/* Styles go here */ 
 

 
.chart { 
 
    font-family: Arial, sans-serif; 
 
    font-size: 10px; 
 
} 
 

 
.axis path, .axis line { 
 
    fill: none; 
 
    stroke: #000; 
 
    shape-rendering: crispEdges; 
 
} 
 

 
.bar { 
 
    fill: steelblue; 
 
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> 
 
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

Tôi đã tạo ra zoom.onXScrollEnd chức năng để thêm điểm mới cho dữ liệu.

Hy vọng điều đó sẽ hữu ích.

3

Như đã đề cập trong câu trả lời khác, tôi biết đây là một bài rất cũ nhưng hy vọng sau đây sẽ giúp ai đó ...

tôi đã thực hiện một pen mà tôi nghĩ rằng tấn công tất cả các yêu cầu nêu. Vì tôi không có API thực sự để sử dụng, tôi đã tạo một số dữ liệu bằng cách sử dụng một công cụ tuyệt vời json-generator, bao gồm nó và sắp xếp nó theo thứ tự giảm dần. Sau đó, tôi sử dụng phương pháp được xây dựng trong các phương pháp sliceconcat để lấy bit của mảng, data và thêm vào biến số chart_data (tương tự như cách người ta có thể sử dụng api).

mục quan trọng:

Khi bạn đã tạo bạn cân, rìu, và điểm (đường, quán bar, vv), bạn cần phải tạo ra các zoom behavior. Như đã đề cập trong câu hỏi, giữ scaleExtent giới hạn ở số lượng tương tự ở cả hai bên ngăn cản phóng to:

var pan = d3.behavior.zoom() 
    .x(x_scale) 
    .scale(scale) 
    .size([width, height]) 
    .scaleExtent([scale, scale]) 
    .on('zoom', function(e) { ... }); 

Bây giờ chúng ta đã tạo hành vi, chúng ta cần phải gọi nó. Tôi cũng đang tính toán những gì x dịch sẽ được cho thời điểm này trong thời gian, now và theo chương trình panning có:

// Apply the behavior 
viz.call(pan); 

// Now that we've scaled in, find the farthest point that 
// we'll allow users to pan forward in time (to the right) 
max_translate_x = width - x_scale(new Date(now)); 
viz.call(pan.translate([max_translate_x, 0]).event); 

Cả ngăn chặn người dùng từ di chuyển qua bây giờ và tải nhiều dữ liệu hơn là tất cả được thực hiện trong trường hợp zoom handler:

... 
.scaleExtent([scale, scale]) 
.on('zoom', function(e) { 
    var current_domain = x_scale.domain(), 
     current_max = current_domain[1].getTime(); 

    // If we go past the max (i.e. now), reset translate to the max 
    if (current_max > now) 
     pan.translate([max_translate_x, 0]); 

    // Update the data & points once user hits the point where current data ends 
    if (pan.translate()[0] > min_translate_x) { 
     updateData(); 
     addNewPoints(); 
    } 

    // Redraw any components defined by the x axis 
    x_axis.call(x_axis_generator); 
    circles.attr('cx', function(d) { 
     return x_scale(new Date(d.registered)); 
    }); 
}); 

Các chức năng khác khá đơn giản và có thể tìm thấy ở cuối bút. Tôi không biết về bất kỳ chức năng D3 tích hợp nào để ngăn chặn việc lướt qua hiện tại nhưng tôi chắc chắn sẽ mở ra phản hồi nếu tôi đã bỏ lỡ một cách dễ dàng hơn để thực hiện một số việc này.

Hãy cho tôi biết nếu bạn gặp sự cố khi xem pen hoặc cần làm rõ về điều gì đó. Nếu tôi có thời gian tôi sẽ cập nhật điều này với một phiên bản khác giới thiệu một biểu đồ đường cuộn vô hạn.

P.S. Trong bút, tôi an ủi việc lựa chọn và dữ liệu khi chúng cập nhật. Tôi đề nghị mở giao diện điều khiển để xem chính xác những gì đang xảy ra.

More Ví dụ:

Line Chart Addition - thêm một hình ảnh động trên tải dữ liệu mới có thể làm cho nó một chút quyến rũ

+0

Bạn có thể vui lòng cập nhật liên kết cho ví dụ về Biểu đồ đường của bạn. Có vẻ như liên kết đã hết hạn. Tôi cũng đang làm việc trên dự án tương tự nên chỉ muốn kiểm tra xem đây có phải là những gì tôi yêu cầu không. –

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