你知道吗?
首次创建时间是出现在 Scene 的构造函数中:
function Scene (/**/) {
// ...
updateFrameNumber(this, 0.0, JulianDate.now());
// ...
}
function updateFrameNumber(scene, frameNumber, time) {
const frameState = scene._frameState;
frameState.frameNumber = frameNumber;
frameState.time = JulianDate.clone(time, frameState.time);
}
源于此,很多自己应用 CesiumJS 着色器的文章中就用 FrameState
上的 frameNumber
就近似表达了“时间”的概念,因为在 60FPS 的屏幕上,可以通过 frameNumber / 60
粗略获得时间值(秒),但是一旦浏览器的帧速率变化,比如 144 FPS,这个获得的时间就会不准确。
CesiumJS 使用 JulianDate
类来表示整个程序中的时间,它是一种天文时间系统,叫作“儒略”日期,它有两个成员字段,一个是自儒略第一天(公元前 4713 年 1 月 1 日)到现在的天数 dayNumber
,另一个是今天已经走过的秒数(零点起算) secondsOfDay
。
注:我们所说的公历时间,即 GregorianDate(格里日历记法),在 CesiumJS 中也是有的,是作为 JS 原生类 Date 的高精度替代品。
根据上面的 Scene 类构造函数,使用 JulianDate.now
方法,无论什么时候初始化 CesiumJS,获取的时间值永远都是程序运行的那个时刻:
JulianDate.now = function (result) {
return JulianDate.fromDate(new Date(), result);
}
所以,真正的时间值在帧状态对象 scene._frameState
的 time
字段上。
CesiumJS 内部的时间是如何更新的?
CesiumJS 的渲染源头是 CesiumWidget
对象,它每一帧都会运行 CesiumWidget.prototype.render
方法,会让此对象上的时钟 tick
一次(也就是跳一下),返回的时间就作为这一帧的时间,传递给 Scene.prototype.render
,进而调用 updateFrameNumber
函数更新累计帧数、时间值:
CesiumWidget.prototype.render = function () {
if (this._canRender) {
this._scene.initializeFrame();
const currentTime = this._clock.tick();
this._scene.render(currentTime);
} else {
this._clock.tick();
}
}
所以要看时间是如何更新的,就要看 Clock
对象的 tick
方法。
初始化 Clock 时,默认就以当前的 JulianDate 为时钟起点时刻,往后一天为终点时刻。
每当调用 tick
时,会获取当前的时刻 clock.currentTime
,然后调用 JulianDate.addSeconds()
方法把时间往前推。 在所有默认条件下,调用的逻辑分支是:
const milliseconds = currentSystemTime - this._lastSystemTime;
currentTime = JulianDate.addSeconds(
currentTime,
multiplier * (milliseconds / 1000.0),
currentTime
);
而这个 currentSystemTime
即时间戳,来自 Performance API
(浏览器高精度性能 API)或 Date API
,能获取当前的毫秒数。
最后,把计算的 currentTime
(类型是 JulianDate
)返回给调用者,也就是 CesiumWidget.prototype.render
方法,继续更新一帧。
在之前写源码系列的时候,就提过 Entity API 是怎么运作的。
首先,EntityAPI 挂载于 Viewer
上,若无 Viewer
那默认的 Entity 容器就得自己实现一套,很麻烦。
其次, Viewer
拥有 _onTick
事件,它监听了 CesiumWidget
的 clock
的 onTick
事件,通过 EventHelper
完成:
eventHelper.add(clock.onTick, Viewer.prototype._onTick, this);
往后就是 DataSourceDisplay、CustomDataSource 等内容了,较为复杂,请移步源码解析文章。
引自源码解析文章,以参数化几何的 Entity
为例,它用的是 GeometryVisualizer
,当 GeometryVisualizer
调用 fireChangedEvent
函数后,Visualizer 就会拿到最新的 Entity 定义,进而借助 Property API、Updater 等复杂架构更新数据。
总之,若无时钟的 onTick
跳动,也就没有办法根据当前时间去更新 Entity,也就拿不到最新的 Property,更别说动态更新场景中的三维 Entity 了。
这个最好的说明就是 JulianDate.now
了,在上面第 1 节已经列出源码。当然,也可以自己来搞一个:
const myDate = JulianDate.fromDate(new Date())
以北京时间为例:
const myDate = JulianDate.fromIso8601('2023-05-01T13:15:21+08:00')
注意日期和时间之间有一个大写字母 T
。我在尾部加上了 +08:00
表示东八区北京时间。
这个就很简单了:
clock.startTime = JulianDate.fromIso8601('2023-05-01T00:00:00+08:00')
clock.stopTime = JulianDate.fromDate(new Date('2023/05/02 00:00:00')) // Date 会默认使用当前时区,当然你也可以手动 +8,格式按 Date 的文档来就可以
clock.multiplier = 3600 // 3600倍速,一秒过一小时
注意,设置倍数要配合参数 clock.clockStep === ClockStep.SYSTEM_CLOCK_MULTIPLIER
或 ClockStep.TICK_DEPENDENT
才有效。
clock.clockRange = ClockRange.LOOP_STOP
LOOP_STOP
是默认的,到终点不会停止,会继续往前走,但是会重新回到起点时刻,类似于 重播效果。
CLAMPED
会在终点时刻停下来,类似于 播完就停在那里。
UNBOUNDED
即使超过终点时刻,也不会停下来,类似 直播效果。
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://www.cnblogs.com/onsummer/p/17418504.html
内容来源于网络,如有侵权,请联系作者删除!