【发布时间】:2015-05-11 15:38:33
【问题描述】:
我正在使用 d3.js 构建一种时间线工具,可以在设定的时间段内向前和向后缩放和翻页。我的轴完美过渡,但我似乎无法让我的数据重新加载。这个想法是缩放级别定义了一个移动窗口,例如用户在几天内前后移动的 1 小时。每次他们这样做时,都会调用以获取该小时的数据。
为大代码块道歉,处理新数据获取的方法位于底部。如果有任何不同,该工具就是 angular.js 的指令。
scope.zoomLevels = [
{
value: 1,
unit: 'minutes',
tickFormat: $window.d3.time.format('%S')
},
{
value: 30,
unit: 'minutes',
tickFormat: $window.d3.time.format('%H:%M')
},
{
value: 1,
unit: 'hours',
tickFormat: $window.d3.time.format('%H:%M')
},
{
value: 3,
unit: 'hours',
tickFormat: $window.d3.time.format('%H:%M')
},
{
value: 6,
unit: 'hours',
tickFormat: $window.d3.time.format('%H:%M')
},
{
value: 12,
unit: 'hours',
tickFormat: $window.d3.time.format('%H:%M')
},
{
value: 1,
unit: 'days',
tickFormat: $window.d3.time.format('%H:%M')
}
];
var margin = {top: 0, right: 0, bottom: 0, left: 0},
width = $window.innerWidth,
height = $window.innerHeight - margin.top - margin.bottom,
xAxisHeight = $window.innerHeight - margin.top - ($window.innerHeight/2);
var start = $window.moment(Datasets.current.dates[0], 'DD/MM/YYYY'),
end = $window.moment(Datasets.current.dates[Datasets.current.dates.length - 1], 'DD/MM/YYYY'),
duration = $window.moment.duration(end.diff(start));
scope.zoomLevel = 3;
scope.page = 0;
scope.maxPage = duration.asMinutes()/scope.zoomLevels[scope.zoomLevel].value;
//$scope.tweets = Tweets.getTweets(start.format('x'), end.format('x'));
var rows = Math.floor(height/ 60);
console.log(rows + ' Rows');
var x = $window.d3.time.scale()
.domain([start.toDate(), start.clone().add(scope.zoomLevels[scope.zoomLevel].value, scope.zoomLevels[scope.zoomLevel].unit).toDate()])
.range([0, width]);
var y = $window.d3.scale.linear()
.domain([(rows/2) * -1,(rows/2)])
.range([height, 0]);
var xAxis = $window.d3.svg.axis()
.scale(x);
var yAxis = $window.d3.svg.axis()
.scale(y)
.tickSize(5)
.orient('right');
var svg = $window.d3.select('#timeline').append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height)
.append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
svg.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(0,' + xAxisHeight + ')')
.call(xAxis)
.selectAll('text')
.attr('y', 15)
.attr('x', 0)
.attr('dy', '.35em');
svg.append('g')
.attr('class', 'y axis')
.call(yAxis);
Articles.get({startDate: start.format(), endDate: start.clone().add(scope.zoomLevels[scope.zoomLevel].value, scope.zoomLevels[scope.zoomLevel].unit).format(), limit: 20}, function(data){
var bar = svg.selectAll('.news')
.data(data.items)
.enter()
.append('g')
.attr('class', 'news')
.on('mouseover', function(d) {
console.log(d);
})
.on('mouseout', function(d) {
});
bar.append('rect')
.attr('width', 250)
.attr('height', 60)
.attr('x', function(d){return x($window.moment(d.date, 'YYYY-MM-DDTHH:mm:ss.000Z').format('x'));})
.attr('y', function(d){return y(2);})
bar.append('foreignObject')
.attr('width', 250)
.attr('height', 60)
.attr('x', function(d){return x($window.moment(d.date, 'YYYY-MM-DDTHH:mm:ss.000Z').format('x'));})
.attr('y', function(d){return y(2);})
.append('xhtml:p')
.attr("class","statement")
.text(function(d) { return d.title; });
});
scope.movePage = function(direction){
if(!(direction === 1 && scope.page === scope.maxPage) || !(direction === -1 && scope.page === 0)){
console.log('Paging allowed');
if(direction === 1){
start = start.add(scope.zoomLevels[scope.zoomLevel].value, scope.zoomLevels[scope.zoomLevel].unit);
x.domain([start.toDate(), start.clone().add(scope.zoomLevels[scope.zoomLevel].value, scope.zoomLevels[scope.zoomLevel].unit).toDate()]);
Articles.get({startDate: start.format(), endDate: start.clone().add(scope.zoomLevels[scope.zoomLevel].value, scope.zoomLevels[scope.zoomLevel].unit).format(), limit: 20}, function(data) {
console.log(data.items.length);
svg.selectAll('.news').data(data.items).transition().duration(1500).ease('sin-in-out');
svg.selectAll('g.axis.x').transition().duration(1500).ease('sin-in-out').call(xAxis);
});
scope.page++;
}
if(direction === -1){
start = start.subtract(scope.zoomLevels[scope.zoomLevel].value, scope.zoomLevels[scope.zoomLevel].unit);
x.domain([start.toDate(), start.clone().add(scope.zoomLevels[scope.zoomLevel].value, scope.zoomLevels[scope.zoomLevel].unit).toDate()]);
svg.selectAll('g.axis.x').transition().duration(1500).ease('sin-in-out').call(xAxis);
scope.page--;
}
}
};
scope.toggleZoom = function(direction){
if(!(direction === 1 && scope.zoomLevel === 6) || !(direction === -1 && scope.zoomLevel === 0)){
duration = $window.moment.duration(end.diff(start));
scope.zoomLevel = scope.zoomLevel + direction;
console.log(scope.zoomLevels[scope.zoomLevel].unit);
switch (scope.zoomLevels[scope.zoomLevel].unit){
case 'minutes':
scope.maxPage = duration.asMinutes()/scope.zoomLevels[scope.zoomLevel].value;
console.log(duration.asMinutes() + ' Minutes');
console.log((duration.asMinutes()/scope.zoomLevels[scope.zoomLevel].value) + ' Pages');
break;
case 'hours':
scope.maxPage = duration.asHours()/scope.zoomLevels[scope.zoomLevel].value;
console.log(duration.asHours() + ' Hours');
console.log((duration.asHours()/scope.zoomLevels[scope.zoomLevel].value) + ' Pages');
break;
case 'days':
scope.maxPage = duration.asDays()/scope.zoomLevels[scope.zoomLevel].value;
console.log(duration.asDays() + ' Days');
console.log((duration.asDays()/scope.zoomLevels[scope.zoomLevel].value) + ' Pages');
break;
}
x.domain([start.toDate(), start.clone().add(scope.zoomLevels[scope.zoomLevel].value, scope.zoomLevels[scope.zoomLevel].unit).toDate()]);
svg.selectAll('g.axis').transition().duration(1500).ease('sin-in-out').call(xAxis);
}
};
更新 #1
我已经能够在 console.log(data.items.length) 行之后使用 Articles.get 回调中的以下代码来显示新数据并删除旧数据
var news = svg.selectAll(".news").data(data.items);
// Add new element
news.enter()
.append("g")
.attr("class", "news")
.transition()
.duration(1500)
.ease("sin-in-out");
news.append('rect')
.attr('width', 250)
.attr('height', 60)
.attr('x', function(d){return x($window.moment(d.date, 'YYYY-MM-DDTHH:mm:ss.000Z').format('x'));})
.attr('y', function(d){return y(2);});
news.append('foreignObject')
.attr('width', 250)
.attr('height', 60)
.attr('x', function(d){return x($window.moment(d.date, 'YYYY-MM-DDTHH:mm:ss.000Z').format('x'));})
.attr('y', function(d){return y(2);})
.append('xhtml:p')
.attr("class","statement")
.text(function(d) { return d.title; });
// Remove old elements
news.exit()
.attr("class", "news")
.transition()
.duration(1500)
.ease("sin-in-out")
.remove();
但是动画并没有真正起作用。最终,如果用户单击下一页,我希望代表每个新闻项目的 svg 元素以与轴移动相同的速度向左移动。目前,当用户单击前进和后退按钮时,它们会立即出现/消失。
【问题讨论】:
-
console.log(data.items.length);有没有发生过?Paging allowed怎么样? -
是的,它们都发生了。 svg.selectAll('g.axis.x').transition().duration(1500).ease('sin-in-out').call(xAxis);同样发生的情况是,轴会用轴域上的新开始日期和结束日期重绘
-
而
data.items.length是 20? -
并非总是如此,最多可以是 20 个。它从数据库中提取国际新闻头条,每个都带有时间戳并绘制在图表上。
-
好的,很酷,只需检查它是否大于零。我想你必须调试你的 svg.selectAll('.news') 行。也许添加一个
$scope.debug = data.items,然后你可以放一个{{ debug | json }} 在你的 html 中的某个地方,看看来自 API 的数据是否有问题
标签: javascript angularjs d3.js