关于cesium:CesiumJS-源码杂谈-时间与时钟系统

12次阅读

共计 3093 个字符,预计需要花费 8 分钟才能阅读完成。

你晓得吗?

  • Cesium 是元素 的英文单词,而 铯原子钟 具备世界上最高的计时精度
  • 工夫,是时刻距离的意思,时刻是动态的点;而工夫就指有起止时刻的一段范畴
  • 很多利用都要有一个时钟,例如 GPS 授时、实时渲染零碎,工夫能够测量很多事物,万物静止也体现了工夫在流逝

1. 工夫的“诞生”

首次创立工夫是呈现在 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._frameStatetime 字段上。

2. 工夫的推动

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 办法,持续更新一帧。

3. Entity API 与 Property API 的更新动力源

在之前写源码系列的时候,就提过 Entity API 是怎么运作的。

首先,EntityAPI 挂载于 Viewer 上,若无 Viewer 那默认的 Entity 容器就得本人实现一套,很麻烦。

其次,Viewer 领有 _onTick 事件,它监听了 CesiumWidgetclockonTick 事件,通过 EventHelper 实现:

eventHelper.add(clock.onTick, Viewer.prototype._onTick, this);

往后就是 DataSourceDisplay、CustomDataSource 等内容了,较为简单,请移步源码解析文章。

引自源码解析文章,以参数化几何的 Entity 为例,它用的是 GeometryVisualizer,当 GeometryVisualizer 调用 fireChangedEvent 函数后,Visualizer 就会拿到最新的 Entity 定义,进而借助 Property API、Updater 等简单架构更新数据。

总之,若无时钟的 onTick 跳动,也就没有方法依据以后工夫去更新 Entity,也就拿不到最新的 Property,更别说动静更新场景中的三维 Entity 了。

4. 简略利用

4.1. 应用原生 JS Date 对象创立 JulianDate

这个最好的阐明就是 JulianDate.now 了,在下面第 1 节曾经列出源码。当然,也能够本人来搞一个:

const myDate = JulianDate.fromDate(new Date())

4.2. 应用工夫字符串(ISO8601 规范的工夫字符串或 UTC 工夫字符串)创立 JulianDate

以北京工夫为例:

const myDate = JulianDate.fromIso8601('2023-05-01T13:15:21+08:00')

留神日期和工夫之间有一个大写字母 T。我在尾部加上了 +08:00 示意东八区北京工夫。

4.3. 为时钟设置起止工夫和速率

这个就很简略了:

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_MULTIPLIERClockStep.TICK_DEPENDENT 才无效。

4.4. 调整时钟的循环状况

clock.clockRange = ClockRange.LOOP_STOP

LOOP_STOP 是默认的,到起点不会进行,会持续往前走,然而会从新回到终点时刻,相似于 重播成果

CLAMPED 会在起点时刻停下来,相似于 播完就停在那里

UNBOUNDED 即便超过起点时刻,也不会停下来,相似 直播成果

正文完
 0