如何修复更改浏览器选项卡时不同步的可视化?

How to fix viz that is getting out of sync when I change browser tabs?

我正在使用 this jsFiddle 作为模板,我注意到当我更改浏览器选项卡并返回时,图表会不同步(彩色线条不会移动,但 x -轴偏移)。

我可以在 d3 中添加什么,以便在我更改浏览器选项卡然后更改回可视化项选项卡时同时移动可视化项的线条和时间轴?完整代码如下:

<!DOCTYPE html>
<meta charset="utf-8">
<html>
<head>
    <title>Live Pressure Data</title>
    <style>
        svg {
            font: 12px sans-serif;
        }

        .line {
            fill: none;
            stroke: black;
            stroke-width: 1.5px;
        }

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

        .y.axis path {
            display: none;
        }

        .y.axis line {
            stroke: #777;
            stroke-dasharray: 2px,2px;
        }

        .grid .tick {
            stroke: lightgrey;
            opacity: 0.7;
        }

        .grid path {
            stroke-width: 0;
        }
    </style>
    <script src="http://d3js.org/d3.v3.js" charset="utf-8"></script>
    <!--<script src="/socket.io/socket.io.js"></script>-->
</head>
<body>
    <script>
        var n = 120;
        var nSensors = 4;
        var duration = 1000;

        var now = new Date(Date.now() - duration);
        // 1 x nSensors array of zeros
        var count = d3.range(nSensors).map(function () {
            return 0;
        });
        // nSensors x n array of zeros
        var data = count.map(function () {
            return d3.range(n).map(function () {
                return 0;
            });
        });

        var margin = { top: 20, right: 10, bottom: 20, left: 20 };
        var width = 800 - margin.right - margin.left;
        var height = 300 - margin.top - margin.bottom;

        var x = d3.time.scale()
                       .domain([now - (n - 2) * duration, now - duration])
                       .range([0, width]);

        var y = d3.scale.linear()
                        .domain([0, 100])
                        .range([height, 0]);

        var line = d3.svg.line()
                         .interpolate("basis")
                         .x(function (d, i) { return x(now - (n - 1 - i) * duration); })
                         .y(function (d, i) { return y(d); });

        var color = d3.scale.category10();

        var svg = d3.select("body").append("svg")
                    .attr("class", "lineChart")
                    .attr("width", width + margin.left + margin.right)
                    .attr("height", height + margin.top + margin.bottom)
                    .append("g")
                    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

        svg.append("defs").append("clipPath")
           .attr("id", "clip")
           .append("rect")
           .attr("width", width)
           .attr("height", height);

        var yAxis = d3.svg.axis()
                          .scale(y)
                          .tickSize(width)
                          .orient("right")

        var gy = svg.append("g")
                    .attr("class", "y axis")
                    .call(yAxis)

        gy.selectAll("text")
          .attr("text-anchor", "end")
          .attr("x", 4)
          .attr("dy", -4)

        var xAxis = svg.append("g")
                       .attr("class", "x axis")
                       .attr("transform", "translate(0," + height + ")")
                       .call(x.axis = d3.svg.axis().scale(x).orient("bottom"));

        var clipPath = svg.append("g")
                          .attr("clip-path", "url(#clip)");

        var paths = clipPath.append("g")

        for (var series in data) {
            paths.append("path")
                 .attr("class", "line")
                 .data([data[series]])
                 .style("stroke", color(series))
        }

        // Live bar graph
        var barW = 300 - margin.left - margin.right;
        var barH = 190 - margin.top - margin.bottom;
        var rectH = 28;

        var barX = d3.scale.linear()
                               .domain([0, 100])
                               .range([0, barW]);

        var barSvg = d3.select("body").append("svg")
                       .attr("class", "barChart")
                       .attr("width", barW + margin.right + margin.left)
                       .attr("height", barH + margin.top + margin.bottom)
                       .append("g")
                       .attr("transform", "translate(" + margin.left + "," + margin.top + ")")

        barSvg.selectAll("rect")
              .data(count)
              .enter().append("rect")
              .attr("y", function (d, i) { return i * 38; })
              .attr("width", barX)
              .style("fill", function (d, i) { return color(i); })
              .attr("height", rectH);

        barSvg.selectAll("text")
              .data(count)
              .enter().append("text")
              .attr("x", 50)
              .attr("y", function (d, i) { return (i * 38) + 14 })
              .attr("dy", "0.35em")
              .attr("text-anchor", "end")
              .text(function (d) { return d + " psi" })

        barSvg.append("g")
              .attr("class", "x axis")
              .attr("transform", "translate(0," + barH + ")")
              .call(d3.svg.axis().scale(barX).orient("bottom"))

        // Live data
        //var socket = io.connect('');

        //socket.on('news', function (pressure) {
        //    oldCount = count;
        //    count = pressure.values;
        //})

        setInterval(function () {
            for (var series in count) {
                count[series] += (Math.random() - 0.5) * 5;
                count[series] = Math.min(Math.max(count[series], 0), 100);
            }
        }, 900);


        // Animate
        tick();

        function tick() {

            // update the domains
            now = new Date();
            x.domain([now - (n - 2) * duration, now - duration]);

            for (var series in data) {
                data[series].push(count[series]);
            }

            // slide the x-axis left
            xAxis.transition()
                 .duration(duration)
                 .ease("linear")
                 .call(x.axis);

            // redraw the line
            svg.selectAll(".line")
               .attr("d", line)

            // slide the line left
            paths.attr("transform", null)
                 .transition()
                 .duration(duration)
                 .ease("linear")
                 .attr("transform", "translate(" + x(now - (n - 1) * duration) + ")")
                 .each("end", tick);

            // pop the old data point off the front
            for (var series in data) {
                data[series].shift();
            }

            // bar animation
            barSvg.selectAll("rect")
                  .data(count)
                  .transition()
                  .duration(duration)
                  .attr("width", barX);

            barSvg.selectAll("text")
                  .data(count)
                  .text(function (d) { return Math.floor(d) + " psi" })
        }

    </script>
</body>
</html>

您的转换效果很差。

如果您查看示例 here 的源代码,您会注意到 tick 函数中的这条奇怪的行:

function tick() {
    transition = transition.each(function() {
    ...

根据docs

If type is not specified, behaves similarly to selection.each: immediately invokes the specified function for each element in the current transition, passing in the current datum d and index i, with the this context of the current DOM element. Any transitions created within the scope of transition.each will inherit transition parameters from the parent transition, including id, delay, duration and easing. Thus, transitions created within a transition.each will not interrupt the parent transition, similar to subtransitions.

The transition.each method can be used to chain transitions and apply shared timing across a set of transitions.

[加粗我的]

将此应用于您的 example,似乎可以解决您的问题。

请注意,我必须更改您的 paths 变量。在我结束过渡后,当包含在外部 g 元素中时,它不再是平滑过渡。