响应D3缩放行为



我正在使用D3制作一个利用可缩放行为的以时间为中心的图表。我正在遵循Mike Bostock的"迈向可重用图表"文章中列出的模式,并且,此外,试图使所述图表响应。

可重用的图表模式允许我简单地在setInterval中调用图表来处理响应性。一些值,如宽度,在每次调用时更新,其他值封装在闭包中,仅在初始图表创建时设置。每次调用都可能需要更新的值之一是刻度的范围。

此外,根据https://github.com/mbostock/d3/wiki/Zoom-Behavior,修改由缩放行为自动调整的比例的域或范围需要(重新)指定缩放行为的比例(此外,缩放行为的比例和转换值将被重置)。

然而,以下是我通过修改缩放范围(并使用最新的缩放和翻译值更新缩放)重新指定缩放行为获得的结果:

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>

我可以通过最小限度地重新指定缩放行为的比例来非常接近。下面的工作,直到我调整窗口的大小,然后缩放焦点不再与鼠标对齐:

http://jsfiddle.net/xf3fk8hu/1/

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 isZoomControllingScale = false;
    var xAxis = d3.svg.axis().scale(xScale).ticks(5);
    var currentScale = 1;
    var currentTranslate = [0, 0];
    var zoom = d3.behavior.zoom().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]);
            if(!isZoomControllingScale) {
                isZoomControllingScale = true;
            	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://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>

如何以响应性和可重用的方式使用缩放行为?

我通过在检测到宽度变化后重新指定缩放行为的比例,解决了缩放焦点在调整大小后不与鼠标对齐的问题。但是,新宽度仍然有一个问题,在调整大小时导致错误的翻译。我在d3中找到了答案,在重置范围后保留比例/翻译,这使我找到了解决方案:

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>

最新更新