我正在尝试创建一个图表,根据所选的时间间隔(小时,天,周,月)显示CSV中的数据。我可以动态更新我的比例,但每次如果我缩小(第一个)然后放大(严格按此顺序),我的数据点就越来越近了。以下是我的代码。任何人都可以建议我哪里出错了?谢谢!
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;
d.Interval = d.level;
});
// 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 mindate = d3.min(data, function (d) { return d.date; });
var minDate = mindate.getDate();
var minMonth = mindate.getMonth();
var minYear = mindate.getFullYear();
var minHour = mindate.getHours();
var mindate = new Date(minYear, minMonth, minDate, minHour);
var maxdate = new Date(minYear, minMonth, minDate, minHour + 5);
console.log(mindate);
console.log(maxdate);
var xScale = 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();
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();
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') {
console.log('hours, hour');
return 'translate(' + xScale(d.date) + ', ' + yScale(d.Ranking) + ')';
}
if (interval == 'days') {
console.log('days, day');
return 'translate(' + xDaysScale(d.date) + ', ' + yScale(d.Ranking) + ')';
}
if (interval == 'weeks') {
console.log('weeks, week');
return 'translate(' + xWeeksScale(d.date) + ', ' + yScale(d.Ranking) + ')';
}
if (interval == 'months') {
console.log('months, month');
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();
});