我花了几天时间尝试创建一个实时时间轴,在该时间轴上异步添加数据。似乎不可能每次都不会破坏其他东西而使事情顺利进行。
我看了几个例子,但似乎都不符合我的情况。也就是说,大多数实时示例要么依靠数据每次使时间轴递增一步,要么都假设数据以固定间隔连续出现。
问题在于:
这里是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>
答案 0 :(得分:0)
只需一些更新。我的解决方法是在很小的间隔(即20毫秒)内重新粉刷所有内容。
这解决了所有同步问题,但不确定性能是否会有所不同。
类似这样的东西
function tick(){
// Redraw all elements
scaleX.domain([now(-TIMELINE_LENGTH_MS), now()])
$slidables.selectAll('circle')
.data(datapoints, d=>d)
.attr("cx", p => scaleX(p[0]))
.transition()
.duration(0)
}
setInterval(tick, REFRESH_INTERVAL)
将REFRESH_INTERVAL
设置为20毫秒,看起来与进行过渡几乎一样。上面的所有内容看上去都显得断断续续,但至少比以前更准确。