【问题标题】:Update X-axis via zoom scale通过缩放比例更新 X 轴
【发布时间】:2014-09-13 06:07:58
【问题描述】:

我正在尝试通过检查当前缩放比例来创建动态 x 轴时间比例。

它在第一次加载时工作,但如果我随后尝试放大或缩小图表,我的 x 轴没有响应,尽管我已将变量声明为函数并期望它返回适当的比例和轴每次调用缩放操作时。

我的代码如下。提前谢谢你。

d3.csv('data/sample.csv', function (data) {
    // sort out the data we'll be using
    data.forEach(function (d) {
        d.date = new Date(d.dateTime);
        d.Ranking = +d.weight;
    });

    // define the boundaries of the svg canvas
    var margin = {top : 30, right : 45, bottom : 30, left : 45},
    width = window.innerWidth - margin.left - margin.right,
    height = window.innerHeight - margin.top - margin.bottom;

    // define the scale for each axis
    var interval = 'hours';

    var xScale = d3.time.scale()
        .domain([d3.min(data, function (d) {
                    return d.date;
                }), d3.max(data, function (d) {
                    return d.date;
                })])
        .range([0, width])
        .nice();

    var xDaysScale = d3.time.scale()
        .domain([new Date(2014, 7, 1), new Date(2014, 7, 6)])
        .range([0, width])
        .nice();

    var xWeeksScale = d3.time.scale()
        .domain([new Date(2014, 7, 1), new Date(2014, 7, 31)])
        .range([0, width])
        .nice();

    var xMonthsScale = d3.time.scale()
        .domain([new Date(2014, 7, 1), new Date(2015, 0, 1)])
        .range([0, width])
        .nice();

    var yScale = d3.scale.linear().domain([0, 5]).range([height, 0]).nice();

    // define the axes
    var xAxis = d3.svg.axis()
        .scale(xScale)
        .ticks(d3.time.hours, 1)
        .orient('bottom');

    var yAxis = d3.svg.axis().scale(yScale).orient('left');

    // define the zoom behavior
    var zm = d3.behavior.zoom()
        .x(xScale)
        .scaleExtent([.1, 1024])
        .on('zoom', zoom);

    // initialize the tooltip, and append it to the body
    var tooltip = d3.select('body')
        .append('div')
        .attr('class', 'tooltip')
        .attr('id', 'tooltip')
        .style('opacity', 0);

    // initialize the chart
    var svg = d3.select('.graph')
        .attr('width', width + margin.left + margin.right)
        .attr('height', height + margin.top + margin.bottom)
        .append('svg:g')
        .attr('transform', 'translate(' + margin.left + ', ' + margin.top + ')');

    // append the manipulation areas to the chart
    svg.append('rect')
    .attr('class', 'zoom xy')
    .attr('width', width)
    .attr('height', height)
    .call(zm)
    .on('dblclick.zoom', null) // disable the standard shitty doubleclick zoom function
    .on('dblclick', function (d) { // use my own awesome zoom function, that resets the chart
        // reset the main scale
        zm.scale(1);
        zm.translate([0, 0]);

        // let's not forget to update the axes
        svg.select('.x.axis').call(xAxis);
        svg.select('.y.axis').call(yAxis);

        // apply the changes
        update();
    });

    // append the axes to the chart
    svg.append('svg:g')
    .attr('class', 'x axis')
    .attr('transform', 'translate(-' + width + ', ' + height + ')')
    .attr('transform', 'translate(0, ' + height + ')')
    .call(xAxis);

    // create the elements based on the data provided
    var elem = svg.selectAll()
        .data(data)
        .enter()
        .append('g')
        .attr('class', 'element')
        .attr('transform', function (d) {
            return 'translate(' + xScale(d.date) + ', ' + yScale(d.Ranking) + ')';
        });

    // we append the full text description as a HTML element, because, a text element is more expensive, and doesn't support line breaks
    elem.append('foreignObject')
    .attr('width', 200)
    .attr('height', 1000)
    .append('xhtml:div')
    .attr('class', 'fulltext')
    .html(function (d) {
        return d.content;
    })
    .style('opacity', 0);

    // function::zoom - handles the scaling, translation of the chart elements
    function zoom() {
        //svg.select('.x.axis').call(xAxis);
        //svg.select('.y.axis').call(yAxis);

        //update();

        console.log(interval);

        if (zm.scale() < 1) {
            //console.log('zooming OUT');

            if (interval == 'hours') {
                xAxis = d3.svg.axis().scale(xDaysScale).ticks(d3.time.days, 1).orient("bottom");
                zm.x(xDaysScale);
                interval = 'days';
            }
            else if (interval == 'days') {
                xAxis = d3.svg.axis().scale(xWeeksScale).ticks(d3.time.weeks, 1).orient("bottom");
                zm.x(xWeeksScale);
                interval = 'weeks';
            }
            else if (interval == 'weeks') {
                xAxis = d3.svg.axis().scale(xMonthsScale).ticks(d3.time.months, 1).orient("bottom");
                zm.x(xMonthsScale);
                interval = 'months';
            }
        }

        else if (zm.scale() > 1) {
            //console.log('zooming IN');

            if (interval == 'months') {
                xAxis = d3.svg.axis().scale(xWeeksScale).ticks(d3.time.weeks, 1).orient("bottom");              
                zm.x(xWeeksScale);
                interval = 'weeks';
            }

            else if (interval == 'weeks') {
                xAxis = d3.svg.axis().scale(xDaysScale).ticks(d3.time.days, 1).orient("bottom");
                zm.x(xDaysScale);
                interval = 'days';
            }

            else if (interval == 'days') {
                xAxis = d3.svg.axis().scale(xScale).ticks(d3.time.hours, 1).orient("bottom");
                zm.x(xScale);
                interval = 'hours';
            }
        }

        svg.select('.x.axis').call(xAxis);
        svg.select('.y.axis').call(yAxis);
        update();
    }

    // function::resize - handles the responsive positioning and sizing of elements depending on the size of the viewport
    function resize() {

        // re-get the size of the window
        width = window.innerWidth - margin.left - margin.right,
        height = window.innerHeight - margin.top - margin.bottom;

        // re-set the size of the chart's range
        xScale.range([0, width]).nice();
        yScale.range([height, 0]).nice();

        // configure the chart to the new size
        d3.select('.graph')
        .attr('width', width + margin.left + margin.right)
        .attr('height', height + margin.top + margin.bottom);

        // re-set the size of the manipulation area
        svg.select('rect')
        .attr('width', width)
        .attr('height', height);

        // re-size and re-position the axes
        svg.select('.x.axis')
        .attr('transform', 'translate(0, ' + height + ')')
        .call(xAxis);

        // update the elements according to the resized elements
        update();
    }

    // function::update - handles all redrawing of the chart and checking of dynamic elements
    function update() {
        // re-position individual elements
        svg.selectAll('.element')
        .attr('transform', function (d) {
            if (interval == 'hours') {
                return 'translate(' + xScale(d.date) + ', ' + yScale(d.Ranking) + ')';
            }

            if (interval == 'days') {
                return 'translate(' + xDaysScale(d.date) + ', ' + yScale(d.Ranking) + ')';
            }

            if (interval == 'weeks') {
                return 'translate(' + xWeeksScale(d.date) + ', ' + yScale(d.Ranking) + ')';
            }

            if (interval == 'months') {
                return 'translate(' + xMonthsScale(d.date) + ', ' + yScale(d.Ranking) + ')';
            }

            //return 'translate(' + xScale(d.date) + ', ' + yScale(d.Ranking) + ')';
        });

        d3.selectAll('.fulltext')
        .style('opacity', function (d) {
            // hide or show the full text descriptions based on zoom level
            if (zm.scale() >= 1) {
                return 1;
            } else {
                return 0;
            }
        });
    }

    // add the event handler for resizing
    d3.select(window).on('resize', resize);

    // refresh once to make sure all the processing gets through
    update();
});

【问题讨论】:

    标签: javascript d3.js


    【解决方案1】:

    声明

    xScale = returnScale() 
    

    只会调用一次 returnScale,这听起来不像你想要的。在您的 zoom() 或 update() 中,您需要再次调用 returnScale(),然后执行

    zm.x(xScale) 
    

    【讨论】:

    • 您好,Constantions,感谢您的快速回复。我试过你告诉我的。 function zoom() { console.log('你在缩放时打电话给我'); returnScale(); zm.x(xScale);返回轴(); svg.select('.x.axis').call(xAxis); svg.select('.y.axis').call(yAxis);更新();但是我的 x 轴在那之后的几天被卡住了。你能给我进一步的建议吗?
    • 我是否也需要为 xAxis 做同样的事情?
    • 我认为这个 if 条件可能是错误的: else if (zm.scale() >= 1) 应该是 else if (zm.scale()
    • 或者至少你需要一些其他条件来覆盖案例 zm.scale()
    • 感谢您指出这一点!我现在正在进行更改和测试。 :)
    【解决方案2】:
    function zoom() {
        console.log('interval: ' + interval);
        console.log('zoom: ' + zm.scale());
    
        if (zm.scale() < 1) {
            if (interval == 'hours') {
                //xAxis = xDaysAxis;
                xDaysScale = d3.time.scale().domain([new Date(2014, 7, 1), new Date(2014, 7, 6)]).range([0, width]).nice();
                xAxis = d3.svg.axis().scale(xDaysScale).ticks(d3.time.days, 1).orient("bottom");
                zm.x(xDaysScale);
                interval = 'days';
            }
            else if (interval == 'days') {
                //xAxis = xWeeksAxis;
                xWeeksScale = d3.time.scale().domain([new Date(2014, 7, 1), new Date(2014, 7, 31)]).range([0, width]).nice();
                xAxis = d3.svg.axis().scale(xWeeksScale).ticks(d3.time.weeks, 1).orient("bottom");
                zm.x(xWeeksScale);
                interval = 'weeks';
            }
            else if (interval == 'weeks') {
                //xAxis = xMonthsAxis;
                xMonthsScale = d3.time.scale().domain([new Date(2014, 7, 1), new Date(2015, 0, 1)]).range([0, width]).nice();
                xAxis = d3.svg.axis().scale(xMonthsScale).ticks(d3.time.months, 1).orient("bottom");
                zm.x(xMonthsScale);
                interval = 'months';
            }
            else {
                //zm.x(xMonthsScale);
                //interval = 'months';
            }
        }
        else if (zm.scale() > 1) {
            if (interval == 'months') {
                //xAxis = xWeeksAxis;
                xWeeksScale = d3.time.scale().domain([new Date(2014, 7, 1), new Date(2014, 7, 31)]).range([0, width]).nice();
                xAxis = d3.svg.axis().scale(xWeeksScale).ticks(d3.time.weeks, 1).orient("bottom");
                zm.x(xWeeksScale);
                interval = 'weeks';
            }
    
            else if (interval == 'weeks') {
                //xAxis = xDaysAxis;
                xDaysScale = d3.time.scale().domain([new Date(2014, 7, 1), new Date(2014, 7, 6)]).range([0, width]).nice();
                xAxis = d3.svg.axis().scale(xDaysScale).ticks(d3.time.days, 1).orient("bottom");
                zm.x(xDaysScale);
                interval = 'days';
            }
    
            else if (interval == 'days') {
                //xAxis = xHoursAxis;
                xHoursScale = d3.time.scale()
                    .domain([
                        //d3.min(data, function (d) { return d.date; }), 
                        //d3.max(data, function (d) { return d.date; })
                        mindate, maxdate
                        ])
                    .range([0, width])
                    .nice();
                xAxis = d3.svg.axis().scale(xHoursScale).ticks(d3.time.hours, 1).orient("bottom");
                zm.x(xHoursScale);
                interval = 'hours';
            }
            else {
                //zm.x(xHoursScale);
                //interval = 'hours';
            }
        }
    
        svg.select('.x.axis')
            //.attr('transform', 'translate(0, ' + height + ')')
            .call(xAxis);
        svg.select('.y.axis').call(yAxis);
    
        update();
    }
    

    【讨论】:

      猜你喜欢
      • 2015-06-10
      • 1970-01-01
      • 1970-01-01
      • 2011-09-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-27
      • 2017-09-11
      相关资源
      最近更新 更多