问题
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: £" + 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