Entity在项目中使用的比较多,但是有几个属性值一直没有使用到,抱着好奇的心态去看了下源码。这些属性实际发挥着什么样的作用。
我接触过的项目中,很少有使用到这个事件监听器。它的用法比较简单,直接entity.definitionChanged.addEventListener(方法,上下文);在官方文档的Event有案例。
监听事件接收四个参数,第一个参数为当前的entity,第二个参数为更改属性的名称,比如position更改了就会是字符串形式的'position',第三个值为更改之后的新值newValue,第四个值为更改之前的旧值oldValue。
源码中直接赋予监听事件的属性值有show,parent,isShowing这三个。
- // 203行 到 220行
- function updateShow(entity, children, isShowing) {
- const length = children.length;
- for (let i = 0; i < length; i++) {
- const child = children[i];
- const childShow = child._show;
- const oldValue = !isShowing && childShow;
- const newValue = isShowing && childShow;
- if (oldValue !== newValue) {
- updateShow(child, child._children, isShowing);
- }
- }
- entity._definitionChanged.raiseEvent(
- entity,
- "isShowing",
- isShowing,
- !isShowing
- );
- }
-
- // 268行 到 293行
- show: {
- get: function () {
- return this._show;
- },
- set: function (value) {
- //>>includeStart('debug', pragmas.debug);
- if (!defined(value)) {
- throw new DeveloperError("value is required.");
- }
- //>>includeEnd('debug');
-
- if (value === this._show) {
- return;
- }
-
- const wasShowing = this.isShowing;
- this._show = value;
- const isShowing = this.isShowing;
-
- if (wasShowing !== isShowing) {
- updateShow(this, this._children, isShowing);
- }
-
- this._definitionChanged.raiseEvent(this, "show", value, !value);
- },
- }
- // 314行 到 344行
- parent: {
- get: function () {
- return this._parent;
- },
- set: function (value) {
- const oldValue = this._parent;
-
- if (oldValue === value) {
- return;
- }
-
- const wasShowing = this.isShowing;
- if (defined(oldValue)) {
- const index = oldValue._children.indexOf(this);
- oldValue._children.splice(index, 1);
- }
-
- this._parent = value;
- if (defined(value)) {
- value._children.push(this);
- }
-
- const isShowing = this.isShowing;
-
- if (wasShowing !== isShowing) {
- updateShow(this, this._children, isShowing);
- }
-
- this._definitionChanged.raiseEvent(this, "parent", value, oldValue);
- },
- }
其他属性的更改也是可以被监听到的,只不过并没有在Entity.js文件中直接触发监听。而是通过了两种方法。createPropertyTypeDescriptor和createPropertyDescriptor。
虽然是两个,最后执行的都是createPropertyDescriptor方法,另一个只是做了检查而已。最终实现监听的是在createPropertyDescriptor.js 中的createProperty方法里。
- function createProperty(
- name,
- privateName,
- subscriptionName,
- configurable,
- createPropertyCallback
- ) {
- return {
- configurable: configurable,
- get: function () {
- return this[privateName];
- },
- set: function (value) {
- const oldValue = this[privateName];
- const subscription = this[subscriptionName];
- if (defined(subscription)) {
- subscription();
- this[subscriptionName] = undefined;
- }
-
- const hasValue = value !== undefined;
- if (
- hasValue &&
- (!defined(value) || !defined(value.getValue)) &&
- defined(createPropertyCallback)
- ) {
- value = createPropertyCallback(value);
- }
-
- if (oldValue !== value) {
- this[privateName] = value;
- this._definitionChanged.raiseEvent(this, name, value, oldValue);
- }
-
- if (defined(value) && defined(value.definitionChanged)) {
- this[subscriptionName] = value.definitionChanged.addEventListener(
- function () {
- this._definitionChanged.raiseEvent(this, name, value, value);
- },
- this
- );
- }
- },
- };
- }
对entity设置监听,我个人的理解可以应用的场景在于一些无法预期下一个值的追踪上。
比如,地图上有一个entity对象,这个对象并不会根据时间的推移而改变位置。而是通过接口或者某个定时行为来设置它的position。这时候就可以设置监听事件来针对position改变来实现代码功能。这种设置只针对于,无法知晓在哪里更新了position值,或者说使用的是其他人封装好的方法,没法在源码基础上添加代码;以及需要延时处理。因为,能直接设置entity的position值的地方,应该可以多放点代码进去。
按官方文档的意思,parent的值是一个entity,代表着它的父级entity。有父级就代表着有子级,可以通过entity._children来知道,有多少个entity儿子。
重新设置parent的时候,如果设置的值与原有的值不相同。则会先把原先的_children中移除当前的entity,然后再加入到新设置的值的_children。这样就不会造成一个entity同时存在于另外两个entity的_children中,当然,人为添加的行为除外。
_parent和_children最终会影响到的,就是当前entity的show属性是否有效。官方文档的解释中,如果你为entity的show设置为了true,那么当它的parent的显示也为true的时候才会显示。说人话就是如果你有parent,你的show并不由你决定了,你要看父级的脸色。
- // 300行 到 308行
- isShowing: {
- get: function () {
- return (
- this._show &&
- (!defined(this.entityCollection) || this.entityCollection.show) &&
- (!defined(this._parent) || this._parent.isShowing)
- );
- },
- }
show的值决定是否显示,并不代表着一定会显示。如果需要知道一个entity是否在屏幕内显示,比较简单的判断方法就是进行两次判断。
entity.entityCollection && entity.isShowing
如果那种自定义的entityCollection或者CustomDataSource,判断就复杂一点。需要用对应的contains方法看是否被添加到viewer的entities或者dataSources里面。
当使用new Cesium.Entity()方式创建一个entity的时候,它并没有添加到哪个entityCollection里面去显示,所以单纯的isShowing判断的并不准确。
子级受父级的限制,那么父级的变化就会影响到子级。体现在了updateShow方法上,它会去遍历所有子级。
- function updateShow(entity, children, isShowing) {
- const length = children.length;
- for (let i = 0; i < length; i++) {
- const child = children[i];
- const childShow = child._show;
- const oldValue = !isShowing && childShow;
- const newValue = isShowing && childShow;
- if (oldValue !== newValue) {
- updateShow(child, child._children, isShowing);
- }
- }
- entity._definitionChanged.raiseEvent(
- entity,
- "isShowing",
- isShowing,
- !isShowing
- );
- }
常见的用法就是多个entity有关联性,同时显示同时消失,将这些entity的parent都设置为同一个entity。这样控制显示的时候就不用去遍历所有的。也要看量,如果数量比较多,为什么不用entityCollection呢?
实际项目上能用到的场景,比如,一个柱子cylinder和一个标签label。你想让label显示在柱子顶端,但是cylinder根据position定位,label也一样。柱子需要拔地而起,标签需要落在柱子上,那就只能创建两个entity,拥有相同的position定位,只不过label定位的高度是柱子的高度,柱子贴地就是了。
前面两个属性的探讨就是它全部的作用了。官方文档的解释说,通过这个值可以知道当前的entity属于哪个collection集合里面。言简意骇。
在使用viewer.entities.add()方法将entity添加进去的时候,在add方法内部就将entity的entityCollection就指向了viewer.entities了。