【发布时间】:2019-03-31 22:25:06
【问题描述】:
我花了几天时间尝试创建一个异步附加数据的实时时间线。如果不每次都破坏其他东西,似乎不可能让事情顺利进行。
我查看了几个示例,但似乎没有一个与我的情况相符。也就是说,大多数实时示例要么依赖数据每次将时间线递增一步,要么都假设数据以固定间隔连续出现。
我遇到的问题:
- 点滑动得很好。但是,如果我切换选项卡并切换回来,它们会从离开的位置继续,因此与 x 轴上的当前刻度不匹配
- 时间线中的滴答声时不时会出现一些奇怪的过渡,看起来像是在洗牌。改组后,实际点与时间线不同步。
这是fiddle
<!doctype html><html lang="en">
<head><script src="https://d3js.org/d3.v5.min.js"></script></head>
<body><script>
const LENGTH = 10 // in seconds
const TICKS = 10 // num of ticks in time axis
const HEIGHT = 240
const WIDTH = 950
const MARGIN_LEFT = 40
const MARGIN_TOP = 40
var datapoints = []
// Create root element + background rect
svg = d3.select('body').append('svg')
.attr('width', WIDTH)
.attr('height', HEIGHT)
svg.append('rect')
.attr('width', '100%')
.attr('height', '100%')
.attr('fill', 'rgba(59, 58, 52, 0.8)')
$graphs = svg.append('g')
$slidables = $graphs.append('g')
// We use two scalers for x. This solves the issue with the axis being out
// of sync
scaleX = d3.scaleTime().range([MARGIN_LEFT, WIDTH-MARGIN_LEFT])
scaleY = d3.scaleLinear().range([MARGIN_TOP, HEIGHT-MARGIN_TOP])
updateTimeScale()
// -------------------------------------------------------------------------
function logDate(date){
console.log(date.toTimeString().split(' GMT')[0] + ' ' + date.getMilliseconds() + 'ms')
}
function logPoint(point){
const date = point[0]
console.log(
date.toTimeString().split(' GMT')[0] + ' ' + date.getMilliseconds() + 'ms, ',
point[1]
)
}
function oneSecondAgo(){
d = new Date()
d.setSeconds(d.getSeconds() - 1)
return d
}
function leftDate(){
d = new Date()
d.setSeconds(d.getSeconds() - LENGTH)
return d
}
function tickDist(){
return scaleX(new Date()) - scaleX(oneSecondAgo())
}
// -------------------------------- Init -----------------------------------
/* Resets timescale to the current time */
function updateTimeScale(){
right = new Date()
left = new Date()
right.setSeconds(right.getSeconds())
left.setSeconds(right.getSeconds()-LENGTH)
scaleX.domain([left, right])
}
function init(){
// Setup axis
xaxis = d3.axisBottom(scaleX).ticks(TICKS)
$xaxis = svg.append('g')
.attr('transform', 'translate(0, ' + (HEIGHT - MARGIN_TOP) + ')')
.call(xaxis)
yaxis = d3.axisLeft(scaleY)
$yaxis = svg.append('g')
.attr('transform', 'translate(' + MARGIN_LEFT + ', 0)')
.call(yaxis)
// Garbage collect old points every second
setInterval(function(){
while (datapoints.length > 0 && scaleX(datapoints[0][0]) <= MARGIN_LEFT){
datapoints.shift()
}
$slidables.selectAll('circle')
.data(datapoints, d=>d)
.exit()
.remove()
}, 1000)
// Slide axis at interval
function tick(){
right = new Date()
left = new Date()
right.setSeconds(right.getSeconds()+1)
left.setSeconds(right.getSeconds()-LENGTH)
scaleX.domain([left, right])
$xaxis.transition()
.ease(d3.easeLinear)
.duration(new Date() - oneSecondAgo())
.call(xaxis)
}
tick()
setInterval(tick, 1000)
}
// ------------------------------ Update -----------------------------------
/* Update graph with points
We always set right edge to current time
*/
function update(points){
datapoints = datapoints.concat(points)
logPoint(points[0])
updateTimeScale()
// Add new points, transition until left edge and then remove
$slidablesEnter = $slidables.selectAll('circle')
.data(datapoints, d=>d)
.enter()
$slidablesEnter
.append("circle")
.style("fill", "rgb(74, 255, 0)")
.attr("r", 2)
.attr("cx", p=>scaleX(p[0])) // put it at right
.attr("cy", p=>scaleY(p[1]))
.transition()
.duration(function(p){
remainingTime = p[0] - leftDate()
return remainingTime
})
.ease(d3.easeLinear)
.attr("cx", p => MARGIN_LEFT)
.remove()
}
// Start everything with two random datapoints
init()
d1 = new Date()
d2 = new Date()
d2.setMilliseconds(d2.getMilliseconds()-1500)
update([[d1, Math.random()]])
update([[d2, Math.random()]])
</script></body></html>
【问题讨论】:
标签: javascript d3.js data-visualization real-time