当前位置: 动力学知识库 > 问答 > 编程问答 >

javascript - Responsive D3 zoom behavior

问题描述:

I am using D3 to make a time-focused chart that makes use of the zoomable behavior. I'm following the pattern laid out in Mike Bostock's "Towards Reusable Charts" article, and, in addition, trying to make said chart responsive.

The reusable chart pattern allows me to simply call my chart in a setInterval to handle responsiveness. Some values, like width, are updated each call, others are encapsulated in a closure and set only on initial chart creation. One of the values that needs potential updating every call is the scale's range.

Furthermore, according to https://github.com/mbostock/d3/wiki/Zoom-Behavior, modifying the domain or range of a scale that is being automatically adjusted by a zoom behavior necessitates (re)specifying the scale to the zoom behavior (in addition, the zoom behavior's scale and translate values will be reset).

However, the following is the result I get by respecifying the scale to the zoom behavior whenever I modify the scale's range (and also updating the zoom with the latest scale and translate values):

http://jsfiddle.net/xf3fk8hu/

function test(config) {

var aspectRatio = 10 / 3;

var margin = { top: 0, right: 0, bottom: 30, left: 0 };

var current = new Date();

var xScale = d3.time.scale().domain([d3.time.year.offset(current, -1), current]);

var xAxis = d3.svg.axis().scale(xScale).ticks(5);

var currentScale = 1;

var currentTranslate = [0, 0];

var zoom = d3.behavior.zoom().x(xScale).on('zoom', function() {

currentScale = d3.event.scale;

currentTranslate = d3.event.translate;

d3.select(this.parentNode.parentNode.parentNode).call(result);

});

var result = function(selection) {

selection.each(function(data) {

var outerWidth = $(this).width();

var outerHeight = outerWidth / aspectRatio;

var width = outerWidth - margin.left - margin.right;

var height = outerHeight - margin.top - margin.bottom;

xScale.range([0, width]);

zoom.x(xScale).scale(currentScale).translate(currentTranslate);

var svg = d3.select(this).selectAll('svg').data([data]);

var svgEnter = svg.enter().append('svg');

svg.attr('width', outerWidth).attr('height', outerHeight); var gEnter = svgEnter.append('g'); var g = svg.select('g').attr('transform', 'translate(' + margin.left + ' ' + margin.top + ')');

gEnter.append('rect').attr('class', 'background').style('fill', '#F4F4F4').call(zoom);

g.select('rect.background').attr('width', width).attr('height', height);var rectItem = g.selectAll('rect.item').data(function(d) {

return d;

});rectItem.enter().append('rect').attr('class', 'item').style('fill', '#00F');rectItem.attr('x', function(d) {

return xScale(d);

}).attr('width', xScale(d3.time.day.offset(xScale.invert(0), 7))).attr('height', height);

gEnter.append('g');

g.select('g').attr('transform', 'translate(0 ' + height + ')').call(xAxis);

});

};

return result;

}

setInterval(function() {

var selection = d3.select('#main').datum(d3.range(5).map(function() {

var current = new Date();

var mean = -d3.time.minute.range(current, d3.time.month.offset(current, 6)).length;

var deviation = d3.time.minute.range(current, d3.time.month.offset(current, 1)).length;

var random = d3.random.normal(mean, deviation);

return function() {

return d3.time.minute.offset(current, random());

};

}()));

var myTest = test();

return function() {selection.call(myTest);};

}(), 1000 / 60);

<div id="main"></div>

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

网友答案:

I solved the problem of the zoom focus not lining up with the mouse after resizing, by respecifying the scale to the zoom behavior only after detecting a change in width. However, there was still a problem with the new width causing an erroneous translation during resize. I found the answer to that at d3 Preserve scale/translate after resetting range, which leads me to a solution:

http://jsfiddle.net/xf3fk8hu/5/

function test(config) {
    var aspectRatio = 10 / 3;
    var margin = { top: 0, right: 0, bottom: 30, left: 0 };
    var current = new Date();
    var xScale = d3.time.scale();
    var xAxis = d3.svg.axis().scale(xScale).ticks(5);
    var zoom = d3.behavior.zoom().x(xScale).on('zoom', function() {
        currentScale = d3.event.scale;
        currentTranslate = d3.event.translate;
        d3.select(this.parentNode.parentNode.parentNode).call(result);
    });
    var currentScale = zoom.scale();
    var currentTranslate = zoom.translate();
    var oldWidth;
    var result = function(selection) {
        selection.each(function(data) {
            var outerWidth = $(this).width();
            var outerHeight = outerWidth / aspectRatio;
            var width = outerWidth - margin.left - margin.right;
            var height = outerHeight - margin.top - margin.bottom;
            if(oldWidth !== width) {
                if(oldWidth === undefined) oldWidth = width;
                currentTranslate[0] *= width / oldWidth;
                xScale.domain([d3.time.year.offset(current, -1), current]).range([0, width]);
                zoom.x(xScale).scale(currentScale).translate(currentTranslate);
            }
            oldWidth = width;

            var svg = d3.select(this).selectAll('svg').data([data]);
            var svgEnter = svg.enter().append('svg');
            svg.attr('width', outerWidth).attr('height', outerHeight);
            var gEnter = svgEnter.append('g');
            var g = svg.select('g').attr('transform', 'translate(' + margin.left + ' ' + margin.top + ')');
            gEnter.append('rect').attr('class', 'background').style('fill', '#F4F4F4').call(zoom);
            g.select('rect.background').attr('width', width).attr('height', height);

            var rectItem = g.selectAll('rect.item').data(function(d) {
                return d;
            });
            rectItem.enter().append('rect').attr('class', 'item').style('fill', '#00F');
            rectItem.attr('x', function(d) {
                return xScale(d);
            }).attr('width', xScale(d3.time.day.offset(xScale.invert(0), 7))).attr('height', height);

            gEnter.append('g');
            g.select('g').attr('transform', 'translate(0 ' + height + ')').call(xAxis);
        });
    };
    return result;
}

setInterval(function() {
    var selection = d3.select('#main').datum(d3.range(5).map(function() {
        var current = new Date();
        var mean = -d3.time.minute.range(current, d3.time.month.offset(current, 6)).length;
        var deviation = d3.time.minute.range(current, d3.time.month.offset(current, 1)).length;
        var random = d3.random.normal(mean, deviation);
        return function() {
            return d3.time.minute.offset(current, random());
        };
    }()));
    var myTest = test();
    return function() {selection.call(myTest);
    };
}(), 1000 / 60);
<div id="main"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
分享给朋友:
您可能感兴趣的文章:
随机阅读: