d3 chart redraw not working on window resize

﹥>﹥吖頭↗ 提交于 2019-12-25 01:34:32

问题


I'm displaying several equivalent line charts using d3, that I'm building using a single chart() function, which accepts all the chart data.

This chart() function is called as required by a draw() function, which loops through all the placeholder chart elements on the page.

I want to redraw all charts when the viewport is resized. So, to do this I'm removing the existing charts using d3.select("svg").remove();, then calling my draw() function again.

Unfortunately, when the charts are redrawn, not all elements are correct - my path does not appear (although it is present in the mark up), and some other circle objects also do not show). I don't know why this is.

Please see the codepen I've created. Also, here is the relevant code:

function debounce(func, wait, immediate) {
    var timeout;
    return function() {
        var context = this, args = arguments;
        var later = function() {
            timeout = null;
            if (!immediate) func.apply(context, args);
        };
        var callNow = immediate && !timeout;
        clearTimeout(timeout);
        timeout = setTimeout(later, wait);
        if (callNow) func.apply(context, args);
    };
}

var data = [], promos = [], stocks = [], start = [], end = [];

data['chart-7114'] = [{"date":"2017-08-14","value":"5.0000"},{"date":"2017-08-16","value":"5.0000"},{"date":"2017-08-17","value":"5.0000"},{"date":"2017-08-24","value":"5.0000"},{"date":"2017-08-31","value":"5.0000"},{"date":"2017-09-13","value":"5.0000"},{"date":"2017-09-14","value":"5.0000"},{"date":"2017-09-25","value":"6.4500"},{"date":"2017-09-26","value":"6.4500"},{"date":"2017-09-27","value":"6.4500"},{"date":"2017-09-28","value":"6.4500"}];
                promos['chart-7114'] = [{"start_date":"2017-08-14","end_date":"2017-08-16"},{"start_date":"2017-08-24","end_date":"2017-08-24"},{"start_date":"2017-09-13","end_date":"2017-09-14"}];
                stocks['chart-7114'] = [{"start_date":"2017-08-16","end_date":"2017-08-16"},{"start_date":"2017-09-14","end_date":"2017-09-14"},{"start_date":"2017-09-26","end_date":"2017-09-26"}];
                start['chart-7114'] = "2017-08-14";
                end['chart-7114'] = "2017-09-28";


$(document).ready(function() {

function chart(selector, data, promos, stocks, start, end) {

    var margin = {top: 20, right: 20, bottom: 30, left: 60},
        width = $(selector).width() - margin.left - margin.right,
        height = $(selector).height() - margin.top - margin.bottom;

    var svg = d3.select(selector).append("svg")
        .attr("width", width + margin.left + margin.right)
        .attr("height", height + margin.top + margin.bottom);

    g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");

    var parseDate = d3.timeParse("%Y-%m-%d"),
        formatDate = d3.timeFormat("%Y"),
        formatDate2 = d3.timeFormat("%d/%m/%Y");

    var x = d3.scaleTime()
        .domain([parseDate(start), parseDate(end)])
        .range([0, width]);

    var y = d3.scaleLinear()
        .range([height, 0]);

    var xAxis = d3.axisBottom(x);
    var yAxis = d3.axisLeft(y);

    var area = d3.line()
        .curve(d3.curveLinear)
        .y(function(d) { return y(d.value); });

    var areaPath = g.append("path")
        .attr('class', 'line')
        .attr("clip-path", "url(#clip)");

    var yGroup = g.append("g");

    var xGroup = g.append("g")
        .attr("transform", "translate(0," + height + ")");

    var tip = d3.tip()
        .attr('class', 'd3-tip')
        .offset([-10, 0])
        .html(function(d) {
            return "Date: " + formatDate2(d.date) + "<br>Price: &pound;" + d.value;
        });

    g.call(tip);

    var promoHighlights = g.selectAll('.promo')
        .data(promos)
        .enter().append("rect")
            .attr('class', 'promo' )
            .style("pointer-events","all")
            .attr("height", height);


    var stockHighlights = g.selectAll('.nostock')
        .data(stocks)
        .enter().append("rect")
            .attr('class', 'nostock' )
            .style("pointer-events","all")
            .attr("height", height);

    var zoom = d3.zoom()
        .scaleExtent([1 / 4, 8])
        .translateExtent([[-width, -Infinity], [2 * width, Infinity]])
        .on("zoom", zoomed);

    var zoomRect = g.append("rect")
        .attr("width", width + margin.left + margin.right)
        .attr("height", height + margin.top + margin.bottom)
        .attr("fill", "none")
        .attr("pointer-events", "all")
        .call(zoom);

    var toolTips = g.selectAll("circle")
        .data(data).enter()
        .append("circle")
            .attr("r", 4)
            .style("pointer-events","all")
            .on('mouseover', function(d, e) {
                tip.show(d);
                d3.select(this).attr('r', 8);
            })
            .on('mouseout', function(d, e) {
                tip.hide(d);
                d3.select(this).attr('r', 4);
            });

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

    data.forEach(function(d) {
        d.date = parseDate(d.date);
        d.value = +d.value;
    });

    var xExtent = d3.extent(data, function(d) { return d.date; });
    zoom.translateExtent([[x(xExtent[0]), -Infinity], [x(xExtent[1]), Infinity]]);
    y.domain([0, d3.max(data, function(d) { return d.value; })+1]);
    yGroup.call(yAxis).select(".domain").remove();
    areaPath.datum(data);
    zoomRect.call(zoom.transform, d3.zoomIdentity);
    toolTips.attr("cy", function(d) { return y(d.value); });

    function zoomed() {
        var xz = d3.event.transform.rescaleX(x);
        xGroup.call(xAxis.scale(xz));
        areaPath.attr("d", area.x(function(d) { return xz(d.date); }));
        toolTips.attr("cx", function(d) { return xz(d.date); });
        promoHighlights.attr("x", function(d) { return xz(parseDate(d.start_date)); })
            .attr("width", function(d) {
                if(d.end_date != d.start_date) {
                    //console.log(parseDate(d.end_date));
                    return xz(parseDate(d.end_date))-xz(parseDate(d.start_date));
                }
                else {
                    return xz(d3.timeDay.offset(parseDate(d.end_date)))-xz(parseDate(d.start_date));
                }
            });
        stockHighlights.attr("x", function(d) {
                return xz(parseDate(d.start_date));
            })
            .attr("width", function(d) {
                if(d.end_date != d.start_date) {
                    //console.log(parseDate(d.end_date));
                    return xz(parseDate(d.end_date))-xz(parseDate(d.start_date));
                }
                else {
                    return xz(d3.timeDay.offset(parseDate(d.end_date)))-xz(parseDate(d.start_date));
                }
            });
    }

}

function draw() {
    $('.chart').each(function() {
        chart_id = $(this).attr('id');
        chart('#'+$(this).attr('id'), data[chart_id], promos[chart_id], stocks[chart_id], start[chart_id], end[chart_id]);
    });
}

if($('.chart').length > 0) {
    draw();

    $(window).resize(debounce(function(){
        d3.select("svg").remove();
        draw();
    },100));
}

});

回答1:


You must look in this example. code on jsbin

function render() {
    updateDimensions(window.innerWidth);   
    x.range([0, width]);
    y.range([height, 0]);
    svg
      .attr('width', '100%')
      .attr('height', height + margin.top + margin.bottom);
    fh = svg.style("height").replace("px", "");
    fw = svg.style("width").replace("px", "");
    chartWrapper
      .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');           
    xAxis.scale(x);
    yAxis.scale(y);
    if(window.innerWidth < wbreakPoint) {
      xAxis.ticks(d3.timeHour.every(12))
    }
    else {
      xAxis.ticks(d3.timeHour.every(5))
    };
    if(window.innerinnerHeight < hbreakPoint) {   
      yAxis.ticks(Math.max(height/50, 2))
    }
    else {
      yAxis.ticks(Math.max(height/50, 2));
    }; 
    svg.select('.x.axis')
        .attr('transform', 'translate(0,' + height + ')')
        .transition()
        .duration(200)
        .call(xAxis)
        .selectAll("text")  
            .style("text-anchor", "end")
            .attr("dx", "-.8em")
            .attr("dy", ".15em")
            .attr("transform", "rotate(-50)" ); 
    svg.select('.y.axis')
        .transition()
        .duration(200)
        .call(yAxis);
    chartWrapper.select("text")
        .attr("x", (fw / 2))             
        .attr("y", 0 - (margin.top / 2))
        .attr("text-anchor", "middle");     
    path
        .transition()
        .duration(1000)
        .attr('d',  d => lineGen(d.Data))
        .attr("stroke-width", 3)
        .attr("fill", "none")
        .attr("stroke", (d, i) => colors(i));
    groups.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
    groups
        .transition()
        .duration(1000);} 
function updateDimensions(winWidth) {
    margin.top = 40;
    margin.right = winWidth < wbreakPoint ? 50 : 80;
    margin.left = winWidth < wbreakPoint ? 30 : 50;
    margin.bottom = 100;
    width = winWidth - margin.left - margin.right;
    height = .1 * width;}
return {render : render}
})(window,d3);
window.addEventListener('resize', Chart.render);

Just delete some strings for better understanding

var inter = setInterval(function() {
            updateData(jsont1);

Or look on this link with responsive scatterplot

Hope it helps




回答2:


I eventually identified the issue - it was this loop:

data.forEach(function(d) {
    d.date = parseDate(d.date);
    d.value = +d.value;
});

Essentially, this was updating the date values in my array. As it was part of the chart() function, it was trying to process the dates twice, which was then effectively changing their value to null - and breaking the data.



来源:https://stackoverflow.com/questions/46486646/d3-chart-redraw-not-working-on-window-resize

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!